Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : #ifndef MEDIAENGINE_H_
6 : #define MEDIAENGINE_H_
7 :
8 : #include "mozilla/RefPtr.h"
9 : #include "DOMMediaStream.h"
10 : #include "MediaStreamGraph.h"
11 : #include "MediaTrackConstraints.h"
12 : #include "mozilla/dom/MediaStreamTrackBinding.h"
13 : #include "mozilla/dom/VideoStreamTrack.h"
14 : #include "mozilla/ipc/PBackgroundSharedTypes.h"
15 : #include "mozilla/media/DeviceChangeCallback.h"
16 :
17 : namespace mozilla {
18 :
19 : namespace dom {
20 : class Blob;
21 : } // namespace dom
22 :
23 : enum {
24 : kVideoTrack = 1,
25 : kAudioTrack = 2,
26 : kTrackCount
27 : };
28 :
29 : /**
30 : * Abstract interface for managing audio and video devices. Each platform
31 : * must implement a concrete class that will map these classes and methods
32 : * to the appropriate backend. For example, on Desktop platforms, these will
33 : * correspond to equivalent webrtc (GIPS) calls, and on B2G they will map to
34 : * a Gonk interface.
35 : */
36 : class MediaEngineVideoSource;
37 : class MediaEngineAudioSource;
38 :
39 : enum MediaEngineState {
40 : kAllocated,
41 : kStarted,
42 : kStopped,
43 : kReleased
44 : };
45 :
46 0 : class MediaEngine : public DeviceChangeCallback
47 : {
48 : public:
49 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEngine)
50 :
51 : static const int DEFAULT_VIDEO_FPS = 30;
52 : static const int DEFAULT_VIDEO_MIN_FPS = 10;
53 : static const int DEFAULT_43_VIDEO_WIDTH = 640;
54 : static const int DEFAULT_43_VIDEO_HEIGHT = 480;
55 : static const int DEFAULT_169_VIDEO_WIDTH = 1280;
56 : static const int DEFAULT_169_VIDEO_HEIGHT = 720;
57 :
58 : #ifndef MOZ_B2G
59 : static const int DEFAULT_SAMPLE_RATE = 32000;
60 : #else
61 : static const int DEFAULT_SAMPLE_RATE = 16000;
62 : #endif
63 : // This allows using whatever rate the graph is using for the
64 : // MediaStreamTrack. This is useful for microphone data, we know it's already
65 : // at the correct rate for insertion in the MSG.
66 : static const int USE_GRAPH_RATE = -1;
67 :
68 : /* Populate an array of video sources in the nsTArray. Also include devices
69 : * that are currently unavailable. */
70 : virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
71 : nsTArray<RefPtr<MediaEngineVideoSource> >*) = 0;
72 :
73 : /* Populate an array of audio sources in the nsTArray. Also include devices
74 : * that are currently unavailable. */
75 : virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
76 : nsTArray<RefPtr<MediaEngineAudioSource> >*) = 0;
77 :
78 : virtual void Shutdown() = 0;
79 :
80 0 : virtual void SetFakeDeviceChangeEvents() {}
81 :
82 : protected:
83 0 : virtual ~MediaEngine() {}
84 : };
85 :
86 : /**
87 : * Video source and friends.
88 : */
89 : class MediaEnginePrefs {
90 : public:
91 0 : MediaEnginePrefs()
92 0 : : mWidth(0)
93 : , mHeight(0)
94 : , mFPS(0)
95 : , mMinFPS(0)
96 : , mFreq(0)
97 : , mAecOn(false)
98 : , mAgcOn(false)
99 : , mNoiseOn(false)
100 : , mAec(0)
101 : , mAgc(0)
102 : , mNoise(0)
103 : , mPlayoutDelay(0)
104 : , mFullDuplex(false)
105 : , mExtendedFilter(false)
106 : , mDelayAgnostic(false)
107 : , mFakeDeviceChangeEventOn(false)
108 0 : , mChannels(0)
109 0 : {}
110 :
111 : int32_t mWidth;
112 : int32_t mHeight;
113 : int32_t mFPS;
114 : int32_t mMinFPS;
115 : int32_t mFreq; // for test tones (fake:true)
116 : bool mAecOn;
117 : bool mAgcOn;
118 : bool mNoiseOn;
119 : int32_t mAec;
120 : int32_t mAgc;
121 : int32_t mNoise;
122 : int32_t mPlayoutDelay;
123 : bool mFullDuplex;
124 : bool mExtendedFilter;
125 : bool mDelayAgnostic;
126 : bool mFakeDeviceChangeEventOn;
127 : int32_t mChannels;
128 :
129 : // mWidth and/or mHeight may be zero (=adaptive default), so use functions.
130 :
131 0 : int32_t GetWidth(bool aHD = false) const {
132 0 : return mWidth? mWidth : (mHeight?
133 0 : (mHeight * GetDefWidth(aHD)) / GetDefHeight(aHD) :
134 0 : GetDefWidth(aHD));
135 : }
136 :
137 0 : int32_t GetHeight(bool aHD = false) const {
138 0 : return mHeight? mHeight : (mWidth?
139 0 : (mWidth * GetDefHeight(aHD)) / GetDefWidth(aHD) :
140 0 : GetDefHeight(aHD));
141 : }
142 : private:
143 0 : static int32_t GetDefWidth(bool aHD = false) {
144 : // It'd be nice if we could use the ternary operator here, but we can't
145 : // because of bug 1002729.
146 0 : if (aHD) {
147 0 : return MediaEngine::DEFAULT_169_VIDEO_WIDTH;
148 : }
149 :
150 0 : return MediaEngine::DEFAULT_43_VIDEO_WIDTH;
151 : }
152 :
153 0 : static int32_t GetDefHeight(bool aHD = false) {
154 : // It'd be nice if we could use the ternary operator here, but we can't
155 : // because of bug 1002729.
156 0 : if (aHD) {
157 0 : return MediaEngine::DEFAULT_169_VIDEO_HEIGHT;
158 : }
159 :
160 0 : return MediaEngine::DEFAULT_43_VIDEO_HEIGHT;
161 : }
162 : };
163 :
164 : /**
165 : * Callback interface for TakePhoto(). Either PhotoComplete() or PhotoError()
166 : * should be called.
167 : */
168 0 : class MediaEnginePhotoCallback {
169 : public:
170 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEnginePhotoCallback)
171 :
172 : // aBlob is the image captured by MediaEngineSource. It is
173 : // called on main thread.
174 : virtual nsresult PhotoComplete(already_AddRefed<dom::Blob> aBlob) = 0;
175 :
176 : // It is called on main thread. aRv is the error code.
177 : virtual nsresult PhotoError(nsresult aRv) = 0;
178 :
179 : protected:
180 0 : virtual ~MediaEnginePhotoCallback() {}
181 : };
182 :
183 : /**
184 : * Common abstract base class for audio and video sources.
185 : *
186 : * By default, the base class implements Allocate and Deallocate using its
187 : * UpdateSingleSource pattern, which manages allocation handles and calculates
188 : * net constraints from competing allocations and updates a single shared device.
189 : *
190 : * Classes that don't operate as a single shared device can override Allocate
191 : * and Deallocate and simply not pass the methods up.
192 : */
193 : class MediaEngineSource : public nsISupports,
194 : protected MediaConstraintsHelper
195 : {
196 : public:
197 : // code inside webrtc.org assumes these sizes; don't use anything smaller
198 : // without verifying it's ok
199 : static const unsigned int kMaxDeviceNameLength = 128;
200 : static const unsigned int kMaxUniqueIdLength = 256;
201 :
202 0 : virtual ~MediaEngineSource()
203 0 : {
204 0 : if (!mInShutdown) {
205 0 : Shutdown();
206 : }
207 0 : }
208 :
209 0 : virtual void Shutdown()
210 : {
211 0 : mInShutdown = true;
212 0 : };
213 :
214 : /* Populate the human readable name of this device in the nsAString */
215 : virtual void GetName(nsAString&) const = 0;
216 :
217 : /* Populate the UUID of this device in the nsACString */
218 : virtual void GetUUID(nsACString&) const = 0;
219 :
220 : /* Override w/true if source does end-run around cross origin restrictions. */
221 0 : virtual bool GetScary() const { return false; };
222 :
223 : class AllocationHandle
224 : {
225 : public:
226 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AllocationHandle);
227 : protected:
228 0 : ~AllocationHandle() {}
229 : public:
230 0 : AllocationHandle(const dom::MediaTrackConstraints& aConstraints,
231 : const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
232 : const MediaEnginePrefs& aPrefs,
233 : const nsString& aDeviceId)
234 :
235 0 : : mConstraints(aConstraints),
236 : mPrincipalInfo(aPrincipalInfo),
237 : mPrefs(aPrefs),
238 0 : mDeviceId(aDeviceId) {}
239 : public:
240 : NormalizedConstraints mConstraints;
241 : mozilla::ipc::PrincipalInfo mPrincipalInfo;
242 : MediaEnginePrefs mPrefs;
243 : nsString mDeviceId;
244 : };
245 :
246 : /* Release the device back to the system. */
247 0 : virtual nsresult Deallocate(AllocationHandle* aHandle)
248 : {
249 0 : MOZ_ASSERT(aHandle);
250 0 : RefPtr<AllocationHandle> handle = aHandle;
251 :
252 : class Comparator {
253 : public:
254 0 : static bool Equals(const RefPtr<AllocationHandle>& a,
255 : const RefPtr<AllocationHandle>& b) {
256 0 : return a.get() == b.get();
257 : }
258 : };
259 :
260 0 : auto ix = mRegisteredHandles.IndexOf(handle, 0, Comparator());
261 0 : if (ix == mRegisteredHandles.NoIndex) {
262 0 : MOZ_ASSERT(false);
263 : return NS_ERROR_FAILURE;
264 : }
265 :
266 0 : mRegisteredHandles.RemoveElementAt(ix);
267 0 : if (mRegisteredHandles.Length() && !mInShutdown) {
268 : // Whenever constraints are removed, other parties may get closer to ideal.
269 0 : auto& first = mRegisteredHandles[0];
270 0 : const char* badConstraint = nullptr;
271 0 : return ReevaluateAllocation(nullptr, nullptr, first->mPrefs,
272 0 : first->mDeviceId, &badConstraint);
273 : }
274 0 : return NS_OK;
275 : }
276 :
277 : /* Start the device and add the track to the provided SourceMediaStream, with
278 : * the provided TrackID. You may start appending data to the track
279 : * immediately after. */
280 : virtual nsresult Start(SourceMediaStream*, TrackID, const PrincipalHandle&) = 0;
281 :
282 : /* tell the source if there are any direct listeners attached */
283 : virtual void SetDirectListeners(bool) = 0;
284 :
285 : /* Called when the stream wants more data */
286 : virtual void NotifyPull(MediaStreamGraph* aGraph,
287 : SourceMediaStream *aSource,
288 : TrackID aId,
289 : StreamTime aDesiredTime,
290 : const PrincipalHandle& aPrincipalHandle) = 0;
291 :
292 : /* Stop the device and release the corresponding MediaStream */
293 : virtual nsresult Stop(SourceMediaStream *aSource, TrackID aID) = 0;
294 :
295 : /* Restart with new capability */
296 : virtual nsresult Restart(AllocationHandle* aHandle,
297 : const dom::MediaTrackConstraints& aConstraints,
298 : const MediaEnginePrefs &aPrefs,
299 : const nsString& aDeviceId,
300 : const char** aOutBadConstraint) = 0;
301 :
302 : /* Returns true if a source represents a fake capture device and
303 : * false otherwise
304 : */
305 : virtual bool IsFake() = 0;
306 :
307 : /* Returns the type of media source (camera, microphone, screen, window, etc) */
308 : virtual dom::MediaSourceEnum GetMediaSource() const = 0;
309 :
310 : /* If implementation of MediaEngineSource supports TakePhoto(), the picture
311 : * should be return via aCallback object. Otherwise, it returns NS_ERROR_NOT_IMPLEMENTED.
312 : * Currently, only Gonk MediaEngineSource implementation supports it.
313 : */
314 : virtual nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) = 0;
315 :
316 : /* Return false if device is currently allocated or started */
317 0 : bool IsAvailable() {
318 0 : if (mState == kAllocated || mState == kStarted) {
319 0 : return false;
320 : } else {
321 0 : return true;
322 : }
323 : }
324 :
325 : /* It is an error to call Start() before an Allocate(), and Stop() before
326 : * a Start(). Only Allocate() may be called after a Deallocate(). */
327 :
328 : /* This call reserves but does not start the device. */
329 0 : virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
330 : const MediaEnginePrefs &aPrefs,
331 : const nsString& aDeviceId,
332 : const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
333 : AllocationHandle** aOutHandle,
334 : const char** aOutBadConstraint)
335 : {
336 0 : AssertIsOnOwningThread();
337 0 : MOZ_ASSERT(aOutHandle);
338 : RefPtr<AllocationHandle> handle =
339 0 : new AllocationHandle(aConstraints, aPrincipalInfo, aPrefs, aDeviceId);
340 0 : nsresult rv = ReevaluateAllocation(handle, nullptr, aPrefs, aDeviceId,
341 0 : aOutBadConstraint);
342 0 : if (NS_FAILED(rv)) {
343 0 : return rv;
344 : }
345 0 : mRegisteredHandles.AppendElement(handle);
346 0 : handle.forget(aOutHandle);
347 0 : return NS_OK;
348 : }
349 :
350 : virtual uint32_t GetBestFitnessDistance(
351 : const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
352 : const nsString& aDeviceId) const = 0;
353 :
354 0 : void GetSettings(dom::MediaTrackSettings& aOutSettings)
355 : {
356 0 : MOZ_ASSERT(NS_IsMainThread());
357 0 : aOutSettings = mSettings;
358 0 : }
359 :
360 : protected:
361 : // Only class' own members can be initialized in constructor initializer list.
362 0 : explicit MediaEngineSource(MediaEngineState aState)
363 0 : : mState(aState)
364 0 : , mInShutdown(false)
365 0 : {}
366 :
367 : /* UpdateSingleSource - Centralized abstract function to implement in those
368 : * cases where a single device is being shared between users. Should apply net
369 : * constraints and restart the device as needed.
370 : *
371 : * aHandle - New or existing handle, or null to update after removal.
372 : * aNetConstraints - Net constraints to be applied to the single device.
373 : * aPrefs - As passed in (in case of changes in about:config).
374 : * aDeviceId - As passed in (origin dependent).
375 : * aOutBadConstraint - Result: nonzero if failed to apply. Name of culprit.
376 : */
377 :
378 : virtual nsresult
379 0 : UpdateSingleSource(const AllocationHandle* aHandle,
380 : const NormalizedConstraints& aNetConstraints,
381 : const MediaEnginePrefs& aPrefs,
382 : const nsString& aDeviceId,
383 : const char** aOutBadConstraint) {
384 0 : return NS_ERROR_NOT_IMPLEMENTED;
385 : };
386 :
387 : /* ReevaluateAllocation - Call to change constraints for an allocation of
388 : * a single device. Manages allocation handles, calculates net constraints
389 : * from all competing allocations, and calls UpdateSingleSource with the net
390 : * result, to restart the single device as needed.
391 : *
392 : * aHandle - New or existing handle, or null to update after removal.
393 : * aConstraintsUpdate - Constraints to be applied to existing handle, or null.
394 : * aPrefs - As passed in (in case of changes from about:config).
395 : * aDeviceId - As passed in (origin-dependent id).
396 : * aOutBadConstraint - Result: nonzero if failed to apply. Name of culprit.
397 : */
398 :
399 : nsresult
400 0 : ReevaluateAllocation(AllocationHandle* aHandle,
401 : NormalizedConstraints* aConstraintsUpdate,
402 : const MediaEnginePrefs& aPrefs,
403 : const nsString& aDeviceId,
404 : const char** aOutBadConstraint)
405 : {
406 : // aHandle and/or aConstraintsUpdate may be nullptr (see below)
407 :
408 0 : AutoTArray<const NormalizedConstraints*, 10> allConstraints;
409 0 : for (auto& registered : mRegisteredHandles) {
410 0 : if (aConstraintsUpdate && registered.get() == aHandle) {
411 0 : continue; // Don't count old constraints
412 : }
413 0 : allConstraints.AppendElement(®istered->mConstraints);
414 : }
415 0 : if (aConstraintsUpdate) {
416 0 : allConstraints.AppendElement(aConstraintsUpdate);
417 0 : } else if (aHandle) {
418 : // In the case of AddShareOfSingleSource, the handle isn't registered yet.
419 0 : allConstraints.AppendElement(&aHandle->mConstraints);
420 : }
421 :
422 0 : NormalizedConstraints netConstraints(allConstraints);
423 0 : if (netConstraints.mBadConstraint) {
424 0 : *aOutBadConstraint = netConstraints.mBadConstraint;
425 0 : return NS_ERROR_FAILURE;
426 : }
427 :
428 : nsresult rv = UpdateSingleSource(aHandle, netConstraints, aPrefs, aDeviceId,
429 0 : aOutBadConstraint);
430 0 : if (NS_FAILED(rv)) {
431 0 : return rv;
432 : }
433 0 : if (aHandle && aConstraintsUpdate) {
434 0 : aHandle->mConstraints = *aConstraintsUpdate;
435 : }
436 0 : return NS_OK;
437 : }
438 :
439 0 : void AssertIsOnOwningThread()
440 : {
441 0 : NS_ASSERT_OWNINGTHREAD(MediaEngineSource);
442 0 : }
443 :
444 : MediaEngineState mState;
445 :
446 : NS_DECL_OWNINGTHREAD
447 :
448 : nsTArray<RefPtr<AllocationHandle>> mRegisteredHandles;
449 : bool mInShutdown;
450 :
451 : // Main-thread only:
452 : dom::MediaTrackSettings mSettings;
453 : };
454 :
455 : class MediaEngineVideoSource : public MediaEngineSource
456 : {
457 : public:
458 0 : virtual ~MediaEngineVideoSource() {}
459 :
460 : protected:
461 0 : explicit MediaEngineVideoSource(MediaEngineState aState)
462 0 : : MediaEngineSource(aState) {}
463 0 : MediaEngineVideoSource()
464 0 : : MediaEngineSource(kReleased) {}
465 : };
466 :
467 : /**
468 : * Audio source and friends.
469 : */
470 : class MediaEngineAudioSource : public MediaEngineSource,
471 : public AudioDataListenerInterface
472 : {
473 : public:
474 0 : virtual ~MediaEngineAudioSource() {}
475 :
476 : protected:
477 0 : explicit MediaEngineAudioSource(MediaEngineState aState)
478 0 : : MediaEngineSource(aState) {}
479 : MediaEngineAudioSource()
480 : : MediaEngineSource(kReleased) {}
481 :
482 : };
483 :
484 : } // namespace mozilla
485 :
486 : #endif /* MEDIAENGINE_H_ */
|