Bug 1307042: generate fake audio for getUserMedia from MSG callbacks r=pehrsons
authorRandell Jesup <rjesup@jesup.org>
Mon, 03 Oct 2016 14:35:36 -0400
changeset 318514 1d480bd0f75885e1e0bb69036b39bbf884df9160
parent 318513 f727edc4be4805fb275be192145f0a5902477589
child 318515 c701835f10d7c512b6bf4e12a1e24bca372339a1
push id20725
push userphilringnalda@gmail.com
push dateThu, 20 Oct 2016 01:36:01 +0000
treeherderfx-team@998ad5a74da8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspehrsons
bugs1307042
milestone52.0a1
Bug 1307042: generate fake audio for getUserMedia from MSG callbacks r=pehrsons
dom/media/webrtc/MediaEngineDefault.cpp
dom/media/webrtc/MediaEngineDefault.h
media/webrtc/signaling/test/FakeMediaStreams.h
media/webrtc/signaling/test/FakeMediaStreamsImpl.h
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -8,16 +8,17 @@
 #include "mozilla/dom/File.h"
 #include "mozilla/UniquePtr.h"
 #include "nsILocalFile.h"
 #include "Layers.h"
 #include "ImageContainer.h"
 #include "ImageTypes.h"
 #include "prmem.h"
 #include "nsContentUtils.h"
+#include "MediaStreamGraph.h"
 
 #include "nsIFilePicker.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "nsISupportsUtils.h"
 #endif
@@ -362,24 +363,23 @@ private:
   UniquePtr<int16_t[]> mAudioBuffer;
   int16_t mTotalLength;
   int16_t mReadLength;
 };
 
 /**
  * Default audio source.
  */
-NS_IMPL_ISUPPORTS(MediaEngineDefaultAudioSource, nsITimerCallback)
+
+NS_IMPL_ISUPPORTS0(MediaEngineDefaultAudioSource)
 
 MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource()
   : MediaEngineAudioSource(kReleased)
-  , mPrincipalHandle(PRINCIPAL_HANDLE_NONE)
-  , mTimer(nullptr)
-{
-}
+  , mLastNotify(0)
+{}
 
 MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource()
 {}
 
 void
 MediaEngineDefaultAudioSource::GetName(nsAString& aName) const
 {
   aName.AssignLiteral(u"Default Audio Device");
@@ -448,69 +448,34 @@ MediaEngineDefaultAudioSource::Deallocat
 nsresult
 MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID,
                                      const PrincipalHandle& aPrincipalHandle)
 {
   if (mState != kAllocated) {
     return NS_ERROR_FAILURE;
   }
 
-  mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-  if (!mTimer) {
-    return NS_ERROR_FAILURE;
-  }
-
-  mSource = aStream;
-
-  // We try to keep the appended data at this size.
-  // Make it two timer intervals to try to avoid underruns.
-  mBufferSize = 2 * (AUDIO_RATE * DEFAULT_AUDIO_TIMER_MS) / 1000;
-
   // AddTrack will take ownership of segment
   AudioSegment* segment = new AudioSegment();
-  AppendToSegment(*segment, mBufferSize);
-  mSource->AddAudioTrack(aID, AUDIO_RATE, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
+  aStream->AddAudioTrack(aID, AUDIO_RATE, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
 
   // Remember TrackID so we can finish later
   mTrackID = aID;
 
-  // Remember PrincipalHandle since we don't append in NotifyPull.
-  mPrincipalHandle = aPrincipalHandle;
-
-  mLastNotify = TimeStamp::Now();
-
-  // 1 Audio frame per 10ms
-  // We'd like to do this for Android Debug as well, but that breaks tests that check for
-  // audio frequency data.
-#if defined(MOZ_WIDGET_GONK) && defined(DEBUG)
-// emulator debug is very, very slow and has problems dealing with realtime audio inputs
-  mTimer->InitWithCallback(this, DEFAULT_AUDIO_TIMER_MS*10,
-                           nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP);
-#else
-  mTimer->InitWithCallback(this, DEFAULT_AUDIO_TIMER_MS,
-                           nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP);
-#endif
+  mLastNotify = 0;
   mState = kStarted;
-
   return NS_OK;
 }
 
 nsresult
 MediaEngineDefaultAudioSource::Stop(SourceMediaStream *aSource, TrackID aID)
 {
   if (mState != kStarted) {
     return NS_ERROR_FAILURE;
   }
-  if (!mTimer) {
-    return NS_ERROR_FAILURE;
-  }
-
-  mTimer->Cancel();
-  mTimer = nullptr;
-
   aSource->EndTrack(aID);
 
   mState = kStopped;
   return NS_OK;
 }
 
 nsresult
 MediaEngineDefaultAudioSource::Restart(AllocationHandle* aHandle,
@@ -519,46 +484,43 @@ MediaEngineDefaultAudioSource::Restart(A
                                        const nsString& aDeviceId,
                                        const char** aOutBadConstraint)
 {
   return NS_OK;
 }
 
 void
 MediaEngineDefaultAudioSource::AppendToSegment(AudioSegment& aSegment,
-                                               TrackTicks aSamples)
+                                               TrackTicks aSamples,
+                                               const PrincipalHandle& aPrincipalHandle)
 {
   RefPtr<SharedBuffer> buffer = SharedBuffer::Create(aSamples * sizeof(int16_t));
   int16_t* dest = static_cast<int16_t*>(buffer->Data());
 
   mSineGenerator->generate(dest, aSamples);
   AutoTArray<const int16_t*,1> channels;
   channels.AppendElement(dest);
-  aSegment.AppendFrames(buffer.forget(), channels, aSamples, mPrincipalHandle);
+  aSegment.AppendFrames(buffer.forget(), channels, aSamples, aPrincipalHandle);
 }
 
-NS_IMETHODIMP
-MediaEngineDefaultAudioSource::Notify(nsITimer* aTimer)
+void
+MediaEngineDefaultAudioSource::NotifyPull(MediaStreamGraph* aGraph,
+                                          SourceMediaStream *aSource,
+                                          TrackID aID,
+                                          StreamTime aDesiredTime,
+                                          const PrincipalHandle& aPrincipalHandle)
 {
-  TimeStamp now = TimeStamp::Now();
-  TimeDuration timeSinceLastNotify = now - mLastNotify;
-  mLastNotify = now;
-  TrackTicks samplesSinceLastNotify =
-    RateConvertTicksRoundUp(AUDIO_RATE, 1000000, timeSinceLastNotify.ToMicroseconds());
-
-  // If it's been longer since the last Notify() than mBufferSize holds, we
-  // have underrun and the MSG had to append silence while waiting for us
-  // to push more data. In this case we reset to mBufferSize again.
-  TrackTicks samplesToAppend = std::min(samplesSinceLastNotify, mBufferSize);
-
+  MOZ_ASSERT(aID == mTrackID);
   AudioSegment segment;
-  AppendToSegment(segment, samplesToAppend);
-  mSource->AppendToTrack(mTrackID, &segment);
-
-  return NS_OK;
+  // avoid accumulating rounding errors
+  TrackTicks desired = aSource->TimeToTicksRoundUp(AUDIO_RATE, aDesiredTime);
+  TrackTicks delta = desired - mLastNotify;
+  mLastNotify += delta;
+  AppendToSegment(segment, delta, aPrincipalHandle);
+  aSource->AppendToTrack(mTrackID, &segment);
 }
 
 void
 MediaEngineDefault::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
                                           nsTArray<RefPtr<MediaEngineVideoSource> >* aVSources) {
   MutexAutoLock lock(mMutex);
 
   // only supports camera sources (for now).  See Bug 1038241
--- a/dom/media/webrtc/MediaEngineDefault.h
+++ b/dom/media/webrtc/MediaEngineDefault.h
@@ -92,32 +92,30 @@ protected:
   ~MediaEngineDefaultVideoSource();
 
   friend class MediaEngineDefault;
 
   TrackID mTrackID;
   nsCOMPtr<nsITimer> mTimer;
   // mMonitor protects mImage access/changes, and transitions of mState
   // from kStarted to kStopped (which are combined with EndTrack() and
-  // image changes).  Note that mSources is not accessed from other threads
-  // for video and is not protected.
+  // image changes).
   Monitor mMonitor;
   RefPtr<layers::Image> mImage;
 
   RefPtr<layers::ImageContainer> mImageContainer;
 
   MediaEnginePrefs mOpts;
   int mCb;
   int mCr;
 };
 
 class SineWaveGenerator;
 
-class MediaEngineDefaultAudioSource : public nsITimerCallback,
-                                      public MediaEngineAudioSource
+class MediaEngineDefaultAudioSource : public MediaEngineAudioSource
 {
 public:
   MediaEngineDefaultAudioSource();
 
   void GetName(nsAString&) const override;
   void GetUUID(nsACString&) const override;
 
   nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
@@ -130,32 +128,24 @@ public:
   nsresult Start(SourceMediaStream*, TrackID, const PrincipalHandle&) override;
   nsresult Stop(SourceMediaStream*, TrackID) override;
   nsresult Restart(AllocationHandle* aHandle,
                    const dom::MediaTrackConstraints& aConstraints,
                    const MediaEnginePrefs &aPrefs,
                    const nsString& aDeviceId,
                    const char** aOutBadConstraint) override;
   void SetDirectListeners(bool aHasDirectListeners) override {};
-  void AppendToSegment(AudioSegment& aSegment,
-                       TrackTicks aSamples);
+  void inline AppendToSegment(AudioSegment& aSegment,
+                              TrackTicks aSamples,
+                              const PrincipalHandle& aPrincipalHandle);
   void NotifyPull(MediaStreamGraph* aGraph,
                   SourceMediaStream *aSource,
                   TrackID aId,
                   StreamTime aDesiredTime,
-                  const PrincipalHandle& aPrincipalHandle) override
-  {
-#ifdef DEBUG
-    StreamTracks::Track* data = aSource->FindTrack(aId);
-    NS_WARNING_ASSERTION(
-      !data || data->IsEnded() ||
-      aDesiredTime <= aSource->GetEndOfAppendedData(aId),
-      "MediaEngineDefaultAudioSource data underrun");
-#endif
-  }
+                  const PrincipalHandle& aPrincipalHandle) override;
 
   void NotifyOutputData(MediaStreamGraph* aGraph,
                         AudioDataValue* aBuffer, size_t aFrames,
                         TrackRate aRate, uint32_t aChannels) override
   {}
   void NotifyInputData(MediaStreamGraph* aGraph,
                        const AudioDataValue* aBuffer, size_t aFrames,
                        TrackRate aRate, uint32_t aChannels) override
@@ -175,29 +165,25 @@ public:
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   uint32_t GetBestFitnessDistance(
       const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
       const nsString& aDeviceId) const override;
 
   NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSITIMERCALLBACK
 
 protected:
   ~MediaEngineDefaultAudioSource();
 
   TrackID mTrackID;
-  PrincipalHandle mPrincipalHandle;
-  nsCOMPtr<nsITimer> mTimer;
 
-  TimeStamp mLastNotify;
-  TrackTicks mBufferSize;
+  TrackTicks mLastNotify; // Accessed in ::Start(), then on NotifyPull (from MSG thread)
 
-  SourceMediaStream* mSource;
+  // Created on Allocate, then accessed from NotifyPull (MSG thread)
   nsAutoPtr<SineWaveGenerator> mSineGenerator;
 };
 
 
 class MediaEngineDefault : public MediaEngine
 {
   typedef MediaEngine Super;
 public:
--- a/media/webrtc/signaling/test/FakeMediaStreams.h
+++ b/media/webrtc/signaling/test/FakeMediaStreams.h
@@ -233,16 +233,18 @@ class Fake_MediaStream {
   virtual nsresult Stop() { return NS_OK; }
   virtual void StopStream() {}
 
   virtual void Periodic() {}
 
   double StreamTimeToSeconds(mozilla::StreamTime aTime);
   mozilla::StreamTime
   TicksToTimeRoundDown(mozilla::TrackRate aRate, mozilla::TrackTicks aTicks);
+  mozilla::TrackTicks TimeToTicksRoundUp(mozilla::TrackRate aRate,
+                                         mozilla::StreamTime aTime);
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Fake_MediaStream);
 
  protected:
   std::set<RefPtr<Fake_MediaStreamListener>> mListeners;
   std::vector<BoundTrackListener> mTrackListeners;
   mozilla::Mutex mMutex;  // Lock to prevent the listener list from being modified while
                           // executing Periodic().
--- a/media/webrtc/signaling/test/FakeMediaStreamsImpl.h
+++ b/media/webrtc/signaling/test/FakeMediaStreamsImpl.h
@@ -19,16 +19,21 @@ static const int GRAPH_RATE        = 160
 
 NS_IMPL_ISUPPORTS0(Fake_DOMMediaStream)
 
 // Fake_MediaStream
 double Fake_MediaStream::StreamTimeToSeconds(mozilla::StreamTime aTime) {
   return static_cast<double>(aTime)/GRAPH_RATE;
 }
 
+mozilla::TrackTicks Fake_MediaStream::TimeToTicksRoundUp(mozilla::TrackRate aRate,
+                                                         mozilla::StreamTime aTime) {
+  return (aTime * aRate) / GRAPH_RATE;
+}
+
 mozilla::StreamTime
 Fake_MediaStream::TicksToTimeRoundDown(mozilla::TrackRate aRate,
                                        mozilla::TrackTicks aTicks) {
   return aTicks * GRAPH_RATE / aRate;
 }
 
 // Fake_SourceMediaStream
 nsresult Fake_SourceMediaStream::Start() {