From 4be4624d57d76b0b92679ffe3af945165716ff66 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 21 May 2019 10:10:04 +0000 Subject: [PATCH] Bug 1552530 - Make the wav dumper in AudioStream.cpp reusable. r=jya Differential Revision: https://phabricator.services.mozilla.com/D31648 --HG-- extra : moz-landing-system : lando --- dom/media/AudioStream.cpp | 85 ++------------------------- dom/media/AudioStream.h | 4 +- dom/media/WavDumper.h | 117 ++++++++++++++++++++++++++++++++++++++ dom/media/moz.build | 1 + 4 files changed, 125 insertions(+), 82 deletions(-) create mode 100644 dom/media/WavDumper.h diff --git a/dom/media/AudioStream.cpp b/dom/media/AudioStream.cpp index 4c461d971fef..242789738413 100644 --- a/dom/media/AudioStream.cpp +++ b/dom/media/AudioStream.cpp @@ -129,7 +129,6 @@ AudioStream::AudioStream(DataSource& aSource) mChannels(0), mOutChannels(0), mTimeStretcher(nullptr), - mDumpFile(nullptr), mState(INITIALIZED), mDataSource(aSource), mPrefillQuirk(false) { @@ -144,9 +143,6 @@ 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); } @@ -235,79 +231,6 @@ nsresult AudioStream::SetPreservesPitch(bool 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-.wav, one per AudioStream created, containing - * the audio for the stream including any skips due to underruns. - */ - static Atomic 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 EnableIf::value, void>::Type WriteDumpFileHelper( - T* aInput, size_t aSamples, FILE* aFile) { - Unused << fwrite(aInput, sizeof(T), aSamples, aFile); -} - -template -typename EnableIf::value, void>::Type WriteDumpFileHelper( - T* aInput, size_t aSamples, FILE* aFile) { - AutoTArray 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::Type; - WriteDumpFileHelper(reinterpret_cast(aBuffer), samples, aDumpFile); -} - template struct ToCubebFormat { static const cubeb_sample_format value = CUBEB_SAMPLE_FLOAT32NE; @@ -333,8 +256,6 @@ nsresult AudioStream::Init(uint32_t aNumChannels, mChannels = aNumChannels; mOutChannels = aNumChannels; - mDumpFile = OpenDumpFile(aNumChannels, aRate); - mSinkInfo = aSinkInfo; cubeb_stream_params params; @@ -344,6 +265,9 @@ nsresult AudioStream::Init(uint32_t aNumChannels, params.format = ToCubebFormat::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(); @@ -668,7 +592,8 @@ long AudioStream::DataCallback(void* aBuffer, long aFrames) { mAudioClock.UpdateFrameHistory(aFrames - writer.Available(), 0); } - WriteDumpFile(mDumpFile, this, aFrames, aBuffer); + mDumpFile.Write(static_cast(aBuffer), + aFrames * mOutChannels); return aFrames - writer.Available(); } diff --git a/dom/media/AudioStream.h b/dom/media/AudioStream.h index 4efca6814ea5..de1667503e7d 100644 --- a/dom/media/AudioStream.h +++ b/dom/media/AudioStream.h @@ -17,6 +17,7 @@ # include "nsCOMPtr.h" # include "nsThreadUtils.h" # include "soundtouch/SoundTouchFactory.h" +# include "WavDumper.h" # if defined(XP_WIN) # include "mozilla/audio/AudioNotificationReceiver.h" @@ -308,8 +309,7 @@ class AudioStream final AudioClock mAudioClock; soundtouch::SoundTouch* mTimeStretcher; - // Output file for dumping audio - FILE* mDumpFile; + WavDumper mDumpFile; // Owning reference to a cubeb_stream. UniquePtr mCubebStream; diff --git a/dom/media/WavDumper.h b/dom/media/WavDumper.h new file mode 100644 index 000000000000..503ab5f55a2d --- /dev/null +++ b/dom/media/WavDumper.h @@ -0,0 +1,117 @@ +#if !defined(WavDumper_h_) +# define WavDumper_h_ +# include +# include +# include +# include +# include +# include +# include + +/** + * 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 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 header; + ByteWriter writer(header); + static const int CHANNEL_OFFSET = 22; + static const int SAMPLE_RATE_OFFSET = 24; + static const int BLOCK_ALIGN_OFFSET = 32; + + DebugOnly 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 + 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 buf; + ByteWriter writer(buf); + for (uint32_t i = 0; i < aSamples; ++i) { + DebugOnly 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_ diff --git a/dom/media/moz.build b/dom/media/moz.build index 5c076d7a6e02..a28be4ac61b3 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -166,6 +166,7 @@ EXPORTS += [ 'VideoSegment.h', 'VideoUtils.h', 'VorbisUtils.h', + 'WavDumper.h', 'XiphExtradata.h', ]