Bug 1307042: generate fake audio for getUserMedia from MSG callbacks r=pehrsons

This commit is contained in:
Randell Jesup 2016-10-03 14:35:36 -04:00
parent 1530c55efb
commit 88bea09e95
4 changed files with 38 additions and 83 deletions

View File

@ -13,6 +13,7 @@
#include "ImageTypes.h" #include "ImageTypes.h"
#include "prmem.h" #include "prmem.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "MediaStreamGraph.h"
#include "nsIFilePicker.h" #include "nsIFilePicker.h"
#include "nsIPrefService.h" #include "nsIPrefService.h"
@ -367,14 +368,13 @@ private:
/** /**
* Default audio source. * Default audio source.
*/ */
NS_IMPL_ISUPPORTS(MediaEngineDefaultAudioSource, nsITimerCallback)
NS_IMPL_ISUPPORTS0(MediaEngineDefaultAudioSource)
MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource() MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource()
: MediaEngineAudioSource(kReleased) : MediaEngineAudioSource(kReleased)
, mPrincipalHandle(PRINCIPAL_HANDLE_NONE) , mLastNotify(0)
, mTimer(nullptr) {}
{
}
MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource() MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource()
{} {}
@ -453,43 +453,15 @@ MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID,
return NS_ERROR_FAILURE; 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 // AddTrack will take ownership of segment
AudioSegment* segment = new AudioSegment(); AudioSegment* segment = new AudioSegment();
AppendToSegment(*segment, mBufferSize); aStream->AddAudioTrack(aID, AUDIO_RATE, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
mSource->AddAudioTrack(aID, AUDIO_RATE, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
// Remember TrackID so we can finish later // Remember TrackID so we can finish later
mTrackID = aID; mTrackID = aID;
// Remember PrincipalHandle since we don't append in NotifyPull. mLastNotify = 0;
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
mState = kStarted; mState = kStarted;
return NS_OK; return NS_OK;
} }
@ -499,13 +471,6 @@ MediaEngineDefaultAudioSource::Stop(SourceMediaStream *aSource, TrackID aID)
if (mState != kStarted) { if (mState != kStarted) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
if (!mTimer) {
return NS_ERROR_FAILURE;
}
mTimer->Cancel();
mTimer = nullptr;
aSource->EndTrack(aID); aSource->EndTrack(aID);
mState = kStopped; mState = kStopped;
@ -524,7 +489,8 @@ MediaEngineDefaultAudioSource::Restart(AllocationHandle* aHandle,
void void
MediaEngineDefaultAudioSource::AppendToSegment(AudioSegment& aSegment, MediaEngineDefaultAudioSource::AppendToSegment(AudioSegment& aSegment,
TrackTicks aSamples) TrackTicks aSamples,
const PrincipalHandle& aPrincipalHandle)
{ {
RefPtr<SharedBuffer> buffer = SharedBuffer::Create(aSamples * sizeof(int16_t)); RefPtr<SharedBuffer> buffer = SharedBuffer::Create(aSamples * sizeof(int16_t));
int16_t* dest = static_cast<int16_t*>(buffer->Data()); int16_t* dest = static_cast<int16_t*>(buffer->Data());
@ -532,28 +498,24 @@ MediaEngineDefaultAudioSource::AppendToSegment(AudioSegment& aSegment,
mSineGenerator->generate(dest, aSamples); mSineGenerator->generate(dest, aSamples);
AutoTArray<const int16_t*,1> channels; AutoTArray<const int16_t*,1> channels;
channels.AppendElement(dest); channels.AppendElement(dest);
aSegment.AppendFrames(buffer.forget(), channels, aSamples, mPrincipalHandle); aSegment.AppendFrames(buffer.forget(), channels, aSamples, aPrincipalHandle);
} }
NS_IMETHODIMP void
MediaEngineDefaultAudioSource::Notify(nsITimer* aTimer) MediaEngineDefaultAudioSource::NotifyPull(MediaStreamGraph* aGraph,
SourceMediaStream *aSource,
TrackID aID,
StreamTime aDesiredTime,
const PrincipalHandle& aPrincipalHandle)
{ {
TimeStamp now = TimeStamp::Now(); MOZ_ASSERT(aID == mTrackID);
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);
AudioSegment segment; AudioSegment segment;
AppendToSegment(segment, samplesToAppend); // avoid accumulating rounding errors
mSource->AppendToTrack(mTrackID, &segment); TrackTicks desired = aSource->TimeToTicksRoundUp(AUDIO_RATE, aDesiredTime);
TrackTicks delta = desired - mLastNotify;
return NS_OK; mLastNotify += delta;
AppendToSegment(segment, delta, aPrincipalHandle);
aSource->AppendToTrack(mTrackID, &segment);
} }
void void

View File

@ -97,8 +97,7 @@ protected:
nsCOMPtr<nsITimer> mTimer; nsCOMPtr<nsITimer> mTimer;
// mMonitor protects mImage access/changes, and transitions of mState // mMonitor protects mImage access/changes, and transitions of mState
// from kStarted to kStopped (which are combined with EndTrack() and // from kStarted to kStopped (which are combined with EndTrack() and
// image changes). Note that mSources is not accessed from other threads // image changes).
// for video and is not protected.
Monitor mMonitor; Monitor mMonitor;
RefPtr<layers::Image> mImage; RefPtr<layers::Image> mImage;
@ -111,8 +110,7 @@ protected:
class SineWaveGenerator; class SineWaveGenerator;
class MediaEngineDefaultAudioSource : public nsITimerCallback, class MediaEngineDefaultAudioSource : public MediaEngineAudioSource
public MediaEngineAudioSource
{ {
public: public:
MediaEngineDefaultAudioSource(); MediaEngineDefaultAudioSource();
@ -135,22 +133,14 @@ public:
const nsString& aDeviceId, const nsString& aDeviceId,
const char** aOutBadConstraint) override; const char** aOutBadConstraint) override;
void SetDirectListeners(bool aHasDirectListeners) override {}; void SetDirectListeners(bool aHasDirectListeners) override {};
void AppendToSegment(AudioSegment& aSegment, void inline AppendToSegment(AudioSegment& aSegment,
TrackTicks aSamples); TrackTicks aSamples,
const PrincipalHandle& aPrincipalHandle);
void NotifyPull(MediaStreamGraph* aGraph, void NotifyPull(MediaStreamGraph* aGraph,
SourceMediaStream *aSource, SourceMediaStream *aSource,
TrackID aId, TrackID aId,
StreamTime aDesiredTime, StreamTime aDesiredTime,
const PrincipalHandle& aPrincipalHandle) override 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
}
void NotifyOutputData(MediaStreamGraph* aGraph, void NotifyOutputData(MediaStreamGraph* aGraph,
AudioDataValue* aBuffer, size_t aFrames, AudioDataValue* aBuffer, size_t aFrames,
@ -180,19 +170,15 @@ public:
const nsString& aDeviceId) const override; const nsString& aDeviceId) const override;
NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
protected: protected:
~MediaEngineDefaultAudioSource(); ~MediaEngineDefaultAudioSource();
TrackID mTrackID; TrackID mTrackID;
PrincipalHandle mPrincipalHandle;
nsCOMPtr<nsITimer> mTimer;
TimeStamp mLastNotify; TrackTicks mLastNotify; // Accessed in ::Start(), then on NotifyPull (from MSG thread)
TrackTicks mBufferSize;
SourceMediaStream* mSource; // Created on Allocate, then accessed from NotifyPull (MSG thread)
nsAutoPtr<SineWaveGenerator> mSineGenerator; nsAutoPtr<SineWaveGenerator> mSineGenerator;
}; };

View File

@ -238,6 +238,8 @@ class Fake_MediaStream {
double StreamTimeToSeconds(mozilla::StreamTime aTime); double StreamTimeToSeconds(mozilla::StreamTime aTime);
mozilla::StreamTime mozilla::StreamTime
TicksToTimeRoundDown(mozilla::TrackRate aRate, mozilla::TrackTicks aTicks); TicksToTimeRoundDown(mozilla::TrackRate aRate, mozilla::TrackTicks aTicks);
mozilla::TrackTicks TimeToTicksRoundUp(mozilla::TrackRate aRate,
mozilla::StreamTime aTime);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Fake_MediaStream); NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Fake_MediaStream);

View File

@ -24,6 +24,11 @@ double Fake_MediaStream::StreamTimeToSeconds(mozilla::StreamTime aTime) {
return static_cast<double>(aTime)/GRAPH_RATE; 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 mozilla::StreamTime
Fake_MediaStream::TicksToTimeRoundDown(mozilla::TrackRate aRate, Fake_MediaStream::TicksToTimeRoundDown(mozilla::TrackRate aRate,
mozilla::TrackTicks aTicks) { mozilla::TrackTicks aTicks) {