/src/mozilla-central/dom/media/webrtc/MediaEngineDefault.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "MediaEngineDefault.h" |
6 | | |
7 | | #include "ImageContainer.h" |
8 | | #include "ImageTypes.h" |
9 | | #include "Layers.h" |
10 | | #include "MediaStreamGraph.h" |
11 | | #include "MediaTrackConstraints.h" |
12 | | #include "mozilla/dom/File.h" |
13 | | #include "mozilla/UniquePtr.h" |
14 | | #include "nsCOMPtr.h" |
15 | | #include "nsContentUtils.h" |
16 | | #include "nsIFile.h" |
17 | | #include "nsIFilePicker.h" |
18 | | #include "nsIPrefBranch.h" |
19 | | #include "nsIPrefService.h" |
20 | | #include "Tracing.h" |
21 | | |
22 | | #ifdef MOZ_WIDGET_ANDROID |
23 | | #include "nsISupportsUtils.h" |
24 | | #endif |
25 | | |
26 | | #ifdef MOZ_WEBRTC |
27 | | #include "YuvStamper.h" |
28 | | #endif |
29 | | |
30 | | #define DEFAULT_AUDIO_TIMER_MS 10 |
31 | | namespace mozilla { |
32 | | |
33 | | using namespace mozilla::gfx; |
34 | | |
35 | | /** |
36 | | * Default video source. |
37 | | */ |
38 | | |
39 | | MediaEngineDefaultVideoSource::MediaEngineDefaultVideoSource() |
40 | | : mTimer(nullptr) |
41 | | , mMutex("MediaEngineDefaultVideoSource::mMutex") |
42 | 0 | {} |
43 | | |
44 | | MediaEngineDefaultVideoSource::~MediaEngineDefaultVideoSource() |
45 | 0 | {} |
46 | | |
47 | | nsString |
48 | | MediaEngineDefaultVideoSource::GetName() const |
49 | 0 | { |
50 | 0 | return NS_LITERAL_STRING(u"Default Video Device"); |
51 | 0 | } |
52 | | |
53 | | nsCString |
54 | | MediaEngineDefaultVideoSource::GetUUID() const |
55 | 0 | { |
56 | 0 | return NS_LITERAL_CSTRING("1041FCBD-3F12-4F7B-9E9B-1EC556DD5676"); |
57 | 0 | } |
58 | | |
59 | | uint32_t |
60 | | MediaEngineDefaultVideoSource::GetBestFitnessDistance( |
61 | | const nsTArray<const NormalizedConstraintSet*>& aConstraintSets, |
62 | | const nsString& aDeviceId) const |
63 | 0 | { |
64 | 0 | AssertIsOnOwningThread(); |
65 | 0 |
|
66 | 0 | uint32_t distance = 0; |
67 | 0 | #ifdef MOZ_WEBRTC |
68 | 0 | for (const auto* cs : aConstraintSets) { |
69 | 0 | distance = MediaConstraintsHelper::GetMinimumFitnessDistance(*cs, aDeviceId); |
70 | 0 | break; // distance is read from first entry only |
71 | 0 | } |
72 | 0 | #endif |
73 | 0 | return distance; |
74 | 0 | } |
75 | | |
76 | | nsresult |
77 | | MediaEngineDefaultVideoSource::Allocate(const dom::MediaTrackConstraints &aConstraints, |
78 | | const MediaEnginePrefs &aPrefs, |
79 | | const nsString& aDeviceId, |
80 | | const mozilla::ipc::PrincipalInfo& aPrincipalInfo, |
81 | | AllocationHandle** aOutHandle, |
82 | | const char** aOutBadConstraint) |
83 | 0 | { |
84 | 0 | AssertIsOnOwningThread(); |
85 | 0 |
|
86 | 0 | MOZ_ASSERT(mState == kReleased); |
87 | 0 |
|
88 | 0 | FlattenedConstraints c(aConstraints); |
89 | 0 |
|
90 | 0 | // Mock failure for automated tests. |
91 | 0 | if (c.mDeviceId.mIdeal.find(NS_LITERAL_STRING("bad device")) != |
92 | 0 | c.mDeviceId.mIdeal.end()) { |
93 | 0 | return NS_ERROR_FAILURE; |
94 | 0 | } |
95 | 0 | |
96 | 0 | // emulator debug is very, very slow; reduce load on it with smaller/slower fake video |
97 | 0 | mOpts = aPrefs; |
98 | 0 | mOpts.mWidth = c.mWidth.Get(aPrefs.mWidth ? aPrefs.mWidth : |
99 | | #ifdef DEBUG |
100 | | MediaEnginePrefs::DEFAULT_43_VIDEO_WIDTH/2 |
101 | | #else |
102 | 0 | MediaEnginePrefs::DEFAULT_43_VIDEO_WIDTH |
103 | 0 | #endif |
104 | 0 | ); |
105 | 0 | mOpts.mHeight = c.mHeight.Get(aPrefs.mHeight ? aPrefs.mHeight : |
106 | | #ifdef DEBUG |
107 | | MediaEnginePrefs::DEFAULT_43_VIDEO_HEIGHT/2 |
108 | | #else |
109 | 0 | MediaEnginePrefs::DEFAULT_43_VIDEO_HEIGHT |
110 | 0 | #endif |
111 | 0 | ); |
112 | 0 | mOpts.mWidth = std::max(160, std::min(mOpts.mWidth, 4096)) & ~1; |
113 | 0 | mOpts.mHeight = std::max(90, std::min(mOpts.mHeight, 2160)) & ~1; |
114 | 0 | *aOutHandle = nullptr; |
115 | 0 |
|
116 | 0 | MutexAutoLock lock(mMutex); |
117 | 0 | mState = kAllocated; |
118 | 0 | return NS_OK; |
119 | 0 | } |
120 | | |
121 | | nsresult |
122 | | MediaEngineDefaultVideoSource::Deallocate(const RefPtr<const AllocationHandle>& aHandle) |
123 | 0 | { |
124 | 0 | AssertIsOnOwningThread(); |
125 | 0 |
|
126 | 0 | MOZ_ASSERT(!aHandle); |
127 | 0 | MOZ_ASSERT(!mImage); |
128 | 0 | MOZ_ASSERT(mState == kStopped || mState == kAllocated); |
129 | 0 |
|
130 | 0 | MutexAutoLock lock(mMutex); |
131 | 0 | if (mStream && IsTrackIDExplicit(mTrackID)) { |
132 | 0 | mStream->EndTrack(mTrackID); |
133 | 0 | mStream = nullptr; |
134 | 0 | mTrackID = TRACK_NONE; |
135 | 0 | } |
136 | 0 | mState = kReleased; |
137 | 0 | mImageContainer = nullptr; |
138 | 0 |
|
139 | 0 | return NS_OK; |
140 | 0 | } |
141 | | |
142 | | static void AllocateSolidColorFrame(layers::PlanarYCbCrData& aData, |
143 | | int aWidth, int aHeight, |
144 | | int aY, int aCb, int aCr) |
145 | 0 | { |
146 | 0 | MOZ_ASSERT(!(aWidth&1)); |
147 | 0 | MOZ_ASSERT(!(aHeight&1)); |
148 | 0 | // Allocate a single frame with a solid color |
149 | 0 | int yLen = aWidth*aHeight; |
150 | 0 | int cbLen = yLen>>2; |
151 | 0 | int crLen = cbLen; |
152 | 0 | uint8_t* frame = (uint8_t*) malloc(yLen+cbLen+crLen); |
153 | 0 | memset(frame, aY, yLen); |
154 | 0 | memset(frame+yLen, aCb, cbLen); |
155 | 0 | memset(frame+yLen+cbLen, aCr, crLen); |
156 | 0 |
|
157 | 0 | aData.mYChannel = frame; |
158 | 0 | aData.mYSize = IntSize(aWidth, aHeight); |
159 | 0 | aData.mYStride = aWidth; |
160 | 0 | aData.mCbCrStride = aWidth>>1; |
161 | 0 | aData.mCbChannel = frame + yLen; |
162 | 0 | aData.mCrChannel = aData.mCbChannel + cbLen; |
163 | 0 | aData.mCbCrSize = IntSize(aWidth>>1, aHeight>>1); |
164 | 0 | aData.mPicX = 0; |
165 | 0 | aData.mPicY = 0; |
166 | 0 | aData.mPicSize = IntSize(aWidth, aHeight); |
167 | 0 | aData.mStereoMode = StereoMode::MONO; |
168 | 0 | } |
169 | | |
170 | | static void ReleaseFrame(layers::PlanarYCbCrData& aData) |
171 | 0 | { |
172 | 0 | free(aData.mYChannel); |
173 | 0 | } |
174 | | |
175 | | nsresult |
176 | | MediaEngineDefaultVideoSource::SetTrack(const RefPtr<const AllocationHandle>& aHandle, |
177 | | const RefPtr<SourceMediaStream>& aStream, |
178 | | TrackID aTrackID, |
179 | | const PrincipalHandle& aPrincipal) |
180 | 0 | { |
181 | 0 | AssertIsOnOwningThread(); |
182 | 0 |
|
183 | 0 | MOZ_ASSERT(mState == kAllocated); |
184 | 0 | MOZ_ASSERT(!mStream); |
185 | 0 | MOZ_ASSERT(mTrackID == TRACK_NONE); |
186 | 0 |
|
187 | 0 | { |
188 | 0 | MutexAutoLock lock(mMutex); |
189 | 0 | mStream = aStream; |
190 | 0 | mTrackID = aTrackID; |
191 | 0 | } |
192 | 0 | aStream->AddTrack(aTrackID, 0, new VideoSegment(), |
193 | 0 | SourceMediaStream::ADDTRACK_QUEUED); |
194 | 0 | return NS_OK; |
195 | 0 | } |
196 | | |
197 | | nsresult |
198 | | MediaEngineDefaultVideoSource::Start(const RefPtr<const AllocationHandle>& aHandle) |
199 | 0 | { |
200 | 0 | AssertIsOnOwningThread(); |
201 | 0 |
|
202 | 0 | MOZ_ASSERT(mState == kAllocated || mState == kStopped); |
203 | 0 | MOZ_ASSERT(mStream, "SetTrack() must happen before Start()"); |
204 | 0 | MOZ_ASSERT(IsTrackIDExplicit(mTrackID), "SetTrack() must happen before Start()"); |
205 | 0 |
|
206 | 0 | mTimer = NS_NewTimer(); |
207 | 0 | if (!mTimer) { |
208 | 0 | return NS_ERROR_FAILURE; |
209 | 0 | } |
210 | 0 | |
211 | 0 | if (!mImageContainer) { |
212 | 0 | mImageContainer = |
213 | 0 | layers::LayerManager::CreateImageContainer(layers::ImageContainer::ASYNCHRONOUS); |
214 | 0 | } |
215 | 0 |
|
216 | 0 | // Start timer for subsequent frames |
217 | 0 | uint32_t interval; |
218 | | #if defined(MOZ_WIDGET_ANDROID) && defined(DEBUG) |
219 | | // emulator debug is very, very slow and has problems dealing with realtime audio inputs |
220 | | interval = 10 * (1000 / mOpts.mFPS); |
221 | | #else |
222 | | interval = 1000 / mOpts.mFPS; |
223 | 0 | #endif |
224 | 0 | mTimer->InitWithNamedFuncCallback([](nsITimer* aTimer, void* aClosure) { |
225 | 0 | RefPtr<MediaEngineDefaultVideoSource> source = |
226 | 0 | static_cast<MediaEngineDefaultVideoSource*>(aClosure); |
227 | 0 | source->GenerateFrame(); |
228 | 0 | }, this, interval, nsITimer::TYPE_REPEATING_SLACK, |
229 | 0 | "MediaEngineDefaultVideoSource::GenerateFrame"); |
230 | 0 |
|
231 | 0 | MutexAutoLock lock(mMutex); |
232 | 0 | mState = kStarted; |
233 | 0 | return NS_OK; |
234 | 0 | } |
235 | | |
236 | | nsresult |
237 | | MediaEngineDefaultVideoSource::Stop(const RefPtr<const AllocationHandle>& aHandle) |
238 | 0 | { |
239 | 0 | AssertIsOnOwningThread(); |
240 | 0 |
|
241 | 0 | if (mState == kStopped || mState == kAllocated) { |
242 | 0 | return NS_OK; |
243 | 0 | } |
244 | 0 | |
245 | 0 | MOZ_ASSERT(mState == kStarted); |
246 | 0 | MOZ_ASSERT(mTimer); |
247 | 0 | MOZ_ASSERT(mStream); |
248 | 0 | MOZ_ASSERT(IsTrackIDExplicit(mTrackID)); |
249 | 0 |
|
250 | 0 | mTimer->Cancel(); |
251 | 0 | mTimer = nullptr; |
252 | 0 |
|
253 | 0 | MutexAutoLock lock(mMutex); |
254 | 0 |
|
255 | 0 | mImage = nullptr; |
256 | 0 | mState = kStopped; |
257 | 0 |
|
258 | 0 | return NS_OK; |
259 | 0 | } |
260 | | |
261 | | nsresult |
262 | | MediaEngineDefaultVideoSource::Reconfigure( |
263 | | const RefPtr<AllocationHandle>& aHandle, |
264 | | const dom::MediaTrackConstraints& aConstraints, |
265 | | const MediaEnginePrefs &aPrefs, |
266 | | const nsString& aDeviceId, |
267 | | const char** aOutBadConstraint) |
268 | 0 | { |
269 | 0 | return NS_OK; |
270 | 0 | } |
271 | | |
272 | | void |
273 | | MediaEngineDefaultVideoSource::GenerateFrame() |
274 | 0 | { |
275 | 0 | AssertIsOnOwningThread(); |
276 | 0 |
|
277 | 0 | // Update the target color |
278 | 0 | if (mCr <= 16) { |
279 | 0 | if (mCb < 240) { |
280 | 0 | mCb++; |
281 | 0 | } else { |
282 | 0 | mCr++; |
283 | 0 | } |
284 | 0 | } else if (mCb >= 240) { |
285 | 0 | if (mCr < 240) { |
286 | 0 | mCr++; |
287 | 0 | } else { |
288 | 0 | mCb--; |
289 | 0 | } |
290 | 0 | } else if (mCr >= 240) { |
291 | 0 | if (mCb > 16) { |
292 | 0 | mCb--; |
293 | 0 | } else { |
294 | 0 | mCr--; |
295 | 0 | } |
296 | 0 | } else { |
297 | 0 | mCr--; |
298 | 0 | } |
299 | 0 |
|
300 | 0 | // Allocate a single solid color image |
301 | 0 | RefPtr<layers::PlanarYCbCrImage> ycbcr_image = mImageContainer->CreatePlanarYCbCrImage(); |
302 | 0 | layers::PlanarYCbCrData data; |
303 | 0 | AllocateSolidColorFrame(data, mOpts.mWidth, mOpts.mHeight, 0x80, mCb, mCr); |
304 | 0 |
|
305 | 0 | #ifdef MOZ_WEBRTC |
306 | 0 | uint64_t timestamp = PR_Now(); |
307 | 0 | YuvStamper::Encode(mOpts.mWidth, mOpts.mHeight, mOpts.mWidth, |
308 | 0 | data.mYChannel, |
309 | 0 | reinterpret_cast<unsigned char*>(×tamp), sizeof(timestamp), |
310 | 0 | 0, 0); |
311 | 0 | #endif |
312 | 0 |
|
313 | 0 | bool setData = ycbcr_image->CopyData(data); |
314 | 0 | MOZ_ASSERT(setData); |
315 | 0 |
|
316 | 0 | // SetData copies data, so we can free the frame |
317 | 0 | ReleaseFrame(data); |
318 | 0 |
|
319 | 0 | if (!setData) { |
320 | 0 | return; |
321 | 0 | } |
322 | 0 | |
323 | 0 | MutexAutoLock lock(mMutex); |
324 | 0 | mImage = std::move(ycbcr_image); |
325 | 0 | } |
326 | | |
327 | | void |
328 | | MediaEngineDefaultVideoSource::Pull(const RefPtr<const AllocationHandle>& aHandle, |
329 | | const RefPtr<SourceMediaStream>& aStream, |
330 | | TrackID aTrackID, |
331 | | StreamTime aDesiredTime, |
332 | | const PrincipalHandle& aPrincipalHandle) |
333 | 0 | { |
334 | 0 | TRACE_AUDIO_CALLBACK_COMMENT("SourceMediaStream %p track %i", |
335 | 0 | aStream.get(), aTrackID); |
336 | 0 | // AppendFrame takes ownership of `segment` |
337 | 0 | VideoSegment segment; |
338 | 0 |
|
339 | 0 | RefPtr<layers::Image> image; |
340 | 0 | { |
341 | 0 | MutexAutoLock lock(mMutex); |
342 | 0 | // Started - append real image |
343 | 0 | // Stopped - append null |
344 | 0 | // Released - Track is ended, safe to ignore |
345 | 0 | // Can happen because NotifyPull comes from a stream listener |
346 | 0 | if (mState == kReleased) { |
347 | 0 | return; |
348 | 0 | } |
349 | 0 | MOZ_ASSERT(mState != kAllocated); |
350 | 0 | if (mState == kStarted) { |
351 | 0 | MOZ_ASSERT(mStream == aStream); |
352 | 0 | MOZ_ASSERT(mTrackID == aTrackID); |
353 | 0 | image = mImage; |
354 | 0 | } |
355 | 0 | } |
356 | 0 |
|
357 | 0 | StreamTime delta = aDesiredTime - aStream->GetEndOfAppendedData(aTrackID); |
358 | 0 | if (delta > 0) { |
359 | 0 | // nullptr images are allowed |
360 | 0 | IntSize size(mOpts.mWidth, mOpts.mHeight); |
361 | 0 | segment.AppendFrame(image.forget(), delta, size, aPrincipalHandle); |
362 | 0 | // This can fail if either a) we haven't added the track yet, or b) |
363 | 0 | // we've removed or finished the track. |
364 | 0 | aStream->AppendToTrack(aTrackID, &segment); |
365 | 0 | } |
366 | 0 | } |
367 | | |
368 | | /** |
369 | | * Default audio source. |
370 | | */ |
371 | | |
372 | | MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource() |
373 | | : mMutex("MediaEngineDefaultAudioSource::mMutex") |
374 | 0 | {} |
375 | | |
376 | | MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource() |
377 | 0 | {} |
378 | | |
379 | | nsString |
380 | | MediaEngineDefaultAudioSource::GetName() const |
381 | 0 | { |
382 | 0 | return NS_LITERAL_STRING(u"Default Audio Device"); |
383 | 0 | } |
384 | | |
385 | | nsCString |
386 | | MediaEngineDefaultAudioSource::GetUUID() const |
387 | 0 | { |
388 | 0 | return NS_LITERAL_CSTRING("B7CBD7C1-53EF-42F9-8353-73F61C70C092"); |
389 | 0 | } |
390 | | |
391 | | uint32_t |
392 | | MediaEngineDefaultAudioSource::GetBestFitnessDistance( |
393 | | const nsTArray<const NormalizedConstraintSet*>& aConstraintSets, |
394 | | const nsString& aDeviceId) const |
395 | 0 | { |
396 | 0 | uint32_t distance = 0; |
397 | 0 | #ifdef MOZ_WEBRTC |
398 | 0 | for (const auto* cs : aConstraintSets) { |
399 | 0 | distance = MediaConstraintsHelper::GetMinimumFitnessDistance(*cs, aDeviceId); |
400 | 0 | break; // distance is read from first entry only |
401 | 0 | } |
402 | 0 | #endif |
403 | 0 | return distance; |
404 | 0 | } |
405 | | |
406 | | bool |
407 | | MediaEngineDefaultAudioSource::IsAvailable() const |
408 | 0 | { |
409 | 0 | AssertIsOnOwningThread(); |
410 | 0 |
|
411 | 0 | return mState == kReleased; |
412 | 0 | } |
413 | | |
414 | | nsresult |
415 | | MediaEngineDefaultAudioSource::Allocate(const dom::MediaTrackConstraints &aConstraints, |
416 | | const MediaEnginePrefs &aPrefs, |
417 | | const nsString& aDeviceId, |
418 | | const mozilla::ipc::PrincipalInfo& aPrincipalInfo, |
419 | | AllocationHandle** aOutHandle, |
420 | | const char** aOutBadConstraint) |
421 | 0 | { |
422 | 0 | AssertIsOnOwningThread(); |
423 | 0 |
|
424 | 0 | MOZ_ASSERT(mState == kReleased); |
425 | 0 |
|
426 | 0 | // Mock failure for automated tests. |
427 | 0 | if (aConstraints.mDeviceId.IsString() && |
428 | 0 | aConstraints.mDeviceId.GetAsString().EqualsASCII("bad device")) { |
429 | 0 | return NS_ERROR_FAILURE; |
430 | 0 | } |
431 | 0 | |
432 | 0 | mFreq = aPrefs.mFreq ? aPrefs.mFreq : 1000; |
433 | 0 | *aOutHandle = nullptr; |
434 | 0 |
|
435 | 0 | MutexAutoLock lock(mMutex); |
436 | 0 | mState = kAllocated; |
437 | 0 | return NS_OK; |
438 | 0 | } |
439 | | |
440 | | nsresult |
441 | | MediaEngineDefaultAudioSource::Deallocate(const RefPtr<const AllocationHandle>& aHandle) |
442 | 0 | { |
443 | 0 | AssertIsOnOwningThread(); |
444 | 0 |
|
445 | 0 | MOZ_ASSERT(!aHandle); |
446 | 0 | MOZ_ASSERT(mState == kStopped || mState == kAllocated); |
447 | 0 |
|
448 | 0 | MutexAutoLock lock(mMutex); |
449 | 0 | if (mStream && IsTrackIDExplicit(mTrackID)) { |
450 | 0 | mStream->EndTrack(mTrackID); |
451 | 0 | mStream = nullptr; |
452 | 0 | mTrackID = TRACK_NONE; |
453 | 0 | } |
454 | 0 | mState = kReleased; |
455 | 0 | return NS_OK; |
456 | 0 | } |
457 | | |
458 | | nsresult |
459 | | MediaEngineDefaultAudioSource::SetTrack(const RefPtr<const AllocationHandle>& aHandle, |
460 | | const RefPtr<SourceMediaStream>& aStream, |
461 | | TrackID aTrackID, |
462 | | const PrincipalHandle& aPrincipal) |
463 | 0 | { |
464 | 0 | AssertIsOnOwningThread(); |
465 | 0 |
|
466 | 0 | MOZ_ASSERT(mState == kAllocated); |
467 | 0 | MOZ_ASSERT(!mStream); |
468 | 0 | MOZ_ASSERT(mTrackID == TRACK_NONE); |
469 | 0 |
|
470 | 0 | // AddAudioTrack will take ownership of segment |
471 | 0 | mStream = aStream; |
472 | 0 | mTrackID = aTrackID; |
473 | 0 | aStream->AddAudioTrack(aTrackID, |
474 | 0 | aStream->GraphRate(), |
475 | 0 | 0, |
476 | 0 | new AudioSegment(), |
477 | 0 | SourceMediaStream::ADDTRACK_QUEUED); |
478 | 0 | return NS_OK; |
479 | 0 | } |
480 | | |
481 | | nsresult |
482 | | MediaEngineDefaultAudioSource::Start(const RefPtr<const AllocationHandle>& aHandle) |
483 | 0 | { |
484 | 0 | AssertIsOnOwningThread(); |
485 | 0 |
|
486 | 0 | MOZ_ASSERT(mState == kAllocated || mState == kStopped); |
487 | 0 | MOZ_ASSERT(mStream, "SetTrack() must happen before Start()"); |
488 | 0 | MOZ_ASSERT(IsTrackIDExplicit(mTrackID), "SetTrack() must happen before Start()"); |
489 | 0 |
|
490 | 0 | if (!mSineGenerator) { |
491 | 0 | // generate sine wave (default 1KHz) |
492 | 0 | mSineGenerator = new SineWaveGenerator(mStream->GraphRate(), mFreq); |
493 | 0 | } |
494 | 0 |
|
495 | 0 | MutexAutoLock lock(mMutex); |
496 | 0 | mState = kStarted; |
497 | 0 | return NS_OK; |
498 | 0 | } |
499 | | |
500 | | nsresult |
501 | | MediaEngineDefaultAudioSource::Stop(const RefPtr<const AllocationHandle>& aHandle) |
502 | 0 | { |
503 | 0 | AssertIsOnOwningThread(); |
504 | 0 |
|
505 | 0 | if (mState == kStopped || mState == kAllocated) { |
506 | 0 | return NS_OK; |
507 | 0 | } |
508 | 0 | |
509 | 0 | MOZ_ASSERT(mState == kStarted); |
510 | 0 |
|
511 | 0 | MutexAutoLock lock(mMutex); |
512 | 0 | mState = kStopped; |
513 | 0 | return NS_OK; |
514 | 0 | } |
515 | | |
516 | | nsresult |
517 | | MediaEngineDefaultAudioSource::Reconfigure( |
518 | | const RefPtr<AllocationHandle>& aHandle, |
519 | | const dom::MediaTrackConstraints& aConstraints, |
520 | | const MediaEnginePrefs &aPrefs, |
521 | | const nsString& aDeviceId, |
522 | | const char** aOutBadConstraint) |
523 | 0 | { |
524 | 0 | return NS_OK; |
525 | 0 | } |
526 | | |
527 | | void |
528 | | MediaEngineDefaultAudioSource::AppendToSegment(AudioSegment& aSegment, |
529 | | TrackTicks aSamples, |
530 | | const PrincipalHandle& aPrincipalHandle) |
531 | 0 | { |
532 | 0 | RefPtr<SharedBuffer> buffer = SharedBuffer::Create(aSamples * sizeof(int16_t)); |
533 | 0 | int16_t* dest = static_cast<int16_t*>(buffer->Data()); |
534 | 0 |
|
535 | 0 | mSineGenerator->generate(dest, aSamples); |
536 | 0 | AutoTArray<const int16_t*,1> channels; |
537 | 0 | channels.AppendElement(dest); |
538 | 0 | aSegment.AppendFrames(buffer.forget(), channels, aSamples, aPrincipalHandle); |
539 | 0 | } |
540 | | |
541 | | void |
542 | | MediaEngineDefaultAudioSource::Pull(const RefPtr<const AllocationHandle>& aHandle, |
543 | | const RefPtr<SourceMediaStream>& aStream, |
544 | | TrackID aTrackID, |
545 | | StreamTime aDesiredTime, |
546 | | const PrincipalHandle& aPrincipalHandle) |
547 | 0 | { |
548 | 0 | TRACE_AUDIO_CALLBACK_COMMENT("SourceMediaStream %p track %i", |
549 | 0 | aStream.get(), aTrackID); |
550 | 0 | AudioSegment segment; |
551 | 0 | // avoid accumulating rounding errors |
552 | 0 | TrackTicks desired = aStream->TimeToTicksRoundUp(aStream->GraphRate(), aDesiredTime); |
553 | 0 | TrackTicks delta = desired - mLastNotify; |
554 | 0 | mLastNotify += delta; |
555 | 0 | AppendToSegment(segment, delta, aPrincipalHandle); |
556 | 0 | aStream->AppendToTrack(aTrackID, &segment); |
557 | 0 | } |
558 | | |
559 | | void |
560 | | MediaEngineDefault::EnumerateDevices(uint64_t aWindowId, |
561 | | dom::MediaSourceEnum aMediaSource, |
562 | | MediaSinkEnum aMediaSink, |
563 | | nsTArray<RefPtr<MediaDevice>>* aDevices) |
564 | 0 | { |
565 | 0 | AssertIsOnOwningThread(); |
566 | 0 |
|
567 | 0 | switch (aMediaSource) { |
568 | 0 | case dom::MediaSourceEnum::Camera: { |
569 | 0 | // Only supports camera video sources. See Bug 1038241. |
570 | 0 |
|
571 | 0 | // We once had code here to find a VideoSource with the same settings and |
572 | 0 | // re-use that. This is no longer possible since the resolution gets set |
573 | 0 | // in Allocate(). |
574 | 0 |
|
575 | 0 | nsTArray<RefPtr<MediaEngineSource>>* |
576 | 0 | devicesForThisWindow = mVSources.LookupOrAdd(aWindowId); |
577 | 0 | auto newSource = MakeRefPtr<MediaEngineDefaultVideoSource>(); |
578 | 0 | devicesForThisWindow->AppendElement(newSource); |
579 | 0 | aDevices->AppendElement(MakeRefPtr<MediaDevice>( |
580 | 0 | newSource, |
581 | 0 | newSource->GetName(), |
582 | 0 | NS_ConvertUTF8toUTF16(newSource->GetUUID()))); |
583 | 0 | return; |
584 | 0 | } |
585 | 0 | case dom::MediaSourceEnum::Microphone: { |
586 | 0 | nsTArray<RefPtr<MediaEngineDefaultAudioSource>>* |
587 | 0 | devicesForThisWindow = mASources.LookupOrAdd(aWindowId); |
588 | 0 | for (const RefPtr<MediaEngineDefaultAudioSource>& source : *devicesForThisWindow) { |
589 | 0 | if (source->IsAvailable()) { |
590 | 0 | aDevices->AppendElement(MakeRefPtr<MediaDevice>( |
591 | 0 | source, |
592 | 0 | source->GetName(), |
593 | 0 | NS_ConvertUTF8toUTF16(source->GetUUID()))); |
594 | 0 | } |
595 | 0 | } |
596 | 0 |
|
597 | 0 | if (aDevices->IsEmpty()) { |
598 | 0 | // All streams are currently busy, just make a new one. |
599 | 0 | auto newSource = MakeRefPtr<MediaEngineDefaultAudioSource>(); |
600 | 0 | devicesForThisWindow->AppendElement(newSource); |
601 | 0 | aDevices->AppendElement(MakeRefPtr<MediaDevice>( |
602 | 0 | newSource, |
603 | 0 | newSource->GetName(), |
604 | 0 | NS_ConvertUTF8toUTF16(newSource->GetUUID()))); |
605 | 0 | } |
606 | 0 | return; |
607 | 0 | } |
608 | 0 | default: |
609 | 0 | MOZ_ASSERT_UNREACHABLE("Unsupported source type"); |
610 | 0 | return; |
611 | 0 | } |
612 | 0 |
|
613 | 0 | if (aMediaSink == MediaSinkEnum::Speaker) { |
614 | 0 | NS_WARNING("No default implementation for MediaSinkEnum::Speaker"); |
615 | 0 | } |
616 | 0 | } |
617 | | |
618 | | void |
619 | | MediaEngineDefault::ReleaseResourcesForWindow(uint64_t aWindowId) |
620 | 0 | { |
621 | 0 | nsTArray<RefPtr<MediaEngineDefaultAudioSource>>* audioDevicesForThisWindow = |
622 | 0 | mASources.Get(aWindowId); |
623 | 0 |
|
624 | 0 | if (audioDevicesForThisWindow) { |
625 | 0 | for (const RefPtr<MediaEngineDefaultAudioSource>& source : |
626 | 0 | *audioDevicesForThisWindow) { |
627 | 0 | source->Shutdown(); |
628 | 0 | } |
629 | 0 | } |
630 | 0 |
|
631 | 0 | mASources.Remove(aWindowId); |
632 | 0 |
|
633 | 0 | nsTArray<RefPtr<MediaEngineSource>>* videoDevicesForThisWindow = |
634 | 0 | mVSources.Get(aWindowId); |
635 | 0 |
|
636 | 0 | if (videoDevicesForThisWindow) { |
637 | 0 | for (const RefPtr<MediaEngineSource>& source : |
638 | 0 | *videoDevicesForThisWindow) { |
639 | 0 | source->Shutdown(); |
640 | 0 | } |
641 | 0 | } |
642 | 0 |
|
643 | 0 | mVSources.Remove(aWindowId); |
644 | 0 | } |
645 | | |
646 | | void |
647 | | MediaEngineDefault::Shutdown() |
648 | 0 | { |
649 | 0 | AssertIsOnOwningThread(); |
650 | 0 |
|
651 | 0 | for (auto iter = mVSources.Iter(); !iter.Done(); iter.Next()) { |
652 | 0 | for (const RefPtr<MediaEngineSource>& source : *iter.UserData()) { |
653 | 0 | if (source) { |
654 | 0 | source->Shutdown(); |
655 | 0 | } |
656 | 0 | } |
657 | 0 | } |
658 | 0 | for (auto iter = mASources.Iter(); !iter.Done(); iter.Next()) { |
659 | 0 | for (const RefPtr<MediaEngineDefaultAudioSource>& source : *iter.UserData()) { |
660 | 0 | if (source) { |
661 | 0 | source->Shutdown(); |
662 | 0 | } |
663 | 0 | } |
664 | 0 | } |
665 | 0 | mVSources.Clear(); |
666 | 0 | mASources.Clear(); |
667 | 0 | }; |
668 | | |
669 | | } // namespace mozilla |