content/media/webrtc/MediaEngineDefault.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "MediaEngineDefault.h"
michael@0 6
michael@0 7 #include "nsCOMPtr.h"
michael@0 8 #include "nsDOMFile.h"
michael@0 9 #include "nsILocalFile.h"
michael@0 10 #include "Layers.h"
michael@0 11 #include "ImageContainer.h"
michael@0 12 #include "ImageTypes.h"
michael@0 13 #include "prmem.h"
michael@0 14 #include "nsContentUtils.h"
michael@0 15
michael@0 16 #include "nsIFilePicker.h"
michael@0 17 #include "nsIPrefService.h"
michael@0 18 #include "nsIPrefBranch.h"
michael@0 19
michael@0 20 #ifdef MOZ_WIDGET_ANDROID
michael@0 21 #include "AndroidBridge.h"
michael@0 22 #include "nsISupportsUtils.h"
michael@0 23 #endif
michael@0 24
michael@0 25 #if defined(MOZ_WEBRTC) && defined(MOZ_WEBRTC_SIGNALING)
michael@0 26 #include "YuvStamper.h"
michael@0 27 #endif
michael@0 28
michael@0 29 #define VIDEO_RATE USECS_PER_S
michael@0 30 #define AUDIO_RATE 16000
michael@0 31 #define AUDIO_FRAME_LENGTH ((AUDIO_RATE * MediaEngine::DEFAULT_AUDIO_TIMER_MS) / 1000)
michael@0 32 namespace mozilla {
michael@0 33
michael@0 34 using namespace mozilla::gfx;
michael@0 35
michael@0 36 NS_IMPL_ISUPPORTS(MediaEngineDefaultVideoSource, nsITimerCallback)
michael@0 37 /**
michael@0 38 * Default video source.
michael@0 39 */
michael@0 40
michael@0 41 MediaEngineDefaultVideoSource::MediaEngineDefaultVideoSource()
michael@0 42 : mTimer(nullptr), mMonitor("Fake video")
michael@0 43 {
michael@0 44 mImageContainer = layers::LayerManager::CreateImageContainer();
michael@0 45 mState = kReleased;
michael@0 46 }
michael@0 47
michael@0 48 MediaEngineDefaultVideoSource::~MediaEngineDefaultVideoSource()
michael@0 49 {}
michael@0 50
michael@0 51 void
michael@0 52 MediaEngineDefaultVideoSource::GetName(nsAString& aName)
michael@0 53 {
michael@0 54 aName.Assign(NS_LITERAL_STRING("Default Video Device"));
michael@0 55 return;
michael@0 56 }
michael@0 57
michael@0 58 void
michael@0 59 MediaEngineDefaultVideoSource::GetUUID(nsAString& aUUID)
michael@0 60 {
michael@0 61 aUUID.Assign(NS_LITERAL_STRING("1041FCBD-3F12-4F7B-9E9B-1EC556DD5676"));
michael@0 62 return;
michael@0 63 }
michael@0 64
michael@0 65 nsresult
michael@0 66 MediaEngineDefaultVideoSource::Allocate(const VideoTrackConstraintsN &aConstraints,
michael@0 67 const MediaEnginePrefs &aPrefs)
michael@0 68 {
michael@0 69 if (mState != kReleased) {
michael@0 70 return NS_ERROR_FAILURE;
michael@0 71 }
michael@0 72
michael@0 73 mOpts = aPrefs;
michael@0 74 mOpts.mWidth = mOpts.mWidth ? mOpts.mWidth : MediaEngine::DEFAULT_43_VIDEO_WIDTH;
michael@0 75 mOpts.mHeight = mOpts.mHeight ? mOpts.mHeight : MediaEngine::DEFAULT_43_VIDEO_HEIGHT;
michael@0 76 mState = kAllocated;
michael@0 77 return NS_OK;
michael@0 78 }
michael@0 79
michael@0 80 nsresult
michael@0 81 MediaEngineDefaultVideoSource::Deallocate()
michael@0 82 {
michael@0 83 if (mState != kStopped && mState != kAllocated) {
michael@0 84 return NS_ERROR_FAILURE;
michael@0 85 }
michael@0 86 mState = kReleased;
michael@0 87 return NS_OK;
michael@0 88 }
michael@0 89
michael@0 90 static void AllocateSolidColorFrame(layers::PlanarYCbCrData& aData,
michael@0 91 int aWidth, int aHeight,
michael@0 92 int aY, int aCb, int aCr)
michael@0 93 {
michael@0 94 MOZ_ASSERT(!(aWidth&1));
michael@0 95 MOZ_ASSERT(!(aHeight&1));
michael@0 96 // Allocate a single frame with a solid color
michael@0 97 int yLen = aWidth*aHeight;
michael@0 98 int cbLen = yLen>>2;
michael@0 99 int crLen = cbLen;
michael@0 100 uint8_t* frame = (uint8_t*) PR_Malloc(yLen+cbLen+crLen);
michael@0 101 memset(frame, aY, yLen);
michael@0 102 memset(frame+yLen, aCb, cbLen);
michael@0 103 memset(frame+yLen+cbLen, aCr, crLen);
michael@0 104
michael@0 105 aData.mYChannel = frame;
michael@0 106 aData.mYSize = IntSize(aWidth, aHeight);
michael@0 107 aData.mYStride = aWidth;
michael@0 108 aData.mCbCrStride = aWidth>>1;
michael@0 109 aData.mCbChannel = frame + yLen;
michael@0 110 aData.mCrChannel = aData.mCbChannel + cbLen;
michael@0 111 aData.mCbCrSize = IntSize(aWidth>>1, aHeight>>1);
michael@0 112 aData.mPicX = 0;
michael@0 113 aData.mPicY = 0;
michael@0 114 aData.mPicSize = IntSize(aWidth, aHeight);
michael@0 115 aData.mStereoMode = StereoMode::MONO;
michael@0 116 }
michael@0 117
michael@0 118 static void ReleaseFrame(layers::PlanarYCbCrData& aData)
michael@0 119 {
michael@0 120 PR_Free(aData.mYChannel);
michael@0 121 }
michael@0 122
michael@0 123 nsresult
michael@0 124 MediaEngineDefaultVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
michael@0 125 {
michael@0 126 if (mState != kAllocated) {
michael@0 127 return NS_ERROR_FAILURE;
michael@0 128 }
michael@0 129
michael@0 130 mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 131 if (!mTimer) {
michael@0 132 return NS_ERROR_FAILURE;
michael@0 133 }
michael@0 134
michael@0 135 aStream->AddTrack(aID, VIDEO_RATE, 0, new VideoSegment());
michael@0 136 aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
michael@0 137
michael@0 138 // Remember TrackID so we can end it later
michael@0 139 mTrackID = aID;
michael@0 140
michael@0 141 // Start timer for subsequent frames
michael@0 142 #if defined(MOZ_WIDGET_GONK) && defined(DEBUG)
michael@0 143 // B2G emulator debug is very, very slow and has problems dealing with realtime audio inputs
michael@0 144 mTimer->InitWithCallback(this, (1000 / mOpts.mFPS)*10, nsITimer::TYPE_REPEATING_SLACK);
michael@0 145 #else
michael@0 146 mTimer->InitWithCallback(this, 1000 / mOpts.mFPS, nsITimer::TYPE_REPEATING_SLACK);
michael@0 147 #endif
michael@0 148 mState = kStarted;
michael@0 149
michael@0 150 return NS_OK;
michael@0 151 }
michael@0 152
michael@0 153 nsresult
michael@0 154 MediaEngineDefaultVideoSource::Stop(SourceMediaStream *aSource, TrackID aID)
michael@0 155 {
michael@0 156 if (mState != kStarted) {
michael@0 157 return NS_ERROR_FAILURE;
michael@0 158 }
michael@0 159 if (!mTimer) {
michael@0 160 return NS_ERROR_FAILURE;
michael@0 161 }
michael@0 162
michael@0 163 mTimer->Cancel();
michael@0 164 mTimer = nullptr;
michael@0 165
michael@0 166 aSource->EndTrack(aID);
michael@0 167 aSource->Finish();
michael@0 168
michael@0 169 mState = kStopped;
michael@0 170 return NS_OK;
michael@0 171 }
michael@0 172
michael@0 173 nsresult
michael@0 174 MediaEngineDefaultVideoSource::Snapshot(uint32_t aDuration, nsIDOMFile** aFile)
michael@0 175 {
michael@0 176 *aFile = nullptr;
michael@0 177
michael@0 178 #ifndef MOZ_WIDGET_ANDROID
michael@0 179 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 180 #else
michael@0 181 nsAutoString filePath;
michael@0 182 nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1");
michael@0 183 if (!filePicker)
michael@0 184 return NS_ERROR_FAILURE;
michael@0 185
michael@0 186 nsXPIDLString title;
michael@0 187 nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES, "Browse", title);
michael@0 188 int16_t mode = static_cast<int16_t>(nsIFilePicker::modeOpen);
michael@0 189
michael@0 190 nsresult rv = filePicker->Init(nullptr, title, mode);
michael@0 191 NS_ENSURE_SUCCESS(rv, rv);
michael@0 192 filePicker->AppendFilters(nsIFilePicker::filterImages);
michael@0 193
michael@0 194 // XXX - This API should be made async
michael@0 195 PRInt16 dialogReturn;
michael@0 196 rv = filePicker->Show(&dialogReturn);
michael@0 197 NS_ENSURE_SUCCESS(rv, rv);
michael@0 198 if (dialogReturn == nsIFilePicker::returnCancel) {
michael@0 199 *aFile = nullptr;
michael@0 200 return NS_OK;
michael@0 201 }
michael@0 202
michael@0 203 nsCOMPtr<nsIFile> localFile;
michael@0 204 filePicker->GetFile(getter_AddRefs(localFile));
michael@0 205
michael@0 206 if (!localFile) {
michael@0 207 *aFile = nullptr;
michael@0 208 return NS_OK;
michael@0 209 }
michael@0 210
michael@0 211 nsCOMPtr<nsIDOMFile> domFile = new nsDOMFileFile(localFile);
michael@0 212 domFile.forget(aFile);
michael@0 213 return NS_OK;
michael@0 214 #endif
michael@0 215 }
michael@0 216
michael@0 217 NS_IMETHODIMP
michael@0 218 MediaEngineDefaultVideoSource::Notify(nsITimer* aTimer)
michael@0 219 {
michael@0 220 // Update the target color
michael@0 221 if (mCr <= 16) {
michael@0 222 if (mCb < 240) {
michael@0 223 mCb++;
michael@0 224 } else {
michael@0 225 mCr++;
michael@0 226 }
michael@0 227 } else if (mCb >= 240) {
michael@0 228 if (mCr < 240) {
michael@0 229 mCr++;
michael@0 230 } else {
michael@0 231 mCb--;
michael@0 232 }
michael@0 233 } else if (mCr >= 240) {
michael@0 234 if (mCb > 16) {
michael@0 235 mCb--;
michael@0 236 } else {
michael@0 237 mCr--;
michael@0 238 }
michael@0 239 } else {
michael@0 240 mCr--;
michael@0 241 }
michael@0 242
michael@0 243 // Allocate a single solid color image
michael@0 244 nsRefPtr<layers::Image> image = mImageContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
michael@0 245 nsRefPtr<layers::PlanarYCbCrImage> ycbcr_image =
michael@0 246 static_cast<layers::PlanarYCbCrImage*>(image.get());
michael@0 247 layers::PlanarYCbCrData data;
michael@0 248 AllocateSolidColorFrame(data, mOpts.mWidth, mOpts.mHeight, 0x80, mCb, mCr);
michael@0 249
michael@0 250 #if defined(MOZ_WEBRTC) && defined(MOZ_WEBRTC_SIGNALING)
michael@0 251 uint64_t timestamp = PR_Now();
michael@0 252 YuvStamper::Encode(mOpts.mWidth, mOpts.mHeight, mOpts.mWidth,
michael@0 253 data.mYChannel,
michael@0 254 reinterpret_cast<unsigned char*>(&timestamp), sizeof(timestamp),
michael@0 255 0, 0);
michael@0 256 #endif
michael@0 257
michael@0 258 ycbcr_image->SetData(data);
michael@0 259 // SetData copies data, so we can free the frame
michael@0 260 ReleaseFrame(data);
michael@0 261
michael@0 262 MonitorAutoLock lock(mMonitor);
michael@0 263
michael@0 264 // implicitly releases last image
michael@0 265 mImage = ycbcr_image.forget();
michael@0 266
michael@0 267 return NS_OK;
michael@0 268 }
michael@0 269
michael@0 270 void
michael@0 271 MediaEngineDefaultVideoSource::NotifyPull(MediaStreamGraph* aGraph,
michael@0 272 SourceMediaStream *aSource,
michael@0 273 TrackID aID,
michael@0 274 StreamTime aDesiredTime,
michael@0 275 TrackTicks &aLastEndTime)
michael@0 276 {
michael@0 277 // AddTrack takes ownership of segment
michael@0 278 VideoSegment segment;
michael@0 279 MonitorAutoLock lock(mMonitor);
michael@0 280 if (mState != kStarted) {
michael@0 281 return;
michael@0 282 }
michael@0 283
michael@0 284 // Note: we're not giving up mImage here
michael@0 285 nsRefPtr<layers::Image> image = mImage;
michael@0 286 TrackTicks target = TimeToTicksRoundUp(USECS_PER_S, aDesiredTime);
michael@0 287 TrackTicks delta = target - aLastEndTime;
michael@0 288
michael@0 289 if (delta > 0) {
michael@0 290 // nullptr images are allowed
michael@0 291 IntSize size(image ? mOpts.mWidth : 0, image ? mOpts.mHeight : 0);
michael@0 292 segment.AppendFrame(image.forget(), delta, size);
michael@0 293 // This can fail if either a) we haven't added the track yet, or b)
michael@0 294 // we've removed or finished the track.
michael@0 295 if (aSource->AppendToTrack(aID, &segment)) {
michael@0 296 aLastEndTime = target;
michael@0 297 }
michael@0 298 }
michael@0 299 }
michael@0 300
michael@0 301 // generate 1k sine wave per second
michael@0 302 class SineWaveGenerator
michael@0 303 {
michael@0 304 public:
michael@0 305 static const int bytesPerSample = 2;
michael@0 306 static const int millisecondsPerSecond = 1000;
michael@0 307 static const int frequency = 1000;
michael@0 308
michael@0 309 SineWaveGenerator(int aSampleRate) :
michael@0 310 mTotalLength(aSampleRate / frequency),
michael@0 311 mReadLength(0) {
michael@0 312 MOZ_ASSERT(mTotalLength * frequency == aSampleRate);
michael@0 313 mAudioBuffer = new int16_t[mTotalLength];
michael@0 314 for(int i = 0; i < mTotalLength; i++) {
michael@0 315 // Set volume to -20db. It's from 32768.0 * 10^(-20/20) = 3276.8
michael@0 316 mAudioBuffer[i] = (3276.8f * sin(2 * M_PI * i / mTotalLength));
michael@0 317 }
michael@0 318 }
michael@0 319
michael@0 320 // NOTE: only safely called from a single thread (MSG callback)
michael@0 321 void generate(int16_t* aBuffer, int16_t aLengthInSamples) {
michael@0 322 int16_t remaining = aLengthInSamples;
michael@0 323
michael@0 324 while (remaining) {
michael@0 325 int16_t processSamples = 0;
michael@0 326
michael@0 327 if (mTotalLength - mReadLength >= remaining) {
michael@0 328 processSamples = remaining;
michael@0 329 } else {
michael@0 330 processSamples = mTotalLength - mReadLength;
michael@0 331 }
michael@0 332 memcpy(aBuffer, mAudioBuffer + mReadLength, processSamples * bytesPerSample);
michael@0 333 aBuffer += processSamples;
michael@0 334 mReadLength += processSamples;
michael@0 335 remaining -= processSamples;
michael@0 336 if (mReadLength == mTotalLength) {
michael@0 337 mReadLength = 0;
michael@0 338 }
michael@0 339 }
michael@0 340 }
michael@0 341
michael@0 342 private:
michael@0 343 nsAutoArrayPtr<int16_t> mAudioBuffer;
michael@0 344 int16_t mTotalLength;
michael@0 345 int16_t mReadLength;
michael@0 346 };
michael@0 347
michael@0 348 /**
michael@0 349 * Default audio source.
michael@0 350 */
michael@0 351 NS_IMPL_ISUPPORTS(MediaEngineDefaultAudioSource, nsITimerCallback)
michael@0 352
michael@0 353 MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource()
michael@0 354 : mTimer(nullptr)
michael@0 355 {
michael@0 356 mState = kReleased;
michael@0 357 }
michael@0 358
michael@0 359 MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource()
michael@0 360 {}
michael@0 361
michael@0 362 void
michael@0 363 MediaEngineDefaultAudioSource::GetName(nsAString& aName)
michael@0 364 {
michael@0 365 aName.Assign(NS_LITERAL_STRING("Default Audio Device"));
michael@0 366 return;
michael@0 367 }
michael@0 368
michael@0 369 void
michael@0 370 MediaEngineDefaultAudioSource::GetUUID(nsAString& aUUID)
michael@0 371 {
michael@0 372 aUUID.Assign(NS_LITERAL_STRING("B7CBD7C1-53EF-42F9-8353-73F61C70C092"));
michael@0 373 return;
michael@0 374 }
michael@0 375
michael@0 376 nsresult
michael@0 377 MediaEngineDefaultAudioSource::Allocate(const AudioTrackConstraintsN &aConstraints,
michael@0 378 const MediaEnginePrefs &aPrefs)
michael@0 379 {
michael@0 380 if (mState != kReleased) {
michael@0 381 return NS_ERROR_FAILURE;
michael@0 382 }
michael@0 383
michael@0 384 mState = kAllocated;
michael@0 385 // generate 1Khz sine wave
michael@0 386 mSineGenerator = new SineWaveGenerator(AUDIO_RATE);
michael@0 387 return NS_OK;
michael@0 388 }
michael@0 389
michael@0 390 nsresult
michael@0 391 MediaEngineDefaultAudioSource::Deallocate()
michael@0 392 {
michael@0 393 if (mState != kStopped && mState != kAllocated) {
michael@0 394 return NS_ERROR_FAILURE;
michael@0 395 }
michael@0 396 mState = kReleased;
michael@0 397 return NS_OK;
michael@0 398 }
michael@0 399
michael@0 400 nsresult
michael@0 401 MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID)
michael@0 402 {
michael@0 403 if (mState != kAllocated) {
michael@0 404 return NS_ERROR_FAILURE;
michael@0 405 }
michael@0 406
michael@0 407 mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 408 if (!mTimer) {
michael@0 409 return NS_ERROR_FAILURE;
michael@0 410 }
michael@0 411
michael@0 412 mSource = aStream;
michael@0 413
michael@0 414 // AddTrack will take ownership of segment
michael@0 415 AudioSegment* segment = new AudioSegment();
michael@0 416 mSource->AddTrack(aID, AUDIO_RATE, 0, segment);
michael@0 417
michael@0 418 // We aren't going to add any more tracks
michael@0 419 mSource->AdvanceKnownTracksTime(STREAM_TIME_MAX);
michael@0 420
michael@0 421 // Remember TrackID so we can finish later
michael@0 422 mTrackID = aID;
michael@0 423
michael@0 424 // 1 Audio frame per 10ms
michael@0 425 #if defined(MOZ_WIDGET_GONK) && defined(DEBUG)
michael@0 426 // B2G emulator debug is very, very slow and has problems dealing with realtime audio inputs
michael@0 427 mTimer->InitWithCallback(this, MediaEngine::DEFAULT_AUDIO_TIMER_MS*10,
michael@0 428 nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP);
michael@0 429 #else
michael@0 430 mTimer->InitWithCallback(this, MediaEngine::DEFAULT_AUDIO_TIMER_MS,
michael@0 431 nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP);
michael@0 432 #endif
michael@0 433 mState = kStarted;
michael@0 434
michael@0 435 return NS_OK;
michael@0 436 }
michael@0 437
michael@0 438 nsresult
michael@0 439 MediaEngineDefaultAudioSource::Stop(SourceMediaStream *aSource, TrackID aID)
michael@0 440 {
michael@0 441 if (mState != kStarted) {
michael@0 442 return NS_ERROR_FAILURE;
michael@0 443 }
michael@0 444 if (!mTimer) {
michael@0 445 return NS_ERROR_FAILURE;
michael@0 446 }
michael@0 447
michael@0 448 mTimer->Cancel();
michael@0 449 mTimer = nullptr;
michael@0 450
michael@0 451 aSource->EndTrack(aID);
michael@0 452 aSource->Finish();
michael@0 453
michael@0 454 mState = kStopped;
michael@0 455 return NS_OK;
michael@0 456 }
michael@0 457
michael@0 458 nsresult
michael@0 459 MediaEngineDefaultAudioSource::Snapshot(uint32_t aDuration, nsIDOMFile** aFile)
michael@0 460 {
michael@0 461 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 462 }
michael@0 463
michael@0 464 NS_IMETHODIMP
michael@0 465 MediaEngineDefaultAudioSource::Notify(nsITimer* aTimer)
michael@0 466 {
michael@0 467 AudioSegment segment;
michael@0 468 nsRefPtr<SharedBuffer> buffer = SharedBuffer::Create(AUDIO_FRAME_LENGTH * sizeof(int16_t));
michael@0 469 int16_t* dest = static_cast<int16_t*>(buffer->Data());
michael@0 470
michael@0 471 mSineGenerator->generate(dest, AUDIO_FRAME_LENGTH);
michael@0 472 nsAutoTArray<const int16_t*,1> channels;
michael@0 473 channels.AppendElement(dest);
michael@0 474 segment.AppendFrames(buffer.forget(), channels, AUDIO_FRAME_LENGTH);
michael@0 475 mSource->AppendToTrack(mTrackID, &segment);
michael@0 476
michael@0 477 return NS_OK;
michael@0 478 }
michael@0 479
michael@0 480 void
michael@0 481 MediaEngineDefault::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources) {
michael@0 482 MutexAutoLock lock(mMutex);
michael@0 483
michael@0 484 // We once had code here to find a VideoSource with the same settings and re-use that.
michael@0 485 // This no longer is possible since the resolution is being set in Allocate().
michael@0 486
michael@0 487 nsRefPtr<MediaEngineVideoSource> newSource = new MediaEngineDefaultVideoSource();
michael@0 488 mVSources.AppendElement(newSource);
michael@0 489 aVSources->AppendElement(newSource);
michael@0 490
michael@0 491 return;
michael@0 492 }
michael@0 493
michael@0 494 void
michael@0 495 MediaEngineDefault::EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources) {
michael@0 496 MutexAutoLock lock(mMutex);
michael@0 497 int32_t len = mASources.Length();
michael@0 498
michael@0 499 for (int32_t i = 0; i < len; i++) {
michael@0 500 nsRefPtr<MediaEngineAudioSource> source = mASources.ElementAt(i);
michael@0 501 if (source->IsAvailable()) {
michael@0 502 aASources->AppendElement(source);
michael@0 503 }
michael@0 504 }
michael@0 505
michael@0 506 // All streams are currently busy, just make a new one.
michael@0 507 if (aASources->Length() == 0) {
michael@0 508 nsRefPtr<MediaEngineAudioSource> newSource =
michael@0 509 new MediaEngineDefaultAudioSource();
michael@0 510 mASources.AppendElement(newSource);
michael@0 511 aASources->AppendElement(newSource);
michael@0 512 }
michael@0 513 return;
michael@0 514 }
michael@0 515
michael@0 516 } // namespace mozilla

mercurial