mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Back out 7 changesets (bug 1231793) for Windows crashes in mozilla::MediaDecoderReader::~MediaDecoderReader()
Backed out changeset 273cc690d7cf (bug 1231793) Backed out changeset d79218caeefc (bug 1231793) Backed out changeset 75c7942267f8 (bug 1231793) Backed out changeset 684e62a4d9f5 (bug 1231793) Backed out changeset a5a15d50a8e4 (bug 1231793) Backed out changeset 33d6f22d9b1f (bug 1231793) Backed out changeset cd4fc7f2c80a (bug 1231793)
This commit is contained in:
parent
c22f91b5be
commit
42b4db5e2f
@ -96,19 +96,6 @@ FloatToAudioSample<int16_t>(float aValue)
|
||||
return int16_t(clamped);
|
||||
}
|
||||
|
||||
template <typename T> T UInt8bitToAudioSample(uint8_t aValue);
|
||||
|
||||
template <> inline float
|
||||
UInt8bitToAudioSample<float>(uint8_t aValue)
|
||||
{
|
||||
return aValue * (static_cast<float>(2) / UINT8_MAX) - static_cast<float>(1);
|
||||
}
|
||||
template <> inline int16_t
|
||||
UInt8bitToAudioSample<int16_t>(uint8_t aValue)
|
||||
{
|
||||
return (int16_t(aValue) << 8) + aValue + INT16_MIN;
|
||||
}
|
||||
|
||||
template <typename T> T IntegerToAudioSample(int16_t aValue);
|
||||
|
||||
template <> inline float
|
||||
@ -122,19 +109,6 @@ IntegerToAudioSample<int16_t>(int16_t aValue)
|
||||
return aValue;
|
||||
}
|
||||
|
||||
template <typename T> T Int24bitToAudioSample(int32_t aValue);
|
||||
|
||||
template <> inline float
|
||||
Int24bitToAudioSample<float>(int32_t aValue)
|
||||
{
|
||||
return aValue / static_cast<float>(1 << 23);
|
||||
}
|
||||
template <> inline int16_t
|
||||
Int24bitToAudioSample<int16_t>(int32_t aValue)
|
||||
{
|
||||
return aValue / 256;
|
||||
}
|
||||
|
||||
template<typename SrcT, typename DstT>
|
||||
inline void
|
||||
ConvertAudioSample(SrcT aIn, DstT& aOut);
|
||||
|
@ -13,6 +13,9 @@
|
||||
#include "OggDecoder.h"
|
||||
#include "OggReader.h"
|
||||
|
||||
#include "WaveDecoder.h"
|
||||
#include "WaveReader.h"
|
||||
|
||||
#include "WebMDecoder.h"
|
||||
#include "WebMDemuxer.h"
|
||||
|
||||
@ -48,9 +51,6 @@
|
||||
#include "MP3Decoder.h"
|
||||
#include "MP3Demuxer.h"
|
||||
|
||||
#include "WaveDecoder.h"
|
||||
#include "WaveDemuxer.h"
|
||||
|
||||
#include "ADTSDecoder.h"
|
||||
#include "ADTSDemuxer.h"
|
||||
|
||||
@ -333,13 +333,6 @@ IsAACSupportedType(const nsACString& aType,
|
||||
return ADTSDecoder::CanHandleMediaType(aType, aCodecs);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsWAVSupportedType(const nsACString& aType,
|
||||
const nsAString& aCodecs = EmptyString())
|
||||
{
|
||||
return WaveDecoder::CanHandleMediaType(aType, aCodecs);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType)
|
||||
{
|
||||
@ -625,9 +618,6 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
|
||||
if (IsAACSupportedType(aType)) {
|
||||
decoderReader = new MediaFormatReader(aDecoder, new ADTSDemuxer(aDecoder->GetResource()));
|
||||
} else
|
||||
if (IsWAVSupportedType(aType)) {
|
||||
decoderReader = new MediaFormatReader(aDecoder, new WAVDemuxer(aDecoder->GetResource()));
|
||||
}
|
||||
#ifdef MOZ_RAW
|
||||
if (IsRawType(aType)) {
|
||||
decoderReader = new RawReader(aDecoder);
|
||||
@ -636,6 +626,9 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
|
||||
if (IsOggType(aType)) {
|
||||
decoderReader = new OggReader(aDecoder);
|
||||
} else
|
||||
if (IsWaveType(aType)) {
|
||||
decoderReader = new WaveReader(aDecoder);
|
||||
} else
|
||||
#ifdef MOZ_OMX_DECODER
|
||||
if (IsOmxSupportedType(aType)) {
|
||||
decoderReader = new MediaOmxReader(aDecoder);
|
||||
|
@ -341,7 +341,7 @@ MediaFormatReader::OnDemuxerInitDone(nsresult)
|
||||
mInitDone = true;
|
||||
RefPtr<MetadataHolder> metadata = new MetadataHolder();
|
||||
metadata->mInfo = mInfo;
|
||||
metadata->mTags = tags->Count() ? tags.release() : nullptr;
|
||||
metadata->mTags = nullptr;
|
||||
mMetadataPromise.Resolve(metadata, __func__);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include "OpusDecoder.h"
|
||||
#include "VorbisDecoder.h"
|
||||
#include "VPXDecoder.h"
|
||||
#include "WAVDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -17,8 +16,7 @@ AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
|
||||
{
|
||||
return VPXDecoder::IsVPX(aMimeType) ||
|
||||
OpusDataDecoder::IsOpus(aMimeType) ||
|
||||
VorbisDataDecoder::IsVorbis(aMimeType) ||
|
||||
WaveDataDecoder::IsWave(aMimeType);
|
||||
VorbisDataDecoder::IsVorbis(aMimeType);
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
@ -55,10 +53,6 @@ AgnosticDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
|
||||
m = new OpusDataDecoder(*aConfig.GetAsAudioInfo(),
|
||||
aAudioTaskQueue,
|
||||
aCallback);
|
||||
} else if (WaveDataDecoder::IsWave(aConfig.mMimeType)) {
|
||||
m = new WaveDataDecoder(*aConfig.GetAsAudioInfo(),
|
||||
aAudioTaskQueue,
|
||||
aCallback);
|
||||
}
|
||||
|
||||
return m.forget();
|
||||
|
@ -1,139 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WAVDecoder.h"
|
||||
#include "AudioSampleFormat.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
using mp4_demuxer::ByteReader;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
WaveDataDecoder::WaveDataDecoder(const AudioInfo& aConfig,
|
||||
FlushableTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
: mInfo(aConfig)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mCallback(aCallback)
|
||||
, mFrames(0)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
WaveDataDecoder::Shutdown()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::InitPromise>
|
||||
WaveDataDecoder::Init()
|
||||
{
|
||||
return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
WaveDataDecoder::Input(MediaRawData* aSample)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable(
|
||||
NS_NewRunnableMethodWithArg<RefPtr<MediaRawData>>(
|
||||
this, &WaveDataDecoder::Decode,
|
||||
RefPtr<MediaRawData>(aSample)));
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
WaveDataDecoder::Decode(MediaRawData* aSample)
|
||||
{
|
||||
if (!DoDecode(aSample)) {
|
||||
mCallback->Error();
|
||||
} else if (mTaskQueue->IsEmpty()) {
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WaveDataDecoder::DoDecode(MediaRawData* aSample)
|
||||
{
|
||||
size_t aLength = aSample->Size();
|
||||
ByteReader aReader = ByteReader(aSample->Data(), aLength);
|
||||
int64_t aOffset = aSample->mOffset;
|
||||
uint64_t aTstampUsecs = aSample->mTime;
|
||||
|
||||
int32_t frames = aLength * 8 / mInfo.mBitDepth / mInfo.mChannels;
|
||||
|
||||
auto buffer = MakeUnique<AudioDataValue[]>(frames * mInfo.mChannels);
|
||||
for (int i = 0; i < frames; ++i) {
|
||||
for (unsigned int j = 0; j < mInfo.mChannels; ++j) {
|
||||
if (mInfo.mBitDepth == 8) {
|
||||
uint8_t v = aReader.ReadU8();
|
||||
buffer[i * mInfo.mChannels + j] =
|
||||
UInt8bitToAudioSample<AudioDataValue>(v);
|
||||
} else if (mInfo.mBitDepth == 16) {
|
||||
int16_t v = aReader.ReadLE16();
|
||||
buffer[i * mInfo.mChannels + j] =
|
||||
IntegerToAudioSample<AudioDataValue>(v);
|
||||
} else if (mInfo.mBitDepth == 24) {
|
||||
int32_t v = aReader.ReadLE24();
|
||||
buffer[i * mInfo.mChannels + j] =
|
||||
Int24bitToAudioSample<AudioDataValue>(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aReader.DiscardRemaining();
|
||||
|
||||
int64_t duration = frames / mInfo.mRate;
|
||||
|
||||
mCallback->Output(new AudioData(aOffset,
|
||||
aTstampUsecs,
|
||||
duration,
|
||||
frames,
|
||||
Move(buffer),
|
||||
mInfo.mChannels,
|
||||
mInfo.mRate));
|
||||
mFrames += frames;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WaveDataDecoder::DoDrain()
|
||||
{
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
|
||||
nsresult
|
||||
WaveDataDecoder::Drain()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable(
|
||||
NS_NewRunnableMethod(this, &WaveDataDecoder::DoDrain));
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WaveDataDecoder::Flush()
|
||||
{
|
||||
mTaskQueue->Flush();
|
||||
mFrames = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
WaveDataDecoder::IsWave(const nsACString& aMimeType)
|
||||
{
|
||||
// Some WebAudio uses "audio/x-wav",
|
||||
// WAVdemuxer uses "audio/wave; codecs=aNum".
|
||||
return aMimeType.EqualsLiteral("audio/x-wav") ||
|
||||
aMimeType.EqualsLiteral("audio/wave; codecs=1") ||
|
||||
aMimeType.EqualsLiteral("audio/wave; codecs=65534");
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
#undef LOG
|
@ -1,48 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(WaveDecoder_h_)
|
||||
#define WaveDecoder_h_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mp4_demuxer/ByteReader.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class WaveDataDecoder : public MediaDataDecoder
|
||||
{
|
||||
public:
|
||||
WaveDataDecoder(const AudioInfo& aConfig,
|
||||
FlushableTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
|
||||
RefPtr<InitPromise> Init() override;
|
||||
nsresult Input(MediaRawData* aSample) override;
|
||||
nsresult Flush() override;
|
||||
nsresult Drain() override;
|
||||
nsresult Shutdown() override;
|
||||
const char* GetDescriptionName() const override
|
||||
{
|
||||
return "wave audio decoder";
|
||||
}
|
||||
|
||||
// Return true if mimetype is Wave
|
||||
static bool IsWave(const nsACString& aMimeType);
|
||||
|
||||
private:
|
||||
void Decode (MediaRawData* aSample);
|
||||
bool DoDecode (MediaRawData* aSample);
|
||||
void DoDrain ();
|
||||
|
||||
const AudioInfo& mInfo;
|
||||
RefPtr<FlushableTaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
|
||||
int64_t mFrames;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
#endif
|
@ -258,15 +258,6 @@ AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
|
||||
return true;
|
||||
}
|
||||
|
||||
// When checking "audio/x-wav", CreateDecoder can cause a JNI ERROR by
|
||||
// Accessing a stale local reference leading to a SIGSEGV crash.
|
||||
// To avoid this we check for wav types here.
|
||||
if (aMimeType.EqualsLiteral("audio/x-wav") ||
|
||||
aMimeType.EqualsLiteral("audio/wave; codecs=1") ||
|
||||
aMimeType.EqualsLiteral("audio/wave; codecs=65534")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return widget::HardwareCodecCapabilityUtils::FindDecoderCodecInfoForMimeType(
|
||||
nsCString(TranslateMimeType(aMimeType)));
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ UNIFIED_SOURCES += [
|
||||
'agnostic/OpusDecoder.cpp',
|
||||
'agnostic/VorbisDecoder.cpp',
|
||||
'agnostic/VPXDecoder.cpp',
|
||||
'agnostic/WAVDecoder.cpp',
|
||||
'PDMFactory.cpp',
|
||||
'PlatformDecoderModule.cpp',
|
||||
'wrappers/FuzzingWrapper.cpp',
|
||||
|
@ -681,7 +681,6 @@ var gMetadataTests = [
|
||||
{ name:"wave_metadata_bad_len.wav", tags: {
|
||||
name:"Track Title",
|
||||
artist:"Artist Name",
|
||||
comments:"Comments",
|
||||
}
|
||||
},
|
||||
{ name:"wave_metadata_bad_no_null.wav", tags: {
|
||||
|
@ -20,19 +20,24 @@ function startTest(test, token) {
|
||||
ok(el.error, "Element 'error' attr expected to have a value");
|
||||
ok(el.error instanceof MediaError, "Element 'error' attr expected to be MediaError");
|
||||
is(el.error.code, MediaError.MEDIA_ERR_DECODE, "Expected a decode error");
|
||||
is(el.networkState, HTMLMediaElement.NETWORK_EMPTY, "networkState should be EMPTY");
|
||||
el._sawError = true;
|
||||
}, false);
|
||||
|
||||
v.addEventListener("emptied", function (event) {
|
||||
var el = event.currentTarget;
|
||||
is(el.networkState, HTMLMediaElement.NETWORK_EMPTY, "networkState should be EMPTY");
|
||||
ok(el._sawError, "Expected error event");
|
||||
manager.finished(token);
|
||||
}, false);
|
||||
|
||||
v.addEventListener("loadeddata", function () {
|
||||
ok(false, "Unexpected loadeddata event");
|
||||
manager.finished(token);
|
||||
v.addEventListener("loadedmetadata", function () {
|
||||
ok(false, "Unexpected loadedmetadata event");
|
||||
}, false);
|
||||
|
||||
v.autoplay = true;
|
||||
v.addEventListener("ended", function () {
|
||||
ok(false, "Unexpected ended event");
|
||||
manager.finished(token);
|
||||
}, false);
|
||||
|
||||
v.src = test.name; // implicitly starts a load.
|
||||
|
@ -3,58 +3,15 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WaveDemuxer.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "WaveReader.h"
|
||||
#include "WaveDecoder.h"
|
||||
#include "MediaFormatReader.h"
|
||||
#include "PDMFactory.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MediaDecoder*
|
||||
WaveDecoder::Clone(MediaDecoderOwner* aOwner)
|
||||
MediaDecoderStateMachine* WaveDecoder::CreateStateMachine()
|
||||
{
|
||||
if (!IsEnabled())
|
||||
return nullptr;
|
||||
|
||||
return new WaveDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaDecoderStateMachine*
|
||||
WaveDecoder::CreateStateMachine()
|
||||
{
|
||||
if (Preferences::GetBool("media.wave.decoder.enabled")) {
|
||||
RefPtr<MediaDecoderReader> reader =
|
||||
new MediaFormatReader(this, new WAVDemuxer(GetResource()));
|
||||
return new MediaDecoderStateMachine(this, reader);
|
||||
} else {
|
||||
return new MediaDecoderStateMachine(this, new WaveReader(this));
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
WaveDecoder::IsEnabled()
|
||||
{
|
||||
PDMFactory::Init();
|
||||
RefPtr<PDMFactory> platform = new PDMFactory();
|
||||
return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/x-wav"));
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
WaveDecoder::CanHandleMediaType(const nsACString& aType,
|
||||
const nsAString& aCodecs)
|
||||
{
|
||||
if (aType.EqualsASCII("audio/wave") || aType.EqualsASCII("audio/x-wav") ||
|
||||
aType.EqualsASCII("audio/wav") || aType.EqualsASCII("audio/x-pn-wav")) {
|
||||
return IsEnabled() && (aCodecs.IsEmpty() || aCodecs.EqualsASCII("1"));
|
||||
}
|
||||
|
||||
return false;
|
||||
return new MediaDecoderStateMachine(this, new WaveReader(this));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -8,20 +8,30 @@
|
||||
|
||||
#include "MediaDecoder.h"
|
||||
|
||||
/**
|
||||
* The decoder implementation is currently limited to Linear PCM encoded
|
||||
* audio data with one or two channels of 8- or 16-bit samples at sample
|
||||
* rates from 100 Hz to 96 kHz. The number of channels is limited by what
|
||||
* the audio backend (via AudioStream) currently supports. The supported
|
||||
* sample rate is artificially limited to arbitrarily selected sane values.
|
||||
* Support for additional channels (and other new features) would
|
||||
* require extending WaveDecoder to support parsing the newer
|
||||
* WAVE_FORMAT_EXTENSIBLE chunk format.
|
||||
**/
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class WaveDecoder : public MediaDecoder {
|
||||
class WaveDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
// MediaDecoder interface.
|
||||
explicit WaveDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
|
||||
MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
|
||||
MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
|
||||
if (!IsWaveEnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new WaveDecoder(aOwner);
|
||||
}
|
||||
MediaDecoderStateMachine* CreateStateMachine() override;
|
||||
|
||||
// Returns true if the WAV backend is pref'ed on, and we're running on a
|
||||
// platform that is likely to have decoders for the format.
|
||||
static bool IsEnabled();
|
||||
static bool CanHandleMediaType(const nsACString& aType,
|
||||
const nsAString& aCodecs);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -1,889 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WaveDemuxer.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Endian.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "TimeUnits.h"
|
||||
#include "prenv.h"
|
||||
|
||||
using mozilla::media::TimeUnit;
|
||||
using mozilla::media::TimeIntervals;
|
||||
using mp4_demuxer::ByteReader;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// WAVDemuxer
|
||||
|
||||
WAVDemuxer::WAVDemuxer(MediaResource* aSource)
|
||||
: mSource(aSource)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
WAVDemuxer::InitInternal()
|
||||
{
|
||||
if (!mTrackDemuxer) {
|
||||
mTrackDemuxer = new WAVTrackDemuxer(mSource);
|
||||
}
|
||||
return mTrackDemuxer->Init();
|
||||
}
|
||||
|
||||
RefPtr<WAVDemuxer::InitPromise>
|
||||
WAVDemuxer::Init()
|
||||
{
|
||||
if (!InitInternal()) {
|
||||
return InitPromise::CreateAndReject(
|
||||
DemuxerFailureReason::DEMUXER_ERROR, __func__);
|
||||
}
|
||||
return InitPromise::CreateAndResolve(NS_OK, __func__);
|
||||
}
|
||||
|
||||
bool
|
||||
WAVDemuxer::HasTrackType(TrackInfo::TrackType aType) const
|
||||
{
|
||||
return aType == TrackInfo::kAudioTrack;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
WAVDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
|
||||
{
|
||||
return aType == TrackInfo::kAudioTrack ? 1u : 0u;
|
||||
}
|
||||
|
||||
already_AddRefed<MediaTrackDemuxer>
|
||||
WAVDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
|
||||
{
|
||||
if (!mTrackDemuxer) {
|
||||
return nullptr;
|
||||
}
|
||||
return RefPtr<WAVTrackDemuxer>(mTrackDemuxer).forget();
|
||||
}
|
||||
|
||||
bool
|
||||
WAVDemuxer::IsSeekable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// WAVTrackDemuxer
|
||||
|
||||
WAVTrackDemuxer::WAVTrackDemuxer(MediaResourceIndex aSource)
|
||||
: mSource(aSource)
|
||||
, mOffset(0)
|
||||
, mFirstChunkOffset(0)
|
||||
, mNumParsedChunks(0)
|
||||
, mChunkIndex(0)
|
||||
, mTotalChunkLen(0)
|
||||
, mSamplesPerChunk(0)
|
||||
, mSamplesPerSecond(0)
|
||||
, mChannels(0)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
bool
|
||||
WAVTrackDemuxer::Init()
|
||||
{
|
||||
Reset();
|
||||
FastSeek(TimeUnit());
|
||||
|
||||
if (!mInfo) {
|
||||
mInfo = MakeUnique<AudioInfo>();
|
||||
}
|
||||
|
||||
if (!RIFFParserInit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (!HeaderParserInit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t aChunkName = mHeaderParser.GiveHeader().ChunkName();
|
||||
uint32_t aChunkSize = mHeaderParser.GiveHeader().ChunkSize();
|
||||
|
||||
aChunkSize += aChunkSize % 2;
|
||||
|
||||
if (aChunkName == FRMT_CODE) {
|
||||
if (!FmtChunkParserInit()) {
|
||||
return false;
|
||||
}
|
||||
} else if (aChunkName == LIST_CODE) {
|
||||
mHeaderParser.Reset();
|
||||
uint32_t endOfListChunk = mOffset + aChunkSize;
|
||||
if (!ListChunkParserInit(aChunkSize)) {
|
||||
mOffset = endOfListChunk;
|
||||
}
|
||||
} else if (aChunkName == DATA_CODE) {
|
||||
mDataLength = aChunkSize;
|
||||
if (mFirstChunkOffset != mOffset) {
|
||||
mFirstChunkOffset = mOffset;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
mOffset += aChunkSize; // Skip other irrelevant chunks.
|
||||
}
|
||||
mHeaderParser.Reset();
|
||||
}
|
||||
|
||||
if (mDataLength > StreamLength() - mFirstChunkOffset) {
|
||||
mDataLength = StreamLength() - mFirstChunkOffset;
|
||||
}
|
||||
|
||||
mSamplesPerSecond = mFmtParser.FmtChunk().SampleRate();
|
||||
mChannels = mFmtParser.FmtChunk().Channels();
|
||||
mSampleFormat = mFmtParser.FmtChunk().SampleFormat();
|
||||
mSamplesPerChunk = DATA_CHUNK_SIZE * 8 / mChannels / mSampleFormat;
|
||||
|
||||
mInfo->mRate = mSamplesPerSecond;
|
||||
mInfo->mChannels = mChannels;
|
||||
mInfo->mBitDepth = mSampleFormat;
|
||||
mInfo->mProfile = mFmtParser.FmtChunk().WaveFormat() & 0x00FF;
|
||||
mInfo->mExtendedProfile = (mFmtParser.FmtChunk().WaveFormat() & 0xFF00) >> 8;
|
||||
mInfo->mMimeType = "audio/wave; codecs=";
|
||||
mInfo->mMimeType.AppendInt(mFmtParser.FmtChunk().WaveFormat());
|
||||
mInfo->mDuration = Duration().ToMicroseconds();
|
||||
|
||||
return !!(mInfo->mDuration);
|
||||
}
|
||||
|
||||
bool
|
||||
WAVTrackDemuxer::RIFFParserInit()
|
||||
{
|
||||
RefPtr<MediaRawData> riffHeader = GetFileHeader(FindRIFFHeader());
|
||||
if (!riffHeader) {
|
||||
return false;
|
||||
}
|
||||
ByteReader RIFFReader = ByteReader(riffHeader->Data(), 12);
|
||||
mRIFFParser.Parse(RIFFReader);
|
||||
return mRIFFParser.RiffHeader().IsValid(11);
|
||||
}
|
||||
|
||||
bool
|
||||
WAVTrackDemuxer::HeaderParserInit()
|
||||
{
|
||||
RefPtr<MediaRawData> header = GetFileHeader(FindChunkHeader());
|
||||
if (!header) {
|
||||
return false;
|
||||
}
|
||||
ByteReader HeaderReader = ByteReader(header->Data(), 8);
|
||||
mHeaderParser.Parse(HeaderReader);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WAVTrackDemuxer::FmtChunkParserInit()
|
||||
{
|
||||
RefPtr<MediaRawData> fmtChunk = GetFileHeader(FindFmtChunk());
|
||||
if (!fmtChunk) {
|
||||
return false;
|
||||
}
|
||||
ByteReader fmtReader = ByteReader(fmtChunk->Data(),
|
||||
mHeaderParser.GiveHeader().ChunkSize());
|
||||
mFmtParser.Parse(fmtReader);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WAVTrackDemuxer::ListChunkParserInit(uint32_t aChunkSize)
|
||||
{
|
||||
uint32_t bytesRead = 0;
|
||||
|
||||
RefPtr<MediaRawData> infoTag = GetFileHeader(FindInfoTag());
|
||||
if (!infoTag) {
|
||||
return false;
|
||||
}
|
||||
ByteReader infoTagReader = ByteReader(infoTag->Data(), 4);
|
||||
if (!infoTagReader.CanRead32() || infoTagReader.ReadU32() != INFO_CODE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bytesRead += 4;
|
||||
|
||||
while (bytesRead < aChunkSize) {
|
||||
if (!HeaderParserInit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bytesRead += 8;
|
||||
|
||||
uint32_t id = mHeaderParser.GiveHeader().ChunkName();
|
||||
uint32_t length = mHeaderParser.GiveHeader().ChunkSize();
|
||||
|
||||
// SubChunk Length Cannot Exceed List Chunk length.
|
||||
if (length > aChunkSize - bytesRead) {
|
||||
length = aChunkSize - bytesRead;
|
||||
}
|
||||
|
||||
MediaByteRange mRange = { mOffset, mOffset + length };
|
||||
RefPtr<MediaRawData> mChunkData = GetFileHeader(mRange);
|
||||
if (!mChunkData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* mRawData = reinterpret_cast<const char*>(mChunkData->Data());
|
||||
|
||||
nsCString val(mRawData, length);
|
||||
if (length > 0 && val[length - 1] == '\0') {
|
||||
val.SetLength(length - 1);
|
||||
}
|
||||
|
||||
if (length % 2) {
|
||||
mOffset += 1;
|
||||
length += length % 2;
|
||||
}
|
||||
|
||||
bytesRead += length;
|
||||
|
||||
if (!IsUTF8(val)) {
|
||||
mHeaderParser.Reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
case 0x49415254: // IART
|
||||
mInfo->mTags.AppendElement(MetadataTag(NS_LITERAL_CSTRING("artist"), val));
|
||||
break;
|
||||
case 0x49434d54: // ICMT
|
||||
mInfo->mTags.AppendElement(MetadataTag(NS_LITERAL_CSTRING("comments"), val));
|
||||
break;
|
||||
case 0x49474e52: // IGNR
|
||||
mInfo->mTags.AppendElement(MetadataTag(NS_LITERAL_CSTRING("genre"), val));
|
||||
break;
|
||||
case 0x494e414d: // INAM
|
||||
mInfo->mTags.AppendElement(MetadataTag(NS_LITERAL_CSTRING("name"), val));
|
||||
break;
|
||||
}
|
||||
|
||||
mHeaderParser.Reset();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
media::TimeUnit
|
||||
WAVTrackDemuxer::SeekPosition() const
|
||||
{
|
||||
TimeUnit pos = Duration(mChunkIndex);
|
||||
if (Duration() > TimeUnit()) {
|
||||
pos = std::min(Duration(), pos);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
RefPtr<MediaRawData>
|
||||
WAVTrackDemuxer::DemuxSample()
|
||||
{
|
||||
return GetNextChunk(FindNextChunk());
|
||||
}
|
||||
|
||||
UniquePtr<TrackInfo>
|
||||
WAVTrackDemuxer::GetInfo() const
|
||||
{
|
||||
return mInfo->Clone();
|
||||
}
|
||||
|
||||
RefPtr<WAVTrackDemuxer::SeekPromise>
|
||||
WAVTrackDemuxer::Seek(TimeUnit aTime)
|
||||
{
|
||||
FastSeek(aTime);
|
||||
const TimeUnit seekTime = ScanUntil(aTime);
|
||||
return SeekPromise::CreateAndResolve(seekTime, __func__);
|
||||
}
|
||||
|
||||
TimeUnit
|
||||
WAVTrackDemuxer::FastSeek(const TimeUnit& aTime)
|
||||
{
|
||||
if (aTime.ToMicroseconds()) {
|
||||
mChunkIndex = ChunkIndexFromTime(aTime);
|
||||
} else {
|
||||
mChunkIndex = 0;
|
||||
}
|
||||
|
||||
mOffset = OffsetFromChunkIndex(mChunkIndex);
|
||||
|
||||
if (mOffset > mFirstChunkOffset && StreamLength() > 0) {
|
||||
mOffset = std::min(StreamLength() - 1, mOffset);
|
||||
}
|
||||
|
||||
return Duration(mChunkIndex);
|
||||
}
|
||||
|
||||
TimeUnit
|
||||
WAVTrackDemuxer::ScanUntil(const TimeUnit& aTime)
|
||||
{
|
||||
if (!aTime.ToMicroseconds()) {
|
||||
return FastSeek(aTime);
|
||||
}
|
||||
|
||||
if (Duration(mChunkIndex) > aTime) {
|
||||
FastSeek(aTime);
|
||||
}
|
||||
|
||||
return SeekPosition();
|
||||
}
|
||||
|
||||
RefPtr<WAVTrackDemuxer::SamplesPromise>
|
||||
WAVTrackDemuxer::GetSamples(int32_t aNumSamples)
|
||||
{
|
||||
if (!aNumSamples) {
|
||||
return SamplesPromise::CreateAndReject(
|
||||
DemuxerFailureReason::DEMUXER_ERROR, __func__);
|
||||
}
|
||||
|
||||
RefPtr<SamplesHolder> datachunks = new SamplesHolder();
|
||||
|
||||
while (aNumSamples--) {
|
||||
RefPtr<MediaRawData> datachunk = GetNextChunk(FindNextChunk());
|
||||
if (!datachunk) {
|
||||
break;
|
||||
}
|
||||
datachunks->mSamples.AppendElement(datachunk);
|
||||
}
|
||||
|
||||
if (datachunks->mSamples.IsEmpty()) {
|
||||
return SamplesPromise::CreateAndReject(
|
||||
DemuxerFailureReason::END_OF_STREAM, __func__);
|
||||
}
|
||||
|
||||
return SamplesPromise::CreateAndResolve(datachunks, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
WAVTrackDemuxer::Reset()
|
||||
{
|
||||
FastSeek(TimeUnit());
|
||||
mParser.Reset();
|
||||
mHeaderParser.Reset();
|
||||
mRIFFParser.Reset();
|
||||
mFmtParser.Reset();
|
||||
}
|
||||
|
||||
RefPtr<WAVTrackDemuxer::SkipAccessPointPromise>
|
||||
WAVTrackDemuxer::SkipToNextRandomAccessPoint(TimeUnit aTimeThreshold)
|
||||
{
|
||||
return SkipAccessPointPromise::CreateAndReject(
|
||||
SkipFailureHolder(DemuxerFailureReason::DEMUXER_ERROR, 0), __func__);
|
||||
}
|
||||
|
||||
int64_t
|
||||
WAVTrackDemuxer::GetResourceOffset() const
|
||||
{
|
||||
return mOffset;
|
||||
}
|
||||
|
||||
TimeIntervals
|
||||
WAVTrackDemuxer::GetBuffered()
|
||||
{
|
||||
TimeUnit duration = Duration();
|
||||
|
||||
if (duration <= TimeUnit()) {
|
||||
return TimeIntervals();
|
||||
}
|
||||
|
||||
AutoPinned<MediaResource> stream(mSource.GetResource());
|
||||
return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds());
|
||||
}
|
||||
|
||||
int64_t
|
||||
WAVTrackDemuxer::StreamLength() const
|
||||
{
|
||||
return mSource.GetLength();
|
||||
}
|
||||
|
||||
TimeUnit
|
||||
WAVTrackDemuxer::Duration() const
|
||||
{
|
||||
if (!mDataLength) {
|
||||
return TimeUnit();
|
||||
}
|
||||
|
||||
int64_t numSamples = mDataLength * 8 / mChannels / mSampleFormat;
|
||||
|
||||
int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
|
||||
|
||||
if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
|
||||
numUSeconds++;
|
||||
}
|
||||
|
||||
return TimeUnit::FromMicroseconds(numUSeconds);
|
||||
}
|
||||
|
||||
TimeUnit
|
||||
WAVTrackDemuxer::Duration(int64_t aNumDataChunks) const
|
||||
{
|
||||
if (!mSamplesPerSecond) {
|
||||
return TimeUnit();
|
||||
}
|
||||
const double usPerDataChunk = USECS_PER_S * mSamplesPerChunk /
|
||||
mSamplesPerSecond;
|
||||
return TimeUnit::FromMicroseconds(aNumDataChunks * usPerDataChunk);
|
||||
}
|
||||
|
||||
TimeUnit
|
||||
WAVTrackDemuxer::DurationFromBytes(uint32_t aNumBytes) const
|
||||
{
|
||||
if (!mSamplesPerSecond) {
|
||||
return TimeUnit();
|
||||
}
|
||||
|
||||
int64_t numSamples = aNumBytes * 8 / mChannels / mSampleFormat;
|
||||
|
||||
int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
|
||||
|
||||
if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
|
||||
numUSeconds++;
|
||||
}
|
||||
|
||||
return TimeUnit::FromMicroseconds(numUSeconds);
|
||||
}
|
||||
|
||||
MediaByteRange
|
||||
WAVTrackDemuxer::FindNextChunk()
|
||||
{
|
||||
if (mOffset + DATA_CHUNK_SIZE < mFirstChunkOffset + mDataLength) {
|
||||
return { mOffset, mOffset + DATA_CHUNK_SIZE };
|
||||
} else {
|
||||
return { mOffset, mFirstChunkOffset + mDataLength };
|
||||
}
|
||||
}
|
||||
|
||||
MediaByteRange
|
||||
WAVTrackDemuxer::FindChunkHeader()
|
||||
{
|
||||
return { mOffset, mOffset + CHUNK_HEAD_SIZE };
|
||||
}
|
||||
|
||||
MediaByteRange
|
||||
WAVTrackDemuxer::FindRIFFHeader()
|
||||
{
|
||||
return { mOffset, mOffset + RIFF_CHUNK_SIZE };
|
||||
}
|
||||
|
||||
MediaByteRange
|
||||
WAVTrackDemuxer::FindFmtChunk()
|
||||
{
|
||||
return { mOffset, mOffset + mHeaderParser.GiveHeader().ChunkSize() };
|
||||
}
|
||||
|
||||
MediaByteRange
|
||||
WAVTrackDemuxer::FindListChunk()
|
||||
{
|
||||
return { mOffset, mOffset + mHeaderParser.GiveHeader().ChunkSize() };
|
||||
}
|
||||
|
||||
MediaByteRange
|
||||
WAVTrackDemuxer::FindInfoTag()
|
||||
{
|
||||
return { mOffset, mOffset + 4 };
|
||||
}
|
||||
|
||||
bool
|
||||
WAVTrackDemuxer::SkipNextChunk(const MediaByteRange& aRange)
|
||||
{
|
||||
UpdateState(aRange);
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<MediaRawData>
|
||||
WAVTrackDemuxer::GetNextChunk(const MediaByteRange& aRange)
|
||||
{
|
||||
if (!aRange.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<MediaRawData> datachunk = new MediaRawData();
|
||||
datachunk->mOffset = aRange.mStart;
|
||||
|
||||
nsAutoPtr<MediaRawDataWriter> chunkWriter(datachunk->CreateWriter());
|
||||
if (!chunkWriter->SetSize(aRange.Length())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint32_t read = Read(chunkWriter->Data(),
|
||||
datachunk->mOffset,
|
||||
datachunk->Size());
|
||||
|
||||
if (read != aRange.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UpdateState(aRange);
|
||||
++mNumParsedChunks;
|
||||
++mChunkIndex;
|
||||
|
||||
datachunk->mTime = Duration(mChunkIndex - 1).ToMicroseconds();
|
||||
|
||||
if (static_cast<uint32_t>(mChunkIndex) * DATA_CHUNK_SIZE < mDataLength) {
|
||||
datachunk->mDuration = Duration(1).ToMicroseconds();
|
||||
} else {
|
||||
uint32_t mBytesRemaining =
|
||||
mDataLength - mChunkIndex * DATA_CHUNK_SIZE;
|
||||
datachunk->mDuration = DurationFromBytes(mBytesRemaining).ToMicroseconds();
|
||||
}
|
||||
datachunk->mTimecode = datachunk->mTime;
|
||||
datachunk->mKeyframe = true;
|
||||
|
||||
MOZ_ASSERT(datachunk->mTime >= 0);
|
||||
MOZ_ASSERT(datachunk->mDuration >= 0);
|
||||
|
||||
return datachunk.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<MediaRawData>
|
||||
WAVTrackDemuxer::GetFileHeader(const MediaByteRange& aRange)
|
||||
{
|
||||
if (!aRange.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<MediaRawData> fileHeader = new MediaRawData();
|
||||
fileHeader->mOffset = aRange.mStart;
|
||||
|
||||
nsAutoPtr<MediaRawDataWriter> headerWriter(fileHeader->CreateWriter());
|
||||
if (!headerWriter->SetSize(aRange.Length())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint32_t read = Read(headerWriter->Data(),
|
||||
fileHeader->mOffset,
|
||||
fileHeader->Size());
|
||||
|
||||
if (read != aRange.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UpdateState(aRange);
|
||||
|
||||
return fileHeader.forget();
|
||||
}
|
||||
|
||||
int64_t
|
||||
WAVTrackDemuxer::OffsetFromChunkIndex(int64_t aChunkIndex) const
|
||||
{
|
||||
return mFirstChunkOffset + aChunkIndex * DATA_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
int64_t
|
||||
WAVTrackDemuxer::ChunkIndexFromOffset(int64_t aOffset) const
|
||||
{
|
||||
int64_t chunkIndex = (aOffset - mFirstChunkOffset) / DATA_CHUNK_SIZE;
|
||||
return std::max<int64_t>(0, chunkIndex);
|
||||
}
|
||||
|
||||
int64_t
|
||||
WAVTrackDemuxer::ChunkIndexFromTime(const media::TimeUnit& aTime) const
|
||||
{
|
||||
int64_t chunkIndex =
|
||||
(aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerChunk) - 1;
|
||||
return chunkIndex;
|
||||
}
|
||||
|
||||
void
|
||||
WAVTrackDemuxer::UpdateState(const MediaByteRange& aRange)
|
||||
{
|
||||
// Full chunk parsed, move offset to its end.
|
||||
mOffset = aRange.mEnd;
|
||||
mTotalChunkLen += aRange.Length();
|
||||
}
|
||||
|
||||
int32_t
|
||||
WAVTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize)
|
||||
{
|
||||
const int64_t streamLen = StreamLength();
|
||||
if (mInfo && streamLen > 0) {
|
||||
aSize = std::min<int64_t>(aSize, streamLen - aOffset);
|
||||
}
|
||||
uint32_t read = 0;
|
||||
const nsresult rv = mSource.ReadAt(aOffset,
|
||||
reinterpret_cast<char*>(aBuffer),
|
||||
static_cast<uint32_t>(aSize),
|
||||
&read);
|
||||
NS_ENSURE_SUCCESS(rv, 0);
|
||||
return static_cast<int32_t>(read);
|
||||
}
|
||||
|
||||
// RIFFParser
|
||||
|
||||
uint32_t
|
||||
RIFFParser::Parse(ByteReader& aReader)
|
||||
{
|
||||
MOZ_ASSERT(&aReader);
|
||||
|
||||
while (aReader.CanRead8() && !mRiffHeader.ParseNext(aReader.ReadU8())) { }
|
||||
|
||||
if (mRiffHeader.IsValid()) {
|
||||
return RIFF_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
RIFFParser::Reset()
|
||||
{
|
||||
mRiffHeader.Reset();
|
||||
}
|
||||
|
||||
const RIFFParser::RIFFHeader&
|
||||
RIFFParser::RiffHeader() const
|
||||
{
|
||||
return mRiffHeader;
|
||||
}
|
||||
|
||||
// RIFFParser::RIFFHeader
|
||||
|
||||
RIFFParser::RIFFHeader::RIFFHeader()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void
|
||||
RIFFParser::RIFFHeader::Reset()
|
||||
{
|
||||
mPos = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
RIFFParser::RIFFHeader::ParseNext(uint8_t c)
|
||||
{
|
||||
if (!Update(c)) {
|
||||
Reset();
|
||||
if (!Update(c)) {
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
return IsValid();
|
||||
}
|
||||
|
||||
bool
|
||||
RIFFParser::RIFFHeader::IsValid(int aPos) const
|
||||
{
|
||||
if (aPos > -1 && aPos < 4) {
|
||||
return RIFF[aPos] == mRaw[aPos];
|
||||
} else if (aPos > 7 && aPos < 12) {
|
||||
return WAVE[aPos - 8] == mRaw[aPos];
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RIFFParser::RIFFHeader::IsValid() const
|
||||
{
|
||||
return mPos >= RIFF_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
bool
|
||||
RIFFParser::RIFFHeader::Update(uint8_t c)
|
||||
{
|
||||
if (mPos < RIFF_CHUNK_SIZE) {
|
||||
mRaw[mPos] = c;
|
||||
}
|
||||
return IsValid(mPos++);
|
||||
}
|
||||
|
||||
// HeaderParser
|
||||
|
||||
uint32_t
|
||||
HeaderParser::Parse(ByteReader& aReader)
|
||||
{
|
||||
while (aReader.CanRead8() && !mHeader.ParseNext(aReader.ReadU8())) { }
|
||||
|
||||
if (mHeader.IsValid()) {
|
||||
return CHUNK_HEAD_SIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
HeaderParser::Reset()
|
||||
{
|
||||
mHeader.Reset();
|
||||
}
|
||||
|
||||
const HeaderParser::ChunkHeader&
|
||||
HeaderParser::GiveHeader() const
|
||||
{
|
||||
return mHeader;
|
||||
}
|
||||
|
||||
// HeaderParser::ChunkHeader
|
||||
|
||||
HeaderParser::ChunkHeader::ChunkHeader()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void
|
||||
HeaderParser::ChunkHeader::Reset()
|
||||
{
|
||||
mPos = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
HeaderParser::ChunkHeader::ParseNext(uint8_t c)
|
||||
{
|
||||
Update(c);
|
||||
return IsValid();
|
||||
}
|
||||
|
||||
bool
|
||||
HeaderParser::ChunkHeader::IsValid() const
|
||||
{
|
||||
return mPos >= CHUNK_HEAD_SIZE;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
HeaderParser::ChunkHeader::ChunkName() const
|
||||
{
|
||||
return ((mRaw[0] << 24) | (mRaw[1] << 16) |
|
||||
(mRaw[2] << 8 ) | (mRaw[3]));
|
||||
}
|
||||
|
||||
uint32_t
|
||||
HeaderParser::ChunkHeader::ChunkSize() const
|
||||
{
|
||||
return ((mRaw[7] << 24) | (mRaw[6] << 16) |
|
||||
(mRaw[5] << 8 ) | (mRaw[4]));
|
||||
}
|
||||
|
||||
void
|
||||
HeaderParser::ChunkHeader::Update(uint8_t c)
|
||||
{
|
||||
if (mPos < CHUNK_HEAD_SIZE) {
|
||||
mRaw[mPos++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
// FormatParser
|
||||
|
||||
uint32_t
|
||||
FormatParser::Parse(ByteReader& aReader)
|
||||
{
|
||||
MOZ_ASSERT(&aReader);
|
||||
|
||||
while (aReader.CanRead8() && !mFmtChunk.ParseNext(aReader.ReadU8())) { }
|
||||
|
||||
if (mFmtChunk.IsValid()) {
|
||||
return FMT_CHUNK_MIN_SIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
FormatParser::Reset()
|
||||
{
|
||||
mFmtChunk.Reset();
|
||||
}
|
||||
|
||||
const FormatParser::FormatChunk&
|
||||
FormatParser::FmtChunk() const
|
||||
{
|
||||
return mFmtChunk;
|
||||
}
|
||||
|
||||
// FormatParser::FormatChunk
|
||||
|
||||
FormatParser::FormatChunk::FormatChunk()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void
|
||||
FormatParser::FormatChunk::Reset()
|
||||
{
|
||||
mPos = 0;
|
||||
}
|
||||
|
||||
uint16_t
|
||||
FormatParser::FormatChunk::WaveFormat() const
|
||||
{
|
||||
return (mRaw[1] << 8) | (mRaw[0]);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
FormatParser::FormatChunk::Channels() const
|
||||
{
|
||||
return (mRaw[3] << 8) | (mRaw[2]);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
FormatParser::FormatChunk::SampleRate() const
|
||||
{
|
||||
return (mRaw[7] << 24) | (mRaw[6] << 16) |
|
||||
(mRaw[5] << 8 ) | (mRaw[4]);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
FormatParser::FormatChunk::FrameSize() const
|
||||
{
|
||||
return (mRaw[13] << 8) | (mRaw[12]);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
FormatParser::FormatChunk::SampleFormat() const
|
||||
{
|
||||
return (mRaw[15] << 8) | (mRaw[14]);
|
||||
}
|
||||
|
||||
bool
|
||||
FormatParser::FormatChunk::ParseNext(uint8_t c)
|
||||
{
|
||||
Update(c);
|
||||
return IsValid();
|
||||
}
|
||||
|
||||
bool
|
||||
FormatParser::FormatChunk::IsValid() const
|
||||
{
|
||||
return (FrameSize() == SampleRate() * Channels() / 8) &&
|
||||
(mPos >= FMT_CHUNK_MIN_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
FormatParser::FormatChunk::Update(uint8_t c)
|
||||
{
|
||||
if (mPos < FMT_CHUNK_MIN_SIZE) {
|
||||
mRaw[mPos++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
// DataParser
|
||||
|
||||
DataParser::DataParser()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
DataParser::Reset()
|
||||
{
|
||||
mChunk.Reset();
|
||||
}
|
||||
|
||||
const DataParser::DataChunk&
|
||||
DataParser::CurrentChunk() const
|
||||
{
|
||||
return mChunk;
|
||||
}
|
||||
|
||||
// DataParser::DataChunk
|
||||
|
||||
void
|
||||
DataParser::DataChunk::Reset()
|
||||
{
|
||||
mPos = 0;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
@ -1,262 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* Licence, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef WAV_DEMUXER_H_
|
||||
#define WAV_DEMUXER_H_
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "MediaDataDemuxer.h"
|
||||
#include "MediaResource.h"
|
||||
#include "mp4_demuxer/ByteReader.h"
|
||||
|
||||
using mp4_demuxer::ByteReader;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static const uint32_t FRMT_CODE = 0x666d7420;
|
||||
static const uint32_t DATA_CODE = 0x64617461;
|
||||
static const uint32_t LIST_CODE = 0x4c495354;
|
||||
static const uint32_t INFO_CODE = 0x494e464f;
|
||||
|
||||
static const uint8_t RIFF[4] = {'R', 'I', 'F', 'F'};
|
||||
static const uint8_t WAVE[4] = {'W', 'A', 'V', 'E'};
|
||||
|
||||
static const uint16_t RIFF_CHUNK_SIZE = 12;
|
||||
static const uint16_t CHUNK_HEAD_SIZE = 8;
|
||||
static const uint16_t FMT_CHUNK_MIN_SIZE = 16;
|
||||
static const uint16_t DATA_CHUNK_SIZE = 768;
|
||||
|
||||
class WAVTrackDemuxer;
|
||||
|
||||
class WAVDemuxer : public MediaDataDemuxer {
|
||||
public:
|
||||
// MediaDataDemuxer interface.
|
||||
explicit WAVDemuxer(MediaResource* aSource);
|
||||
RefPtr<InitPromise> Init() override;
|
||||
bool HasTrackType(TrackInfo::TrackType aType) const override;
|
||||
uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
|
||||
already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
|
||||
TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
|
||||
bool IsSeekable() const override;
|
||||
|
||||
private:
|
||||
// Synchronous Initialization.
|
||||
bool InitInternal();
|
||||
|
||||
MediaResourceIndex mSource;
|
||||
RefPtr<WAVTrackDemuxer> mTrackDemuxer;
|
||||
};
|
||||
|
||||
class RIFFParser {
|
||||
private:
|
||||
class RIFFHeader;
|
||||
public:
|
||||
const RIFFHeader& RiffHeader() const;
|
||||
|
||||
uint32_t Parse(ByteReader& aReader);
|
||||
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
class RIFFHeader {
|
||||
public:
|
||||
RIFFHeader();
|
||||
void Reset();
|
||||
|
||||
bool IsValid() const;
|
||||
bool IsValid(int aPos) const;
|
||||
|
||||
bool ParseNext(uint8_t c);
|
||||
|
||||
private:
|
||||
bool Update(uint8_t c);
|
||||
|
||||
uint8_t mRaw[RIFF_CHUNK_SIZE];
|
||||
|
||||
int mPos;
|
||||
};
|
||||
|
||||
RIFFHeader mRiffHeader;
|
||||
};
|
||||
|
||||
class HeaderParser {
|
||||
private:
|
||||
class ChunkHeader;
|
||||
public:
|
||||
const ChunkHeader& GiveHeader() const;
|
||||
|
||||
uint32_t Parse(ByteReader& aReader);
|
||||
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
class ChunkHeader {
|
||||
public:
|
||||
ChunkHeader();
|
||||
void Reset();
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
uint32_t ChunkName() const;
|
||||
uint32_t ChunkSize() const;
|
||||
|
||||
bool ParseNext(uint8_t c);
|
||||
|
||||
private:
|
||||
void Update(uint8_t c);
|
||||
|
||||
uint8_t mRaw[CHUNK_HEAD_SIZE];
|
||||
|
||||
int mPos;
|
||||
};
|
||||
|
||||
ChunkHeader mHeader;
|
||||
};
|
||||
|
||||
class FormatParser {
|
||||
private:
|
||||
class FormatChunk;
|
||||
public:
|
||||
const FormatChunk& FmtChunk() const;
|
||||
|
||||
uint32_t Parse(ByteReader& aReader);
|
||||
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
class FormatChunk {
|
||||
public:
|
||||
FormatChunk();
|
||||
void Reset();
|
||||
|
||||
uint16_t WaveFormat() const;
|
||||
uint16_t Channels() const;
|
||||
uint32_t SampleRate() const;
|
||||
uint16_t FrameSize() const;
|
||||
uint16_t SampleFormat() const;
|
||||
|
||||
bool IsValid() const;
|
||||
bool ParseNext(uint8_t c);
|
||||
|
||||
private:
|
||||
void Update(uint8_t c);
|
||||
|
||||
uint8_t mRaw[FMT_CHUNK_MIN_SIZE];
|
||||
|
||||
int mPos;
|
||||
};
|
||||
|
||||
FormatChunk mFmtChunk;
|
||||
};
|
||||
|
||||
class DataParser {
|
||||
private:
|
||||
class DataChunk;
|
||||
public:
|
||||
DataParser();
|
||||
|
||||
const DataChunk& CurrentChunk() const;
|
||||
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
class DataChunk {
|
||||
public:
|
||||
void Reset();
|
||||
private:
|
||||
int mPos; // To Check Alignment
|
||||
};
|
||||
|
||||
DataChunk mChunk;
|
||||
};
|
||||
|
||||
class WAVTrackDemuxer : public MediaTrackDemuxer {
|
||||
public:
|
||||
explicit WAVTrackDemuxer(MediaResourceIndex aSource);
|
||||
|
||||
bool Init();
|
||||
|
||||
int64_t StreamLength() const;
|
||||
|
||||
media::TimeUnit Duration() const;
|
||||
media::TimeUnit Duration(int64_t aNumDataChunks) const;
|
||||
media::TimeUnit DurationFromBytes(uint32_t aNumBytes) const;
|
||||
|
||||
media::TimeUnit SeekPosition() const;
|
||||
|
||||
RefPtr<MediaRawData> DemuxSample();
|
||||
|
||||
// MediaTrackDemuxer interface.
|
||||
UniquePtr<TrackInfo> GetInfo() const override;
|
||||
RefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
|
||||
RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples) override;
|
||||
void Reset() override;
|
||||
RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(
|
||||
media::TimeUnit aTimeThreshold) override;
|
||||
int64_t GetResourceOffset() const override;
|
||||
media::TimeIntervals GetBuffered() override;
|
||||
|
||||
private:
|
||||
~WAVTrackDemuxer() {}
|
||||
|
||||
media::TimeUnit FastSeek(const media::TimeUnit& aTime);
|
||||
media::TimeUnit ScanUntil(const media::TimeUnit& aTime);
|
||||
|
||||
MediaByteRange FindNextChunk();
|
||||
|
||||
MediaByteRange FindChunkHeader();
|
||||
MediaByteRange FindRIFFHeader();
|
||||
MediaByteRange FindFmtChunk();
|
||||
MediaByteRange FindListChunk();
|
||||
MediaByteRange FindInfoTag();
|
||||
|
||||
bool RIFFParserInit();
|
||||
bool HeaderParserInit();
|
||||
bool FmtChunkParserInit();
|
||||
bool ListChunkParserInit(uint32_t aChunkSize);
|
||||
|
||||
bool SkipNextChunk(const MediaByteRange& aRange);
|
||||
|
||||
already_AddRefed<MediaRawData> GetNextChunk(const MediaByteRange& aRange);
|
||||
already_AddRefed<MediaRawData> GetFileHeader(const MediaByteRange& aRange);
|
||||
|
||||
void UpdateState(const MediaByteRange& aRange);
|
||||
|
||||
int64_t OffsetFromChunkIndex(int64_t aChunkIndex) const;
|
||||
int64_t ChunkIndexFromOffset(int64_t aOffet) const;
|
||||
int64_t ChunkIndexFromTime(const media::TimeUnit& aTime) const;
|
||||
|
||||
int32_t Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize);
|
||||
|
||||
MediaResourceIndex mSource;
|
||||
|
||||
DataParser mParser;
|
||||
RIFFParser mRIFFParser;
|
||||
HeaderParser mHeaderParser;
|
||||
|
||||
FormatParser mFmtParser;
|
||||
// ListChunkParser mListChunkParser;
|
||||
|
||||
int64_t mOffset;
|
||||
int64_t mFirstChunkOffset;
|
||||
|
||||
uint32_t mNumParsedChunks;
|
||||
int32_t mChunkIndex;
|
||||
|
||||
uint32_t mDataLength;
|
||||
uint64_t mTotalChunkLen;
|
||||
|
||||
int32_t mSamplesPerChunk;
|
||||
int32_t mSamplesPerSecond;
|
||||
|
||||
int32_t mChannels;
|
||||
int32_t mSampleFormat;
|
||||
|
||||
UniquePtr<AudioInfo> mInfo;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -6,13 +6,11 @@
|
||||
|
||||
EXPORTS += [
|
||||
'WaveDecoder.h',
|
||||
'WaveDemuxer.h',
|
||||
'WaveReader.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'WaveDecoder.cpp',
|
||||
'WaveDemuxer.cpp',
|
||||
'WaveReader.cpp',
|
||||
]
|
||||
|
||||
|
@ -23,11 +23,11 @@ function finishTest(a) {
|
||||
function decodeUsingAudioElement() {
|
||||
var a = new Audio();
|
||||
a.addEventListener("error", function() {
|
||||
ok(false, "Error loading metadata");
|
||||
ok(true, "We should not be able to decode this file using an HTMLAudioElement");
|
||||
finishTest(a);
|
||||
});
|
||||
a.addEventListener("loadedmetadata", function() {
|
||||
ok(true, "Metadata Loaded");
|
||||
ok(false, "We should not be able to decode this file using an HTMLMediaElement.");
|
||||
finishTest(a);
|
||||
});
|
||||
|
||||
|
@ -87,16 +87,6 @@ public:
|
||||
return mozilla::BigEndian::readUint16(ptr);
|
||||
}
|
||||
|
||||
int16_t ReadLE16()
|
||||
{
|
||||
auto ptr = Read(2);
|
||||
if (!ptr) {
|
||||
MOZ_ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
return mozilla::LittleEndian::readInt16(ptr);
|
||||
}
|
||||
|
||||
uint32_t ReadU24()
|
||||
{
|
||||
auto ptr = Read(3);
|
||||
@ -112,20 +102,6 @@ public:
|
||||
return (uint32_t)ReadU24();
|
||||
}
|
||||
|
||||
int32_t ReadLE24()
|
||||
{
|
||||
auto ptr = Read(3);
|
||||
if (!ptr) {
|
||||
MOZ_ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
int32_t result = int32_t(ptr[2] << 16 | ptr[1] << 8 | ptr[0]);
|
||||
if (result & 0x00800000u) {
|
||||
result -= 0x1000000;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CanRead32() { return mRemaining >= 4; }
|
||||
|
||||
uint32_t ReadU32()
|
||||
|
@ -349,7 +349,6 @@ pref("media.raw.enabled", true);
|
||||
pref("media.ogg.enabled", true);
|
||||
pref("media.opus.enabled", true);
|
||||
pref("media.wave.enabled", true);
|
||||
pref("media.wave.decoder.enabled", true);
|
||||
pref("media.webm.enabled", true);
|
||||
#if defined(MOZ_FMP4) && defined(MOZ_WMF)
|
||||
pref("media.webm.intel_decoder.enabled", false);
|
||||
|
Loading…
Reference in New Issue
Block a user