Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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*>(×tamp), 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 |