author | Paul Adenot <paul@paul.cx> |
Tue, 21 May 2019 10:10:04 +0000 | |
changeset 474708 | 6d2b52f066bf543dbbcbc3299de5147332392d82 |
parent 474707 | c4045738b0cdb76f1ff2c492c390751147bce62c |
child 474709 | 561a328e09ea3fc0e5fe93cf0c9d803b4695186f |
push id | 113168 |
push user | rmaries@mozilla.com |
push date | Tue, 21 May 2019 16:39:23 +0000 |
treeherder | mozilla-inbound@3c0f78074b72 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jya |
bugs | 1552530 |
milestone | 69.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/media/AudioStream.cpp +++ b/dom/media/AudioStream.cpp @@ -124,34 +124,30 @@ class FrameHistory { double mBasePosition; }; AudioStream::AudioStream(DataSource& aSource) : mMonitor("AudioStream"), mChannels(0), mOutChannels(0), mTimeStretcher(nullptr), - mDumpFile(nullptr), mState(INITIALIZED), mDataSource(aSource), mPrefillQuirk(false) { #if defined(XP_WIN) if (XRE_IsContentProcess()) { audio::AudioNotificationReceiver::Register(this); } #endif } AudioStream::~AudioStream() { LOG("deleted, state %d", mState); MOZ_ASSERT(mState == SHUTDOWN && !mCubebStream, "Should've called Shutdown() before deleting an AudioStream"); - if (mDumpFile) { - fclose(mDumpFile); - } if (mTimeStretcher) { soundtouch::destroySoundTouchObj(mTimeStretcher); } #if defined(XP_WIN) if (XRE_IsContentProcess()) { audio::AudioNotificationReceiver::Unregister(this); } #endif @@ -230,89 +226,16 @@ nsresult AudioStream::SetPreservesPitch( mTimeStretcher->setRate(mAudioClock.GetPlaybackRate()); } mAudioClock.SetPreservesPitch(aPreservesPitch); return NS_OK; } -static void SetUint16LE(uint8_t* aDest, uint16_t aValue) { - aDest[0] = aValue & 0xFF; - aDest[1] = aValue >> 8; -} - -static void SetUint32LE(uint8_t* aDest, uint32_t aValue) { - SetUint16LE(aDest, aValue & 0xFFFF); - SetUint16LE(aDest + 2, aValue >> 16); -} - -static FILE* OpenDumpFile(uint32_t aChannels, uint32_t aRate) { - /** - * When MOZ_DUMP_AUDIO is set in the environment (to anything), - * we'll drop a series of files in the current working directory named - * dumped-audio-<nnn>.wav, one per AudioStream created, containing - * the audio for the stream including any skips due to underruns. - */ - static Atomic<int> gDumpedAudioCount(0); - - if (!getenv("MOZ_DUMP_AUDIO")) return nullptr; - char buf[100]; - SprintfLiteral(buf, "dumped-audio-%d.wav", ++gDumpedAudioCount); - FILE* f = fopen(buf, "wb"); - if (!f) return nullptr; - - uint8_t header[] = { - // RIFF header - 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, - // fmt chunk. We always write 16-bit samples. - 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x10, 0x00, - // data chunk - 0x64, 0x61, 0x74, 0x61, 0xFE, 0xFF, 0xFF, 0x7F}; - static const int CHANNEL_OFFSET = 22; - static const int SAMPLE_RATE_OFFSET = 24; - static const int BLOCK_ALIGN_OFFSET = 32; - SetUint16LE(header + CHANNEL_OFFSET, aChannels); - SetUint32LE(header + SAMPLE_RATE_OFFSET, aRate); - SetUint16LE(header + BLOCK_ALIGN_OFFSET, aChannels * 2); - Unused << fwrite(header, sizeof(header), 1, f); - - return f; -} - -template <typename T> -typename EnableIf<IsSame<T, int16_t>::value, void>::Type WriteDumpFileHelper( - T* aInput, size_t aSamples, FILE* aFile) { - Unused << fwrite(aInput, sizeof(T), aSamples, aFile); -} - -template <typename T> -typename EnableIf<IsSame<T, float>::value, void>::Type WriteDumpFileHelper( - T* aInput, size_t aSamples, FILE* aFile) { - AutoTArray<uint8_t, 1024 * 2> buf; - buf.SetLength(aSamples * 2); - uint8_t* output = buf.Elements(); - for (uint32_t i = 0; i < aSamples; ++i) { - SetUint16LE(output + i * 2, int16_t(aInput[i] * 32767.0f)); - } - Unused << fwrite(output, 2, aSamples, aFile); - fflush(aFile); -} - -static void WriteDumpFile(FILE* aDumpFile, AudioStream* aStream, - uint32_t aFrames, void* aBuffer) { - if (!aDumpFile) return; - - uint32_t samples = aStream->GetOutChannels() * aFrames; - - using SampleT = AudioSampleTraits<AUDIO_OUTPUT_FORMAT>::Type; - WriteDumpFileHelper(reinterpret_cast<SampleT*>(aBuffer), samples, aDumpFile); -} - template <AudioSampleFormat N> struct ToCubebFormat { static const cubeb_sample_format value = CUBEB_SAMPLE_FLOAT32NE; }; template <> struct ToCubebFormat<AUDIO_FORMAT_S16> { static const cubeb_sample_format value = CUBEB_SAMPLE_S16NE; @@ -328,27 +251,28 @@ nsresult AudioStream::Init(uint32_t aNum AudioConfig::ChannelLayout::ChannelMap aChannelMap, uint32_t aRate, AudioDeviceInfo* aSinkInfo) { auto startTime = TimeStamp::Now(); LOG("%s channels: %d, rate: %d", __FUNCTION__, aNumChannels, aRate); mChannels = aNumChannels; mOutChannels = aNumChannels; - mDumpFile = OpenDumpFile(aNumChannels, aRate); - mSinkInfo = aSinkInfo; cubeb_stream_params params; params.rate = aRate; params.channels = mOutChannels; params.layout = static_cast<uint32_t>(aChannelMap); params.format = ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value; params.prefs = CubebUtils::GetDefaultStreamPrefs(); + // This is noop if MOZ_DUMP_AUDIO is not set. + mDumpFile.Open("AudioStream", mOutChannels, aRate); + mAudioClock.Init(aRate); cubeb* cubebContext = CubebUtils::GetCubebContext(); if (!cubebContext) { LOGE("Can't get cubeb context!"); CubebUtils::ReportCubebStreamInitFailure(true); return NS_ERROR_DOM_MEDIA_CUBEB_INITIALIZATION_ERR; } @@ -663,17 +587,18 @@ long AudioStream::DataCallback(void* aBu writer.WriteZeros(writer.Available()); } } else { // No more new data in the data source. Don't send silent frames so the // cubeb stream can start draining. mAudioClock.UpdateFrameHistory(aFrames - writer.Available(), 0); } - WriteDumpFile(mDumpFile, this, aFrames, aBuffer); + mDumpFile.Write(static_cast<const AudioDataValue*>(aBuffer), + aFrames * mOutChannels); return aFrames - writer.Available(); } void AudioStream::StateCallback(cubeb_state aState) { MonitorAutoLock mon(mMonitor); MOZ_ASSERT(mState != SHUTDOWN, "No state callback after shutdown"); LOG("StateCallback, mState=%d cubeb_state=%d", mState, aState);
--- a/dom/media/AudioStream.h +++ b/dom/media/AudioStream.h @@ -12,16 +12,17 @@ # include "mozilla/Monitor.h" # include "mozilla/RefPtr.h" # include "mozilla/TimeStamp.h" # include "mozilla/UniquePtr.h" # include "nsAutoPtr.h" # include "nsCOMPtr.h" # include "nsThreadUtils.h" # include "soundtouch/SoundTouchFactory.h" +# include "WavDumper.h" # if defined(XP_WIN) # include "mozilla/audio/AudioNotificationReceiver.h" # endif namespace mozilla { struct CubebDestroyPolicy { @@ -303,18 +304,17 @@ class AudioStream final // The monitor is held to protect all access to member variables. Monitor mMonitor; uint32_t mChannels; uint32_t mOutChannels; AudioClock mAudioClock; soundtouch::SoundTouch* mTimeStretcher; - // Output file for dumping audio - FILE* mDumpFile; + WavDumper mDumpFile; // Owning reference to a cubeb_stream. UniquePtr<cubeb_stream, CubebDestroyPolicy> mCubebStream; enum StreamState { INITIALIZED, // Initialized, playback has not begun. STARTED, // cubeb started. STOPPED, // Stopped by a call to Pause().
new file mode 100644 --- /dev/null +++ b/dom/media/WavDumper.h @@ -0,0 +1,117 @@ +#if !defined(WavDumper_h_) +# define WavDumper_h_ +# include <stdio.h> +# include <stdint.h> +# include <nsTArray.h> +# include <mozilla/Unused.h> +# include <mozilla/Atomics.h> +# include <mozilla/DebugOnly.h> +# include <ByteWriter.h> + +/** + * If MOZ_DUMP_AUDIO is set, this dumps a file to disk containing the output of + * an audio stream, in 16bits integers. + * + * The sandbox needs to be disabled for this to work. + */ +class WavDumper { + public: + WavDumper() = default; + ~WavDumper() { + if (mFile) { + fclose(mFile); + } + } + void Open(const char* aBaseName, uint32_t aChannels, uint32_t aRate) { + using namespace mozilla; + + if (!getenv("MOZ_DUMP_AUDIO")) { + return; + } + + static mozilla::Atomic<int> sDumpedAudioCount(0); + + char buf[100]; + SprintfLiteral(buf, "%s-%d.wav", aBaseName, ++sDumpedAudioCount); + mFile = fopen(buf, "wb"); + if (!mFile) { + NS_WARNING("Could not open file to DUMP a wav. Is sandboxing disabled?"); + return; + } + const uint8_t riffHeader[] = { + // RIFF header + 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, + // fmt chunk. We always write 16-bit samples. + 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x10, 0x00, + // data chunk + 0x64, 0x61, 0x74, 0x61, 0xFE, 0xFF, 0xFF, 0x7F}; + AutoTArray<uint8_t, sizeof(riffHeader)> header; + ByteWriter<LittleEndian> writer(header); + static const int CHANNEL_OFFSET = 22; + static const int SAMPLE_RATE_OFFSET = 24; + static const int BLOCK_ALIGN_OFFSET = 32; + + DebugOnly<bool> rv; + // Then number of bytes written in each iteration. + uint32_t written = 0; + for (size_t i = 0; i != sizeof(riffHeader);) { + switch (i) { + case CHANNEL_OFFSET: + rv = writer.WriteU16(aChannels); + written = 2; + MOZ_ASSERT(rv); + break; + case SAMPLE_RATE_OFFSET: + rv = writer.WriteU32(aRate); + written = 4; + MOZ_ASSERT(rv); + break; + case BLOCK_ALIGN_OFFSET: + rv = writer.WriteU16(aChannels * 2); + written = 2; + MOZ_ASSERT(rv); + break; + default: + // copy from the riffHeader struct above + rv = writer.WriteU8(riffHeader[i]); + written = 1; + MOZ_ASSERT(rv); + break; + } + i += written; + } + Unused << fwrite(header.Elements(), header.Length(), 1, mFile); + } + + template <typename T> + void Write(const T* aBuffer, uint32_t aSamples) { + if (!mFile) { + return; + } + WriteDumpFileHelper(aBuffer, aSamples); + } + + private: + void WriteDumpFileHelper(const int16_t* aInput, size_t aSamples) { + mozilla::Unused << fwrite(aInput, sizeof(int16_t), aSamples, mFile); + fflush(mFile); + } + + void WriteDumpFileHelper(const float* aInput, size_t aSamples) { + using namespace mozilla; + + AutoTArray<uint8_t, 1024 * 2> buf; + ByteWriter<mozilla::LittleEndian> writer(buf); + for (uint32_t i = 0; i < aSamples; ++i) { + DebugOnly<bool> rv = writer.WriteU16(int16_t(aInput[i] * 32767.0f)); + MOZ_ASSERT(rv); + } + mozilla::Unused << fwrite(buf.Elements(), buf.Length(), 1, mFile); + fflush(mFile); + } + + FILE* mFile = nullptr; +}; + +#endif // WavDumper_h_
--- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -161,16 +161,17 @@ EXPORTS += [ 'Tracing.h', 'TrackID.h', 'TrackUnionStream.h', 'VideoFrameContainer.h', 'VideoLimits.h', 'VideoSegment.h', 'VideoUtils.h', 'VorbisUtils.h', + 'WavDumper.h', 'XiphExtradata.h', ] EXPORTS.mozilla += [ 'MediaManager.h', ] EXPORTS.mozilla.media.webrtc += [