From 56c26d86748eadb1292b141993a48bf035081346 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sun, 18 Nov 2018 13:10:11 -0600 Subject: Add AV1 support to MP4Decoder. --- dom/media/fmp4/MP4Decoder.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'dom/media') diff --git a/dom/media/fmp4/MP4Decoder.cpp b/dom/media/fmp4/MP4Decoder.cpp index fdd6f2c7e..25dd53f94 100644 --- a/dom/media/fmp4/MP4Decoder.cpp +++ b/dom/media/fmp4/MP4Decoder.cpp @@ -139,6 +139,14 @@ MP4Decoder::CanHandleMediaType(const MediaContentType& aType, NS_LITERAL_CSTRING("audio/flac"), aType)); continue; } +#ifdef MOZ_AV1 + if (IsAV1CodecString(codec)) { + trackInfos.AppendElement( + CreateTrackInfoWithMIMETypeAndContentTypeExtraParameters( + NS_LITERAL_CSTRING("video/av1"), aType)); + continue; + } +#endif // Note: Only accept H.264 in a video content type, not in an audio // content type. if (IsWhitelistedH264Codec(codec) && isMP4Video) { -- cgit v1.2.3 From 011ec4885e7fdef229309d966c7fa0b98bcdfed8 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sun, 18 Nov 2018 21:59:15 -0600 Subject: Cleanup some comments in our media code. --- dom/media/directshow/DirectShowReader.h | 2 +- dom/media/gmp/GMPParent.cpp | 3 +-- dom/media/mediasource/MediaSource.cpp | 2 -- dom/media/platforms/wmf/WMFDecoderModule.h | 6 ++---- 4 files changed, 4 insertions(+), 9 deletions(-) (limited to 'dom/media') diff --git a/dom/media/directshow/DirectShowReader.h b/dom/media/directshow/DirectShowReader.h index e1326d416..881b27c28 100644 --- a/dom/media/directshow/DirectShowReader.h +++ b/dom/media/directshow/DirectShowReader.h @@ -14,7 +14,7 @@ #include "MP3FrameParser.h" // Add the graph to the Running Object Table so that we can connect -// to this graph with GraphEdit/GraphStudio. Note: on Vista and up you must +// to this graph with GraphEdit/GraphStudio. Note: you must // also regsvr32 proppage.dll from the Windows SDK. // See: http://msdn.microsoft.com/en-us/library/ms787252(VS.85).aspx // #define DIRECTSHOW_REGISTER_GRAPH diff --git a/dom/media/gmp/GMPParent.cpp b/dom/media/gmp/GMPParent.cpp index 40c3e5141..418f14736 100644 --- a/dom/media/gmp/GMPParent.cpp +++ b/dom/media/gmp/GMPParent.cpp @@ -516,8 +516,7 @@ GMPCapability::Supports(const nsTArray& aCapabilities, #ifdef XP_WIN // Clearkey on Windows advertises that it can decode in its GMP info // file, but uses Windows Media Foundation to decode. That's not present - // on Windows XP, and on some Vista, Windows N, and KN variants without - // certain services packs. + // on Windows N and KN variants without certain services packs. if (tag.Equals(kEMEKeySystemClearkey)) { if (capabilities.mAPIName.EqualsLiteral(GMP_API_VIDEO_DECODER)) { if (!WMFDecoderModule::HasH264()) { diff --git a/dom/media/mediasource/MediaSource.cpp b/dom/media/mediasource/MediaSource.cpp index af541bbbb..152c0085a 100644 --- a/dom/media/mediasource/MediaSource.cpp +++ b/dom/media/mediasource/MediaSource.cpp @@ -62,8 +62,6 @@ namespace mozilla { // Returns true if we should enable MSE webm regardless of preferences. // 1. If MP4/H264 isn't supported: -// * Windows XP -// * Windows Vista and Server 2008 without the optional "Platform Update Supplement" // * N/KN editions (Europe and Korea) of Windows 7/8/8.1/10 without the // optional "Windows Media Feature Pack" // 2. If H264 hardware acceleration is not available. diff --git a/dom/media/platforms/wmf/WMFDecoderModule.h b/dom/media/platforms/wmf/WMFDecoderModule.h index cd7b8c660..6582f8056 100644 --- a/dom/media/platforms/wmf/WMFDecoderModule.h +++ b/dom/media/platforms/wmf/WMFDecoderModule.h @@ -40,10 +40,8 @@ public: static int GetNumDecoderThreads(); // Accessors that report whether we have the required MFTs available - // on the system to play various codecs. Windows Vista doesn't have the - // H.264/AAC decoders if the "Platform Update Supplement for Windows Vista" - // is not installed, and Window N and KN variants also require a "Media - // Feature Pack" to be installed. Windows XP doesn't have WMF. + // on the system to play various codecs. Windows N and KN variants + // require a "Media Feature Pack" to be installed. static bool HasAAC(); static bool HasH264(); -- cgit v1.2.3 From 513bf1b6832ed056241bbc376537c55c2d3a987e Mon Sep 17 00:00:00 2001 From: trav90 Date: Wed, 21 Nov 2018 08:44:16 -0600 Subject: Remove remaining MOZ_GONK_MEDIACODEC code --- dom/media/MediaPrefs.h | 3 --- dom/media/fmp4/MP4Decoder.cpp | 4 ---- dom/media/fmp4/moz.build | 3 --- dom/media/mediasource/moz.build | 3 --- dom/media/platforms/PDMFactory.cpp | 9 --------- 5 files changed, 22 deletions(-) (limited to 'dom/media') diff --git a/dom/media/MediaPrefs.h b/dom/media/MediaPrefs.h index e67796edd..c67a89989 100644 --- a/dom/media/MediaPrefs.h +++ b/dom/media/MediaPrefs.h @@ -105,9 +105,6 @@ private: DECL_MEDIA_PREF("media.eme.enabled", EMEEnabled, bool, false); DECL_MEDIA_PREF("media.use-blank-decoder", PDMUseBlankDecoder, bool, false); DECL_MEDIA_PREF("media.gpu-process-decoder", PDMUseGPUDecoder, bool, false); -#ifdef MOZ_GONK_MEDIACODEC - DECL_MEDIA_PREF("media.gonk.enabled", PDMGonkDecoderEnabled, bool, true); -#endif #ifdef MOZ_WIDGET_ANDROID DECL_MEDIA_PREF("media.android-media-codec.enabled", PDMAndroidMediaCodecEnabled, bool, false); DECL_MEDIA_PREF("media.android-media-codec.preferred", PDMAndroidMediaCodecPreferred, bool, false); diff --git a/dom/media/fmp4/MP4Decoder.cpp b/dom/media/fmp4/MP4Decoder.cpp index 25dd53f94..6954e9757 100644 --- a/dom/media/fmp4/MP4Decoder.cpp +++ b/dom/media/fmp4/MP4Decoder.cpp @@ -83,10 +83,6 @@ MP4Decoder::CanHandleMediaType(const MediaContentType& aType, const bool isMP4Audio = aType.GetMIMEType().EqualsASCII("audio/mp4") || aType.GetMIMEType().EqualsASCII("audio/x-m4a"); const bool isMP4Video = - // On B2G, treat 3GPP as MP4 when Gonk PDM is available. -#ifdef MOZ_GONK_MEDIACODEC - aType.GetMIMEType().EqualsASCII(VIDEO_3GPP) || -#endif aType.GetMIMEType().EqualsASCII("video/mp4") || aType.GetMIMEType().EqualsASCII("video/quicktime") || aType.GetMIMEType().EqualsASCII("video/x-m4v"); diff --git a/dom/media/fmp4/moz.build b/dom/media/fmp4/moz.build index 6a249ae3e..a79fb0229 100644 --- a/dom/media/fmp4/moz.build +++ b/dom/media/fmp4/moz.build @@ -20,6 +20,3 @@ SOURCES += [ ] FINAL_LIBRARY = 'xul' - -if CONFIG['MOZ_GONK_MEDIACODEC']: - DEFINES['MOZ_GONK_MEDIACODEC'] = True diff --git a/dom/media/mediasource/moz.build b/dom/media/mediasource/moz.build index 6ded1875d..a1689c216 100644 --- a/dom/media/mediasource/moz.build +++ b/dom/media/mediasource/moz.build @@ -38,9 +38,6 @@ TEST_DIRS += [ 'gtest', ] -if CONFIG['MOZ_GONK_MEDIACODEC']: - DEFINES['MOZ_GONK_MEDIACODEC'] = True - include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' diff --git a/dom/media/platforms/PDMFactory.cpp b/dom/media/platforms/PDMFactory.cpp index c1e58fdc2..5bfdcffb7 100644 --- a/dom/media/platforms/PDMFactory.cpp +++ b/dom/media/platforms/PDMFactory.cpp @@ -19,9 +19,6 @@ #ifdef MOZ_APPLEMEDIA #include "AppleDecoderModule.h" #endif -#ifdef MOZ_GONK_MEDIACODEC -#include "GonkDecoderModule.h" -#endif #ifdef MOZ_WIDGET_ANDROID #include "AndroidDecoderModule.h" #endif @@ -390,12 +387,6 @@ PDMFactory::CreatePDMs() m = new AppleDecoderModule(); StartupPDM(m); #endif -#ifdef MOZ_GONK_MEDIACODEC - if (MediaPrefs::PDMGonkDecoderEnabled()) { - m = new GonkDecoderModule(); - StartupPDM(m); - } -#endif #ifdef MOZ_WIDGET_ANDROID if(MediaPrefs::PDMAndroidMediaCodecEnabled()){ m = new AndroidDecoderModule(); -- cgit v1.2.3 From 4c8c14a712515bc867d6fbfdb9366b976c5904f5 Mon Sep 17 00:00:00 2001 From: trav90 Date: Wed, 21 Nov 2018 08:51:13 -0600 Subject: Remove OMX_PLATFORM_GONK --- dom/media/platforms/omx/OmxPlatformLayer.cpp | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) (limited to 'dom/media') diff --git a/dom/media/platforms/omx/OmxPlatformLayer.cpp b/dom/media/platforms/omx/OmxPlatformLayer.cpp index 039b4a22f..15b3062a4 100644 --- a/dom/media/platforms/omx/OmxPlatformLayer.cpp +++ b/dom/media/platforms/omx/OmxPlatformLayer.cpp @@ -282,26 +282,7 @@ OmxPlatformLayer::CompressionFormat() } } -// Implementations for different platforms will be defined in their own files. -#ifdef OMX_PLATFORM_GONK - -bool -OmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType) -{ - return GonkOmxPlatformLayer::FindComponents(aMimeType); -} - -OmxPlatformLayer* -OmxPlatformLayer::Create(OmxDataDecoder* aDataDecoder, - OmxPromiseLayer* aPromiseLayer, - TaskQueue* aTaskQueue, - layers::ImageContainer* aImageContainer) -{ - return new GonkOmxPlatformLayer(aDataDecoder, aPromiseLayer, aTaskQueue, aImageContainer); -} - -#else // For platforms without OMX IL support. - +// For platforms without OMX IL support. bool OmxPlatformLayer::SupportsMimeType(const nsACString& aMimeType) { @@ -317,6 +298,4 @@ OmxPlatformLayer::Create(OmxDataDecoder* aDataDecoder, return nullptr; } -#endif - } -- cgit v1.2.3 From f68b57c0bd3863d9057508929354ec34a3fd6205 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sat, 1 Dec 2018 09:35:02 -0600 Subject: Remove DirectShowReader Part 1: Conditional code --- dom/media/DecoderTraits.cpp | 40 +-------------------------------------- dom/media/ThreadPoolCOMListener.h | 4 ++-- 2 files changed, 3 insertions(+), 41 deletions(-) (limited to 'dom/media') diff --git a/dom/media/DecoderTraits.cpp b/dom/media/DecoderTraits.cpp index 56ebd9ce9..473ca9533 100644 --- a/dom/media/DecoderTraits.cpp +++ b/dom/media/DecoderTraits.cpp @@ -23,10 +23,6 @@ #include "AndroidMediaReader.h" #include "AndroidMediaPluginHost.h" #endif -#ifdef MOZ_DIRECTSHOW -#include "DirectShowDecoder.h" -#include "DirectShowReader.h" -#endif #ifdef MOZ_FMP4 #include "MP4Decoder.h" #include "MP4Demuxer.h" @@ -125,14 +121,6 @@ IsAndroidMediaType(const nsACString& aType) } #endif -#ifdef MOZ_DIRECTSHOW -static bool -IsDirectShowSupportedType(const nsACString& aType) -{ - return DirectShowDecoder::GetSupportedCodecs(aType, nullptr); -} -#endif - #ifdef MOZ_FMP4 static bool IsMP4SupportedType(const MediaContentType& aParsedType, @@ -247,9 +235,6 @@ CanHandleCodecsType(const MediaContentType& aType, if (IsFlacSupportedType(aType.GetMIMEType(), aType.GetCodecs())) { return CANPLAY_YES; } -#ifdef MOZ_DIRECTSHOW - DirectShowDecoder::GetSupportedCodecs(aType.GetMIMEType(), &codecList); -#endif #ifdef MOZ_ANDROID_OMX if (MediaDecoder::IsAndroidMediaPluginEnabled()) { EnsureAndroidMediaPluginHost()->FindDecoder(aType.GetMIMEType(), &codecList); @@ -320,11 +305,6 @@ CanHandleMediaType(const MediaContentType& aType, if (IsFlacSupportedType(aType.GetMIMEType())) { return CANPLAY_MAYBE; } -#ifdef MOZ_DIRECTSHOW - if (DirectShowDecoder::GetSupportedCodecs(aType.GetMIMEType(), nullptr)) { - return CANPLAY_MAYBE; - } -#endif #ifdef MOZ_ANDROID_OMX if (MediaDecoder::IsAndroidMediaPluginEnabled() && EnsureAndroidMediaPluginHost()->FindDecoder(aType.GetMIMEType(), nullptr)) { @@ -424,15 +404,6 @@ InstantiateDecoder(const nsACString& aType, return decoder.forget(); } -#ifdef MOZ_DIRECTSHOW - // Note: DirectShow should come before WMF, so that we prefer DirectShow's - // MP3 support over WMF's. - if (IsDirectShowSupportedType(aType)) { - decoder = new DirectShowDecoder(aOwner); - return decoder.forget(); - } -#endif - return nullptr; } @@ -484,13 +455,7 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac if (IsWebMSupportedType(aType)) { decoderReader = new MediaFormatReader(aDecoder, new WebMDemuxer(aDecoder->GetResource())); - } else -#ifdef MOZ_DIRECTSHOW - if (IsDirectShowSupportedType(aType)) { - decoderReader = new DirectShowReader(aDecoder); - } else -#endif - if (false) {} // dummy if to take care of the dangling else + } return decoderReader; } @@ -518,9 +483,6 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType) IsMP3SupportedType(aType) || IsAACSupportedType(aType) || IsFlacSupportedType(aType) || -#ifdef MOZ_DIRECTSHOW - IsDirectShowSupportedType(aType) || -#endif false; } diff --git a/dom/media/ThreadPoolCOMListener.h b/dom/media/ThreadPoolCOMListener.h index 881013a78..424ca65d2 100644 --- a/dom/media/ThreadPoolCOMListener.h +++ b/dom/media/ThreadPoolCOMListener.h @@ -13,8 +13,8 @@ namespace mozilla { // Thread pool listener which ensures that MSCOM is initialized and -// deinitialized on the thread pool thread. We may call into WMF or -// DirectShow on this thread, so we need MSCOM working. +// deinitialized on the thread pool thread. We may call into WMF on +// this thread, so we need MSCOM working. class MSCOMInitThreadPoolListener final : public nsIThreadPoolListener { ~MSCOMInitThreadPoolListener() {} public: -- cgit v1.2.3 From b867c50ee62d5ae2379d9c0c2f2a178d53165759 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sat, 1 Dec 2018 09:38:51 -0600 Subject: Remove DirectShowReader Part 2: Build system --- dom/media/moz.build | 8 -------- 1 file changed, 8 deletions(-) (limited to 'dom/media') diff --git a/dom/media/moz.build b/dom/media/moz.build index 4d036a5f6..b3db05af1 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -42,9 +42,6 @@ DIRS += [ 'standalone', ] -if CONFIG['MOZ_DIRECTSHOW']: - DIRS += ['directshow'] - if CONFIG['MOZ_ANDROID_OMX']: DIRS += ['android'] @@ -294,11 +291,6 @@ LOCAL_INCLUDES += [ '/netwerk/base', ] -if CONFIG['MOZ_DIRECTSHOW']: - LOCAL_INCLUDES += [ - '/media/webrtc/trunk/webrtc/modules/video_capture/windows', - ] - if CONFIG['MOZ_WEBRTC']: LOCAL_INCLUDES += [ '/media/webrtc/signaling/src/common', -- cgit v1.2.3 From f2723fc8bf8c961af2d29252f380544c47c72371 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sat, 1 Dec 2018 09:40:13 -0600 Subject: Remove DirectShowReader Part 3: Directories --- dom/media/directshow/AudioSinkFilter.cpp | 285 ------------ dom/media/directshow/AudioSinkFilter.h | 95 ---- dom/media/directshow/AudioSinkInputPin.cpp | 195 -------- dom/media/directshow/AudioSinkInputPin.h | 76 ---- dom/media/directshow/DirectShowDecoder.cpp | 65 --- dom/media/directshow/DirectShowDecoder.h | 45 -- dom/media/directshow/DirectShowReader.cpp | 360 --------------- dom/media/directshow/DirectShowReader.h | 110 ----- dom/media/directshow/DirectShowUtils.cpp | 369 ---------------- dom/media/directshow/DirectShowUtils.h | 125 ------ dom/media/directshow/SampleSink.cpp | 159 ------- dom/media/directshow/SampleSink.h | 67 --- dom/media/directshow/SourceFilter.cpp | 683 ----------------------------- dom/media/directshow/SourceFilter.h | 75 ---- dom/media/directshow/moz.build | 41 -- 15 files changed, 2750 deletions(-) delete mode 100644 dom/media/directshow/AudioSinkFilter.cpp delete mode 100644 dom/media/directshow/AudioSinkFilter.h delete mode 100644 dom/media/directshow/AudioSinkInputPin.cpp delete mode 100644 dom/media/directshow/AudioSinkInputPin.h delete mode 100644 dom/media/directshow/DirectShowDecoder.cpp delete mode 100644 dom/media/directshow/DirectShowDecoder.h delete mode 100644 dom/media/directshow/DirectShowReader.cpp delete mode 100644 dom/media/directshow/DirectShowReader.h delete mode 100644 dom/media/directshow/DirectShowUtils.cpp delete mode 100644 dom/media/directshow/DirectShowUtils.h delete mode 100644 dom/media/directshow/SampleSink.cpp delete mode 100644 dom/media/directshow/SampleSink.h delete mode 100644 dom/media/directshow/SourceFilter.cpp delete mode 100644 dom/media/directshow/SourceFilter.h delete mode 100644 dom/media/directshow/moz.build (limited to 'dom/media') diff --git a/dom/media/directshow/AudioSinkFilter.cpp b/dom/media/directshow/AudioSinkFilter.cpp deleted file mode 100644 index 9f23c0e00..000000000 --- a/dom/media/directshow/AudioSinkFilter.cpp +++ /dev/null @@ -1,285 +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 "SampleSink.h" -#include "AudioSinkFilter.h" -#include "AudioSinkInputPin.h" -#include "VideoUtils.h" -#include "mozilla/Logging.h" - - -#include -#include - -#define DELETE_RESET(p) { delete (p) ; (p) = nullptr ;} - -DEFINE_GUID(CLSID_MozAudioSinkFilter, 0x1872d8c8, 0xea8d, 0x4c34, 0xae, 0x96, 0x69, 0xde, - 0xf1, 0x33, 0x7b, 0x33); - -using namespace mozilla::media; - -namespace mozilla { - -static LazyLogModule gDirectShowLog("DirectShowDecoder"); -#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) - -AudioSinkFilter::AudioSinkFilter(const wchar_t* aObjectName, HRESULT* aOutResult) - : BaseFilter(aObjectName, CLSID_MozAudioSinkFilter), - mFilterCritSec("AudioSinkFilter::mFilterCritSec") -{ - (*aOutResult) = S_OK; - mInputPin = new AudioSinkInputPin(L"AudioSinkInputPin", - this, - &mFilterCritSec, - aOutResult); -} - -AudioSinkFilter::~AudioSinkFilter() -{ -} - -int -AudioSinkFilter::GetPinCount() -{ - return 1; -} - -BasePin* -AudioSinkFilter::GetPin(int aIndex) -{ - CriticalSectionAutoEnter lockFilter(mFilterCritSec); - return (aIndex == 0) ? static_cast(mInputPin) : nullptr; -} - -HRESULT -AudioSinkFilter::Pause() -{ - CriticalSectionAutoEnter lockFilter(mFilterCritSec); - if (mState == State_Stopped) { - // Change the state, THEN activate the input pin. - mState = State_Paused; - if (mInputPin && mInputPin->IsConnected()) { - mInputPin->Active(); - } - } else if (mState == State_Running) { - mState = State_Paused; - } - return S_OK; -} - -HRESULT -AudioSinkFilter::Stop() -{ - CriticalSectionAutoEnter lockFilter(mFilterCritSec); - mState = State_Stopped; - if (mInputPin) { - mInputPin->Inactive(); - } - - GetSampleSink()->Flush(); - - return S_OK; -} - -HRESULT -AudioSinkFilter::Run(REFERENCE_TIME tStart) -{ - LOG("AudioSinkFilter::Run(%lld) [%4.2lf]", - RefTimeToUsecs(tStart), - double(RefTimeToUsecs(tStart)) / USECS_PER_S); - return media::BaseFilter::Run(tStart); -} - -HRESULT -AudioSinkFilter::GetClassID( OUT CLSID * pCLSID ) -{ - (* pCLSID) = CLSID_MozAudioSinkFilter; - return S_OK; -} - -HRESULT -AudioSinkFilter::QueryInterface(REFIID aIId, void **aInterface) -{ - if (aIId == IID_IMediaSeeking) { - *aInterface = static_cast(this); - AddRef(); - return S_OK; - } - return mozilla::media::BaseFilter::QueryInterface(aIId, aInterface); -} - -ULONG -AudioSinkFilter::AddRef() -{ - return ::InterlockedIncrement(&mRefCnt); -} - -ULONG -AudioSinkFilter::Release() -{ - unsigned long newRefCnt = ::InterlockedDecrement(&mRefCnt); - if (!newRefCnt) { - delete this; - } - return newRefCnt; -} - -SampleSink* -AudioSinkFilter::GetSampleSink() -{ - return mInputPin->GetSampleSink(); -} - - -// IMediaSeeking implementation. -// -// Calls to IMediaSeeking are forwarded to the output pin that the -// AudioSinkInputPin is connected to, i.e. upstream towards the parser and -// source filters, which actually implement seeking. -#define ENSURE_CONNECTED_PIN_SEEKING \ - if (!mInputPin) { \ - return E_NOTIMPL; \ - } \ - RefPtr pinSeeking = mInputPin->GetConnectedPinSeeking(); \ - if (!pinSeeking) { \ - return E_NOTIMPL; \ - } - -HRESULT -AudioSinkFilter::GetCapabilities(DWORD* aCapabilities) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetCapabilities(aCapabilities); -} - -HRESULT -AudioSinkFilter::CheckCapabilities(DWORD* aCapabilities) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->CheckCapabilities(aCapabilities); -} - -HRESULT -AudioSinkFilter::IsFormatSupported(const GUID* aFormat) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->IsFormatSupported(aFormat); -} - -HRESULT -AudioSinkFilter::QueryPreferredFormat(GUID* aFormat) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->QueryPreferredFormat(aFormat); -} - -HRESULT -AudioSinkFilter::GetTimeFormat(GUID* aFormat) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetTimeFormat(aFormat); -} - -HRESULT -AudioSinkFilter::IsUsingTimeFormat(const GUID* aFormat) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->IsUsingTimeFormat(aFormat); -} - -HRESULT -AudioSinkFilter::SetTimeFormat(const GUID* aFormat) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->SetTimeFormat(aFormat); -} - -HRESULT -AudioSinkFilter::GetDuration(LONGLONG* aDuration) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetDuration(aDuration); -} - -HRESULT -AudioSinkFilter::GetStopPosition(LONGLONG* aStop) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetStopPosition(aStop); -} - -HRESULT -AudioSinkFilter::GetCurrentPosition(LONGLONG* aCurrent) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetCurrentPosition(aCurrent); -} - -HRESULT -AudioSinkFilter::ConvertTimeFormat(LONGLONG* aTarget, - const GUID* aTargetFormat, - LONGLONG aSource, - const GUID* aSourceFormat) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->ConvertTimeFormat(aTarget, - aTargetFormat, - aSource, - aSourceFormat); -} - -HRESULT -AudioSinkFilter::SetPositions(LONGLONG* aCurrent, - DWORD aCurrentFlags, - LONGLONG* aStop, - DWORD aStopFlags) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->SetPositions(aCurrent, - aCurrentFlags, - aStop, - aStopFlags); -} - -HRESULT -AudioSinkFilter::GetPositions(LONGLONG* aCurrent, - LONGLONG* aStop) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetPositions(aCurrent, aStop); -} - -HRESULT -AudioSinkFilter::GetAvailable(LONGLONG* aEarliest, - LONGLONG* aLatest) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetAvailable(aEarliest, aLatest); -} - -HRESULT -AudioSinkFilter::SetRate(double aRate) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->SetRate(aRate); -} - -HRESULT -AudioSinkFilter::GetRate(double* aRate) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetRate(aRate); -} - -HRESULT -AudioSinkFilter::GetPreroll(LONGLONG* aPreroll) -{ - ENSURE_CONNECTED_PIN_SEEKING - return pinSeeking->GetPreroll(aPreroll); -} - -} // namespace mozilla - diff --git a/dom/media/directshow/AudioSinkFilter.h b/dom/media/directshow/AudioSinkFilter.h deleted file mode 100644 index 85abdfccf..000000000 --- a/dom/media/directshow/AudioSinkFilter.h +++ /dev/null @@ -1,95 +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(AudioSinkFilter_h_) -#define AudioSinkFilter_h_ - -#include "BaseFilter.h" -#include "DirectShowUtils.h" -#include "nsAutoPtr.h" -#include "mozilla/RefPtr.h" - -namespace mozilla { - -class AudioSinkInputPin; -class SampleSink; - -// Filter that acts as the end of the graph. Audio samples input into -// this filter block the calling thread, and the calling thread is -// unblocked when the decode thread extracts the sample. The samples -// input into this filter are stored in the SampleSink, where the blocking -// is implemented. The input pin owns the SampleSink. -class AudioSinkFilter: public mozilla::media::BaseFilter, - public IMediaSeeking -{ - -public: - AudioSinkFilter(const wchar_t* aObjectName, HRESULT* aOutResult); - virtual ~AudioSinkFilter(); - - // Gets the input pin's sample sink. - SampleSink* GetSampleSink(); - - // IUnknown implementation. - STDMETHODIMP QueryInterface(REFIID aIId, void **aInterface); - STDMETHODIMP_(ULONG) AddRef(); - STDMETHODIMP_(ULONG) Release(); - - // -------------------------------------------------------------------- - // CBaseFilter methods - int GetPinCount (); - mozilla::media::BasePin* GetPin ( IN int Index); - STDMETHODIMP Pause (); - STDMETHODIMP Stop (); - STDMETHODIMP GetClassID ( OUT CLSID * pCLSID); - STDMETHODIMP Run(REFERENCE_TIME tStart); - // IMediaSeeking Methods... - - // We defer to SourceFilter, but we must expose the interface on - // the output pins. Seeking commands come upstream from the renderers, - // but they must be actioned at the source filters. - STDMETHODIMP GetCapabilities(DWORD* aCapabilities); - STDMETHODIMP CheckCapabilities(DWORD* aCapabilities); - STDMETHODIMP IsFormatSupported(const GUID* aFormat); - STDMETHODIMP QueryPreferredFormat(GUID* aFormat); - STDMETHODIMP GetTimeFormat(GUID* aFormat); - STDMETHODIMP IsUsingTimeFormat(const GUID* aFormat); - STDMETHODIMP SetTimeFormat(const GUID* aFormat); - STDMETHODIMP GetDuration(LONGLONG* pDuration); - STDMETHODIMP GetStopPosition(LONGLONG* pStop); - STDMETHODIMP GetCurrentPosition(LONGLONG* aCurrent); - STDMETHODIMP ConvertTimeFormat(LONGLONG* aTarget, - const GUID* aTargetFormat, - LONGLONG aSource, - const GUID* aSourceFormat); - STDMETHODIMP SetPositions(LONGLONG* aCurrent, - DWORD aCurrentFlags, - LONGLONG* aStop, - DWORD aStopFlags); - STDMETHODIMP GetPositions(LONGLONG* aCurrent, - LONGLONG* aStop); - STDMETHODIMP GetAvailable(LONGLONG* aEarliest, - LONGLONG* aLatest); - STDMETHODIMP SetRate(double aRate); - STDMETHODIMP GetRate(double* aRate); - STDMETHODIMP GetPreroll(LONGLONG* aPreroll); - - // -------------------------------------------------------------------- - // class factory calls this - static IUnknown * CreateInstance (IN LPUNKNOWN punk, OUT HRESULT * phr); - -private: - CriticalSection mFilterCritSec; - - // Note: The input pin defers its refcounting to the sink filter, so when - // the input pin is addrefed, what actually happens is the sink filter is - // addrefed. - nsAutoPtr mInputPin; -}; - -} // namespace mozilla - -#endif // AudioSinkFilter_h_ diff --git a/dom/media/directshow/AudioSinkInputPin.cpp b/dom/media/directshow/AudioSinkInputPin.cpp deleted file mode 100644 index 85a6e3da3..000000000 --- a/dom/media/directshow/AudioSinkInputPin.cpp +++ /dev/null @@ -1,195 +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 "AudioSinkInputPin.h" -#include "AudioSinkFilter.h" -#include "SampleSink.h" -#include "mozilla/Logging.h" - -#include - -using namespace mozilla::media; - -namespace mozilla { - -static LazyLogModule gDirectShowLog("DirectShowDecoder"); -#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) - -AudioSinkInputPin::AudioSinkInputPin(wchar_t* aObjectName, - AudioSinkFilter* aFilter, - mozilla::CriticalSection* aLock, - HRESULT* aOutResult) - : BaseInputPin(aObjectName, aFilter, aLock, aOutResult, aObjectName), - mSegmentStartTime(0) -{ - MOZ_COUNT_CTOR(AudioSinkInputPin); - mSampleSink = new SampleSink(); -} - -AudioSinkInputPin::~AudioSinkInputPin() -{ - MOZ_COUNT_DTOR(AudioSinkInputPin); -} - -HRESULT -AudioSinkInputPin::GetMediaType(int aPosition, MediaType* aOutMediaType) -{ - NS_ENSURE_TRUE(aPosition >= 0, E_INVALIDARG); - NS_ENSURE_TRUE(aOutMediaType, E_POINTER); - - if (aPosition > 0) { - return S_FALSE; - } - - // Note: We set output as PCM, as IEEE_FLOAT only works when using the - // MP3 decoder as an MFT, and we can't do that while using DirectShow. - aOutMediaType->SetType(&MEDIATYPE_Audio); - aOutMediaType->SetSubtype(&MEDIASUBTYPE_PCM); - aOutMediaType->SetType(&FORMAT_WaveFormatEx); - aOutMediaType->SetTemporalCompression(FALSE); - - return S_OK; -} - -HRESULT -AudioSinkInputPin::CheckMediaType(const MediaType* aMediaType) -{ - if (!aMediaType) { - return E_INVALIDARG; - } - - GUID majorType = *aMediaType->Type(); - if (majorType != MEDIATYPE_Audio && majorType != WMMEDIATYPE_Audio) { - return E_INVALIDARG; - } - - if (*aMediaType->Subtype() != MEDIASUBTYPE_PCM) { - return E_INVALIDARG; - } - - if (*aMediaType->FormatType() != FORMAT_WaveFormatEx) { - return E_INVALIDARG; - } - - // We accept the media type, stash its layout format! - WAVEFORMATEX* wfx = (WAVEFORMATEX*)(aMediaType->pbFormat); - GetSampleSink()->SetAudioFormat(wfx); - - return S_OK; -} - -AudioSinkFilter* -AudioSinkInputPin::GetAudioSinkFilter() -{ - return reinterpret_cast(mFilter); -} - -SampleSink* -AudioSinkInputPin::GetSampleSink() -{ - return mSampleSink; -} - -HRESULT -AudioSinkInputPin::SetAbsoluteMediaTime(IMediaSample* aSample) -{ - HRESULT hr; - REFERENCE_TIME start = 0, end = 0; - hr = aSample->GetTime(&start, &end); - NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL); - { - CriticalSectionAutoEnter lock(*mLock); - start += mSegmentStartTime; - end += mSegmentStartTime; - } - hr = aSample->SetMediaTime(&start, &end); - NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL); - return S_OK; -} - -HRESULT -AudioSinkInputPin::Receive(IMediaSample* aSample ) -{ - HRESULT hr; - NS_ENSURE_TRUE(aSample, E_POINTER); - - hr = BaseInputPin::Receive(aSample); - if (SUCCEEDED(hr) && hr != S_FALSE) { // S_FALSE == flushing - // Set the timestamp of the sample after being adjusted for - // seeking/segments in the "media time" attribute. When we seek, - // DirectShow starts a new "segment", and starts labeling samples - // from time=0 again, so we need to correct for this to get the - // actual timestamps after seeking. - hr = SetAbsoluteMediaTime(aSample); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - hr = GetSampleSink()->Receive(aSample); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - } - return S_OK; -} - -already_AddRefed -AudioSinkInputPin::GetConnectedPinSeeking() -{ - RefPtr peer = GetConnected(); - if (!peer) - return nullptr; - RefPtr seeking; - peer->QueryInterface(static_cast(getter_AddRefs(seeking))); - return seeking.forget(); -} - -HRESULT -AudioSinkInputPin::BeginFlush() -{ - HRESULT hr = media::BaseInputPin::BeginFlush(); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - GetSampleSink()->Flush(); - - return S_OK; -} - -HRESULT -AudioSinkInputPin::EndFlush() -{ - HRESULT hr = media::BaseInputPin::EndFlush(); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - // Reset the EOS flag, so that if we're called after a seek we still work. - GetSampleSink()->Reset(); - - return S_OK; -} - -HRESULT -AudioSinkInputPin::EndOfStream(void) -{ - HRESULT hr = media::BaseInputPin::EndOfStream(); - if (FAILED(hr) || hr == S_FALSE) { - // Pin is stil flushing. - return hr; - } - GetSampleSink()->SetEOS(); - - return S_OK; -} - - -HRESULT -AudioSinkInputPin::NewSegment(REFERENCE_TIME tStart, - REFERENCE_TIME tStop, - double dRate) -{ - CriticalSectionAutoEnter lock(*mLock); - // Record the start time of the new segment, so that we can store the - // correct absolute timestamp in the "media time" each incoming sample. - mSegmentStartTime = tStart; - return S_OK; -} - -} // namespace mozilla - diff --git a/dom/media/directshow/AudioSinkInputPin.h b/dom/media/directshow/AudioSinkInputPin.h deleted file mode 100644 index 80503c641..000000000 --- a/dom/media/directshow/AudioSinkInputPin.h +++ /dev/null @@ -1,76 +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(AudioSinkInputPin_h_) -#define AudioSinkInputPin_h_ - -#include "BaseInputPin.h" -#include "DirectShowUtils.h" -#include "mozilla/RefPtr.h" -#include "nsAutoPtr.h" - -namespace mozilla { - -namespace media { - class MediaType; -} - -class AudioSinkFilter; -class SampleSink; - - -// Input pin for capturing audio output of a DirectShow filter graph. -// This is the input pin for the AudioSinkFilter. -class AudioSinkInputPin: public mozilla::media::BaseInputPin -{ -public: - AudioSinkInputPin(wchar_t* aObjectName, - AudioSinkFilter* aFilter, - mozilla::CriticalSection* aLock, - HRESULT* aOutResult); - virtual ~AudioSinkInputPin(); - - HRESULT GetMediaType (IN int iPos, OUT mozilla::media::MediaType * pmt); - HRESULT CheckMediaType (IN const mozilla::media::MediaType * pmt); - STDMETHODIMP Receive (IN IMediaSample *); - STDMETHODIMP BeginFlush() override; - STDMETHODIMP EndFlush() override; - - // Called when we start decoding a new segment, that happens directly after - // a seek. This captures the segment's start time. Samples decoded by the - // MP3 decoder have their timestamps offset from the segment start time. - // Storing the segment start time enables us to set each sample's MediaTime - // as an offset in the stream relative to the start of the stream, rather - // than the start of the segment, i.e. its absolute time in the stream. - STDMETHODIMP NewSegment(REFERENCE_TIME tStart, - REFERENCE_TIME tStop, - double dRate) override; - - STDMETHODIMP EndOfStream() override; - - // Returns the IMediaSeeking interface of the connected output pin. - // We forward seeking requests upstream from the sink to the source - // filters. - already_AddRefed GetConnectedPinSeeking(); - - SampleSink* GetSampleSink(); - -private: - AudioSinkFilter* GetAudioSinkFilter(); - - // Sets the media time on the media sample, relative to the segment - // start time. - HRESULT SetAbsoluteMediaTime(IMediaSample* aSample); - - nsAutoPtr mSampleSink; - - // Synchronized by the filter lock; BaseInputPin::mLock. - REFERENCE_TIME mSegmentStartTime; -}; - -} // namespace mozilla - -#endif // AudioSinkInputPin_h_ diff --git a/dom/media/directshow/DirectShowDecoder.cpp b/dom/media/directshow/DirectShowDecoder.cpp deleted file mode 100644 index da68b4daa..000000000 --- a/dom/media/directshow/DirectShowDecoder.cpp +++ /dev/null @@ -1,65 +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 "DirectShowDecoder.h" -#include "DirectShowReader.h" -#include "DirectShowUtils.h" -#include "MediaDecoderStateMachine.h" -#include "mozilla/Preferences.h" -#include "mozilla/WindowsVersion.h" - -namespace mozilla { - -MediaDecoderStateMachine* DirectShowDecoder::CreateStateMachine() -{ - return new MediaDecoderStateMachine(this, new DirectShowReader(this)); -} - -/* static */ -bool -DirectShowDecoder::GetSupportedCodecs(const nsACString& aType, - char const *const ** aCodecList) -{ - if (!IsEnabled()) { - return false; - } - - static char const *const mp3AudioCodecs[] = { - "mp3", - nullptr - }; - if (aType.EqualsASCII("audio/mpeg") || - aType.EqualsASCII("audio/mp3")) { - if (aCodecList) { - *aCodecList = mp3AudioCodecs; - } - return true; - } - - return false; -} - -/* static */ -bool -DirectShowDecoder::IsEnabled() -{ - return CanDecodeMP3UsingDirectShow() && - Preferences::GetBool("media.directshow.enabled"); -} - -DirectShowDecoder::DirectShowDecoder(MediaDecoderOwner* aOwner) - : MediaDecoder(aOwner) -{ - MOZ_COUNT_CTOR(DirectShowDecoder); -} - -DirectShowDecoder::~DirectShowDecoder() -{ - MOZ_COUNT_DTOR(DirectShowDecoder); -} - -} // namespace mozilla - diff --git a/dom/media/directshow/DirectShowDecoder.h b/dom/media/directshow/DirectShowDecoder.h deleted file mode 100644 index c4d371fbf..000000000 --- a/dom/media/directshow/DirectShowDecoder.h +++ /dev/null @@ -1,45 +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(DirectShowDecoder_h_) -#define DirectShowDecoder_h_ - -#include "MediaDecoder.h" - -namespace mozilla { - -// Decoder that uses DirectShow to playback MP3 files only. -class DirectShowDecoder : public MediaDecoder -{ -public: - - explicit DirectShowDecoder(MediaDecoderOwner* aOwner); - virtual ~DirectShowDecoder(); - - MediaDecoder* Clone(MediaDecoderOwner* aOwner) override { - if (!IsEnabled()) { - return nullptr; - } - return new DirectShowDecoder(aOwner); - } - - MediaDecoderStateMachine* CreateStateMachine() override; - - // Returns true if aType is a MIME type that we render with the - // DirectShow backend. If aCodecList is non null, - // it is filled with a (static const) null-terminated list of strings - // denoting the codecs we'll playback. Note that playback is strictly - // limited to MP3 only. - static bool GetSupportedCodecs(const nsACString& aType, - char const *const ** aCodecList); - - // Returns true if the DirectShow backend is preffed on. - static bool IsEnabled(); -}; - -} // namespace mozilla - -#endif diff --git a/dom/media/directshow/DirectShowReader.cpp b/dom/media/directshow/DirectShowReader.cpp deleted file mode 100644 index cacf6f8de..000000000 --- a/dom/media/directshow/DirectShowReader.cpp +++ /dev/null @@ -1,360 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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 "DirectShowReader.h" -#include "MediaDecoderReader.h" -#include "mozilla/RefPtr.h" -#include "DirectShowUtils.h" -#include "AudioSinkFilter.h" -#include "SourceFilter.h" -#include "SampleSink.h" -#include "VideoUtils.h" - -using namespace mozilla::media; - -namespace mozilla { - -// Windows XP's MP3 decoder filter. This is available on XP only, on Vista -// and later we can use the DMO Wrapper filter and MP3 decoder DMO. -const GUID DirectShowReader::CLSID_MPEG_LAYER_3_DECODER_FILTER = -{ 0x38BE3000, 0xDBF4, 0x11D0, {0x86, 0x0E, 0x00, 0xA0, 0x24, 0xCF, 0xEF, 0x6D} }; - - -static LazyLogModule gDirectShowLog("DirectShowDecoder"); -#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) - -DirectShowReader::DirectShowReader(AbstractMediaDecoder* aDecoder) - : MediaDecoderReader(aDecoder), - mMP3FrameParser(aDecoder->GetResource()->GetLength()), -#ifdef DIRECTSHOW_REGISTER_GRAPH - mRotRegister(0), -#endif - mNumChannels(0), - mAudioRate(0), - mBytesPerSample(0) -{ - MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); - MOZ_COUNT_CTOR(DirectShowReader); -} - -DirectShowReader::~DirectShowReader() -{ - MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); - MOZ_COUNT_DTOR(DirectShowReader); -#ifdef DIRECTSHOW_REGISTER_GRAPH - if (mRotRegister) { - RemoveGraphFromRunningObjectTable(mRotRegister); - } -#endif -} - -// Try to parse the MP3 stream to make sure this is indeed an MP3, get the -// estimated duration of the stream, and find the offset of the actual MP3 -// frames in the stream, as DirectShow doesn't like large ID3 sections. -static nsresult -ParseMP3Headers(MP3FrameParser *aParser, MediaResource *aResource) -{ - const uint32_t MAX_READ_SIZE = 4096; - - uint64_t offset = 0; - while (aParser->NeedsData() && !aParser->ParsedHeaders()) { - uint32_t bytesRead; - char buffer[MAX_READ_SIZE]; - nsresult rv = aResource->ReadAt(offset, buffer, - MAX_READ_SIZE, &bytesRead); - NS_ENSURE_SUCCESS(rv, rv); - - if (!bytesRead) { - // End of stream. - return NS_ERROR_FAILURE; - } - - aParser->Parse(reinterpret_cast(buffer), bytesRead, offset); - offset += bytesRead; - } - - return aParser->IsMP3() ? NS_OK : NS_ERROR_FAILURE; -} - -nsresult -DirectShowReader::ReadMetadata(MediaInfo* aInfo, - MetadataTags** aTags) -{ - MOZ_ASSERT(OnTaskQueue()); - HRESULT hr; - nsresult rv; - - // Create the filter graph, reference it by the GraphBuilder interface, - // to make graph building more convenient. - hr = CoCreateInstance(CLSID_FilterGraph, - nullptr, - CLSCTX_INPROC_SERVER, - IID_IGraphBuilder, - reinterpret_cast(static_cast(getter_AddRefs(mGraph)))); - NS_ENSURE_TRUE(SUCCEEDED(hr) && mGraph, NS_ERROR_FAILURE); - - rv = ParseMP3Headers(&mMP3FrameParser, mDecoder->GetResource()); - NS_ENSURE_SUCCESS(rv, rv); - - #ifdef DIRECTSHOW_REGISTER_GRAPH - hr = AddGraphToRunningObjectTable(mGraph, &mRotRegister); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - #endif - - // Extract the interface pointers we'll need from the filter graph. - hr = mGraph->QueryInterface(static_cast(getter_AddRefs(mControl))); - NS_ENSURE_TRUE(SUCCEEDED(hr) && mControl, NS_ERROR_FAILURE); - - hr = mGraph->QueryInterface(static_cast(getter_AddRefs(mMediaSeeking))); - NS_ENSURE_TRUE(SUCCEEDED(hr) && mMediaSeeking, NS_ERROR_FAILURE); - - // Build the graph. Create the filters we need, and connect them. We - // build the entire graph ourselves to prevent other decoders installed - // on the system being created and used. - - // Our source filters, wraps the MediaResource. - mSourceFilter = new SourceFilter(MEDIATYPE_Stream, MEDIASUBTYPE_MPEG1Audio); - NS_ENSURE_TRUE(mSourceFilter, NS_ERROR_FAILURE); - - rv = mSourceFilter->Init(mDecoder->GetResource(), mMP3FrameParser.GetMP3Offset()); - NS_ENSURE_SUCCESS(rv, rv); - - hr = mGraph->AddFilter(mSourceFilter, L"MozillaDirectShowSource"); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - // The MPEG demuxer. - RefPtr demuxer; - hr = CreateAndAddFilter(mGraph, - CLSID_MPEG1Splitter, - L"MPEG1Splitter", - getter_AddRefs(demuxer)); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - // Platform MP3 decoder. - RefPtr decoder; - // Firstly try to create the MP3 decoder filter that ships with WinXP - // directly. This filter doesn't normally exist on later versions of - // Windows. - hr = CreateAndAddFilter(mGraph, - CLSID_MPEG_LAYER_3_DECODER_FILTER, - L"MPEG Layer 3 Decoder", - getter_AddRefs(decoder)); - if (FAILED(hr)) { - // Failed to create MP3 decoder filter. Try to instantiate - // the MP3 decoder DMO. - hr = AddMP3DMOWrapperFilter(mGraph, getter_AddRefs(decoder)); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - } - - // Sink, captures audio samples and inserts them into our pipeline. - static const wchar_t* AudioSinkFilterName = L"MozAudioSinkFilter"; - mAudioSinkFilter = new AudioSinkFilter(AudioSinkFilterName, &hr); - NS_ENSURE_TRUE(mAudioSinkFilter && SUCCEEDED(hr), NS_ERROR_FAILURE); - hr = mGraph->AddFilter(mAudioSinkFilter, AudioSinkFilterName); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - // Join the filters. - hr = ConnectFilters(mGraph, mSourceFilter, demuxer); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - hr = ConnectFilters(mGraph, demuxer, decoder); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - hr = ConnectFilters(mGraph, decoder, mAudioSinkFilter); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - WAVEFORMATEX format; - mAudioSinkFilter->GetSampleSink()->GetAudioFormat(&format); - NS_ENSURE_TRUE(format.wFormatTag == WAVE_FORMAT_PCM, NS_ERROR_FAILURE); - - mInfo.mAudio.mChannels = mNumChannels = format.nChannels; - mInfo.mAudio.mRate = mAudioRate = format.nSamplesPerSec; - mInfo.mAudio.mBitDepth = format.wBitsPerSample; - mBytesPerSample = format.wBitsPerSample / 8; - - // Begin decoding! - hr = mControl->Run(); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - DWORD seekCaps = 0; - hr = mMediaSeeking->GetCapabilities(&seekCaps); - mInfo.mMediaSeekable = SUCCEEDED(hr) && (AM_SEEKING_CanSeekAbsolute & seekCaps); - - int64_t duration = mMP3FrameParser.GetDuration(); - if (SUCCEEDED(hr)) { - mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration)); - } - - LOG("Successfully initialized DirectShow MP3 decoder."); - LOG("Channels=%u Hz=%u duration=%lld bytesPerSample=%d", - mInfo.mAudio.mChannels, - mInfo.mAudio.mRate, - RefTimeToUsecs(duration), - mBytesPerSample); - - *aInfo = mInfo; - // Note: The SourceFilter strips ID3v2 tags out of the stream. - *aTags = nullptr; - - return NS_OK; -} - -inline float -UnsignedByteToAudioSample(uint8_t aValue) -{ - return aValue * (2.0f / UINT8_MAX) - 1.0f; -} - -bool -DirectShowReader::Finish(HRESULT aStatus) -{ - MOZ_ASSERT(OnTaskQueue()); - - LOG("DirectShowReader::Finish(0x%x)", aStatus); - // Notify the filter graph of end of stream. - RefPtr eventSink; - HRESULT hr = mGraph->QueryInterface(static_cast(getter_AddRefs(eventSink))); - if (SUCCEEDED(hr) && eventSink) { - eventSink->Notify(EC_COMPLETE, aStatus, 0); - } - return false; -} - -class DirectShowCopy -{ -public: - DirectShowCopy(uint8_t *aSource, uint32_t aBytesPerSample, - uint32_t aSamples, uint32_t aChannels) - : mSource(aSource) - , mBytesPerSample(aBytesPerSample) - , mSamples(aSamples) - , mChannels(aChannels) - , mNextSample(0) - { } - - uint32_t operator()(AudioDataValue *aBuffer, uint32_t aSamples) - { - uint32_t maxSamples = std::min(aSamples, mSamples - mNextSample); - uint32_t frames = maxSamples / mChannels; - size_t byteOffset = mNextSample * mBytesPerSample; - if (mBytesPerSample == 1) { - for (uint32_t i = 0; i < maxSamples; ++i) { - uint8_t *sample = mSource + byteOffset; - aBuffer[i] = UnsignedByteToAudioSample(*sample); - byteOffset += mBytesPerSample; - } - } else if (mBytesPerSample == 2) { - for (uint32_t i = 0; i < maxSamples; ++i) { - int16_t *sample = reinterpret_cast(mSource + byteOffset); - aBuffer[i] = AudioSampleToFloat(*sample); - byteOffset += mBytesPerSample; - } - } - mNextSample += maxSamples; - return frames; - } - -private: - uint8_t * const mSource; - const uint32_t mBytesPerSample; - const uint32_t mSamples; - const uint32_t mChannels; - uint32_t mNextSample; -}; - -bool -DirectShowReader::DecodeAudioData() -{ - MOZ_ASSERT(OnTaskQueue()); - HRESULT hr; - - SampleSink* sink = mAudioSinkFilter->GetSampleSink(); - if (sink->AtEOS()) { - // End of stream. - return Finish(S_OK); - } - - // Get the next chunk of audio samples. This blocks until the sample - // arrives, or an error occurs (like the stream is shutdown). - RefPtr sample; - hr = sink->Extract(sample); - if (FAILED(hr) || hr == S_FALSE) { - return Finish(hr); - } - - int64_t start = 0, end = 0; - sample->GetMediaTime(&start, &end); - LOG("DirectShowReader::DecodeAudioData [%4.2lf-%4.2lf]", - RefTimeToSeconds(start), - RefTimeToSeconds(end)); - - LONG length = sample->GetActualDataLength(); - LONG numSamples = length / mBytesPerSample; - LONG numFrames = length / mBytesPerSample / mNumChannels; - - BYTE* data = nullptr; - hr = sample->GetPointer(&data); - NS_ENSURE_TRUE(SUCCEEDED(hr), Finish(hr)); - - mAudioCompactor.Push(mDecoder->GetResource()->Tell(), - RefTimeToUsecs(start), - mInfo.mAudio.mRate, - numFrames, - mNumChannels, - DirectShowCopy(reinterpret_cast(data), - mBytesPerSample, - numSamples, - mNumChannels)); - return true; -} - -bool -DirectShowReader::DecodeVideoFrame(bool &aKeyframeSkip, - int64_t aTimeThreshold) -{ - MOZ_ASSERT(OnTaskQueue()); - return false; -} - -RefPtr -DirectShowReader::Seek(SeekTarget aTarget, int64_t aEndTime) -{ - nsresult res = SeekInternal(aTarget.GetTime().ToMicroseconds()); - if (NS_FAILED(res)) { - return SeekPromise::CreateAndReject(res, __func__); - } else { - return SeekPromise::CreateAndResolve(aTarget.GetTime(), __func__); - } -} - -nsresult -DirectShowReader::SeekInternal(int64_t aTargetUs) -{ - HRESULT hr; - MOZ_ASSERT(OnTaskQueue()); - - LOG("DirectShowReader::Seek() target=%lld", aTargetUs); - - hr = mControl->Pause(); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - nsresult rv = ResetDecode(); - NS_ENSURE_SUCCESS(rv, rv); - - LONGLONG seekPosition = UsecsToRefTime(aTargetUs); - hr = mMediaSeeking->SetPositions(&seekPosition, - AM_SEEKING_AbsolutePositioning, - nullptr, - AM_SEEKING_NoPositioning); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - hr = mControl->Run(); - NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); - - return NS_OK; -} - -} // namespace mozilla diff --git a/dom/media/directshow/DirectShowReader.h b/dom/media/directshow/DirectShowReader.h deleted file mode 100644 index 881b27c28..000000000 --- a/dom/media/directshow/DirectShowReader.h +++ /dev/null @@ -1,110 +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(DirectShowReader_h_) -#define DirectShowReader_h_ - -#include "windows.h" // HRESULT, DWORD -#include "MediaDecoderReader.h" -#include "MediaResource.h" -#include "mozilla/RefPtr.h" -#include "MP3FrameParser.h" - -// Add the graph to the Running Object Table so that we can connect -// to this graph with GraphEdit/GraphStudio. Note: you must -// also regsvr32 proppage.dll from the Windows SDK. -// See: http://msdn.microsoft.com/en-us/library/ms787252(VS.85).aspx -// #define DIRECTSHOW_REGISTER_GRAPH - -struct IGraphBuilder; -struct IMediaControl; -struct IMediaSeeking; - -namespace mozilla { - -class AudioSinkFilter; -class SourceFilter; - -// Decoder backend for decoding MP3 using DirectShow. DirectShow operates as -// a filter graph. The basic design of the DirectShowReader is that we have -// a SourceFilter that wraps the MediaResource that connects to the -// MP3 decoder filter. The MP3 decoder filter "pulls" data as it requires it -// downstream on its own thread. When the MP3 decoder has produced a block of -// decoded samples, its thread calls downstream into our AudioSinkFilter, -// passing the decoded buffer in. The AudioSinkFilter inserts the samples into -// a SampleSink object. The SampleSink blocks the MP3 decoder's thread until -// the decode thread calls DecodeAudioData(), whereupon the SampleSink -// releases the decoded samples to the decode thread, and unblocks the MP3 -// decoder's thread. The MP3 decoder can then request more data from the -// SourceFilter, and decode more data. If the decode thread calls -// DecodeAudioData() and there's no decoded samples waiting to be extracted -// in the SampleSink, the SampleSink blocks the decode thread until the MP3 -// decoder produces a decoded sample. -class DirectShowReader : public MediaDecoderReader -{ -public: - DirectShowReader(AbstractMediaDecoder* aDecoder); - - virtual ~DirectShowReader(); - - bool DecodeAudioData() override; - bool DecodeVideoFrame(bool &aKeyframeSkip, - int64_t aTimeThreshold) override; - - nsresult ReadMetadata(MediaInfo* aInfo, - MetadataTags** aTags) override; - - RefPtr - Seek(SeekTarget aTarget, int64_t aEndTime) override; - - static const GUID CLSID_MPEG_LAYER_3_DECODER_FILTER; - -private: - // Notifies the filter graph that playback is complete. aStatus is - // the code to send to the filter graph. Always returns false, so - // that we can just "return Finish()" from DecodeAudioData(). - bool Finish(HRESULT aStatus); - - nsresult SeekInternal(int64_t aTime); - - // DirectShow filter graph, and associated playback and seeking - // control interfaces. - RefPtr mGraph; - RefPtr mControl; - RefPtr mMediaSeeking; - - // Wraps the MediaResource, and feeds undecoded data into the filter graph. - RefPtr mSourceFilter; - - // Sits at the end of the graph, removing decoded samples from the graph. - // The graph will block while this is blocked, i.e. it will pause decoding. - RefPtr mAudioSinkFilter; - - // Some MP3s are variable bitrate, so DirectShow's duration estimation - // can make its duration estimation based on the wrong bitrate. So we parse - // the MP3 frames to get a more accuate estimate of the duration. - MP3FrameParser mMP3FrameParser; - -#ifdef DIRECTSHOW_REGISTER_GRAPH - // Used to add/remove the filter graph to the Running Object Table. You can - // connect GraphEdit/GraphStudio to the graph to observe and/or debug its - // topology and state. - DWORD mRotRegister; -#endif - - // Number of channels in the audio stream. - uint32_t mNumChannels; - - // Samples per second in the audio stream. - uint32_t mAudioRate; - - // Number of bytes per sample. Can be either 1 or 2. - uint32_t mBytesPerSample; -}; - -} // namespace mozilla - -#endif diff --git a/dom/media/directshow/DirectShowUtils.cpp b/dom/media/directshow/DirectShowUtils.cpp deleted file mode 100644 index b2afa7528..000000000 --- a/dom/media/directshow/DirectShowUtils.cpp +++ /dev/null @@ -1,369 +0,0 @@ -/* 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 "DirectShowUtils.h" -#include "dmodshow.h" -#include "wmcodecdsp.h" -#include "dmoreg.h" -#include "mozilla/ArrayUtils.h" -#include "mozilla/RefPtr.h" -#include "nsPrintfCString.h" - -#define WARN(...) NS_WARNING(nsPrintfCString(__VA_ARGS__).get()) - -namespace mozilla { - -// Create a table which maps GUIDs to a string representation of the GUID. -// This is useful for debugging purposes, for logging the GUIDs of media types. -// This is only available when logging is enabled, i.e. not in release builds. -struct GuidToName { - const char* name; - const GUID guid; -}; - -#pragma push_macro("OUR_GUID_ENTRY") -#undef OUR_GUID_ENTRY -#define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ - { #name, {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} }, - -static const GuidToName GuidToNameTable[] = { -#include -}; - -#pragma pop_macro("OUR_GUID_ENTRY") - -const char* -GetDirectShowGuidName(const GUID& aGuid) -{ - const size_t len = ArrayLength(GuidToNameTable); - for (unsigned i = 0; i < len; i++) { - if (IsEqualGUID(aGuid, GuidToNameTable[i].guid)) { - return GuidToNameTable[i].name; - } - } - return "Unknown"; -} - -void -RemoveGraphFromRunningObjectTable(DWORD aRotRegister) -{ - RefPtr runningObjectTable; - if (SUCCEEDED(GetRunningObjectTable(0, getter_AddRefs(runningObjectTable)))) { - runningObjectTable->Revoke(aRotRegister); - } -} - -HRESULT -AddGraphToRunningObjectTable(IUnknown *aUnkGraph, DWORD *aOutRotRegister) -{ - HRESULT hr; - - RefPtr moniker; - RefPtr runningObjectTable; - - hr = GetRunningObjectTable(0, getter_AddRefs(runningObjectTable)); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - const size_t STRING_LENGTH = 256; - WCHAR wsz[STRING_LENGTH]; - - StringCchPrintfW(wsz, - STRING_LENGTH, - L"FilterGraph %08x pid %08x", - (DWORD_PTR)aUnkGraph, - GetCurrentProcessId()); - - hr = CreateItemMoniker(L"!", wsz, getter_AddRefs(moniker)); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - hr = runningObjectTable->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, - aUnkGraph, - moniker, - aOutRotRegister); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - return S_OK; -} - -const char* -GetGraphNotifyString(long evCode) -{ -#define CASE(x) case x: return #x - switch(evCode) { - CASE(EC_ACTIVATE); // A video window is being activated or deactivated. - CASE(EC_BANDWIDTHCHANGE); // Not supported. - CASE(EC_BUFFERING_DATA); // The graph is buffering data, or has stopped buffering data. - CASE(EC_BUILT); // Send by the Video Control when a graph has been built. Not forwarded to applications. - CASE(EC_CLOCK_CHANGED); // The reference clock has changed. - CASE(EC_CLOCK_UNSET); // The clock provider was disconnected. - CASE(EC_CODECAPI_EVENT); // Sent by an encoder to signal an encoding event. - CASE(EC_COMPLETE); // All data from a particular stream has been rendered. - CASE(EC_CONTENTPROPERTY_CHANGED); // Not supported. - CASE(EC_DEVICE_LOST); // A Plug and Play device was removed or has become available again. - CASE(EC_DISPLAY_CHANGED); // The display mode has changed. - CASE(EC_END_OF_SEGMENT); // The end of a segment has been reached. - CASE(EC_EOS_SOON); // Not supported. - CASE(EC_ERROR_STILLPLAYING); // An asynchronous command to run the graph has failed. - CASE(EC_ERRORABORT); // An operation was aborted because of an error. - CASE(EC_ERRORABORTEX); // An operation was aborted because of an error. - CASE(EC_EXTDEVICE_MODE_CHANGE); // Not supported. - CASE(EC_FILE_CLOSED); // The source file was closed because of an unexpected event. - CASE(EC_FULLSCREEN_LOST); // The video renderer is switching out of full-screen mode. - CASE(EC_GRAPH_CHANGED); // The filter graph has changed. - CASE(EC_LENGTH_CHANGED); // The length of a source has changed. - CASE(EC_LOADSTATUS); // Notifies the application of progress when opening a network file. - CASE(EC_MARKER_HIT); // Not supported. - CASE(EC_NEED_RESTART); // A filter is requesting that the graph be restarted. - CASE(EC_NEW_PIN); // Not supported. - CASE(EC_NOTIFY_WINDOW); // Notifies a filter of the video renderer's window. - CASE(EC_OLE_EVENT); // A filter is passing a text string to the application. - CASE(EC_OPENING_FILE); // The graph is opening a file, or has finished opening a file. - CASE(EC_PALETTE_CHANGED); // The video palette has changed. - CASE(EC_PAUSED); // A pause request has completed. - CASE(EC_PLEASE_REOPEN); // The source file has changed. - CASE(EC_PREPROCESS_COMPLETE); // Sent by the WM ASF Writer filter when it completes the pre-processing for multipass encoding. - CASE(EC_PROCESSING_LATENCY); // Indicates the amount of time that a component is taking to process each sample. - CASE(EC_QUALITY_CHANGE); // The graph is dropping samples, for quality control. - //CASE(EC_RENDER_FINISHED); // Not supported. - CASE(EC_REPAINT); // A video renderer requires a repaint. - CASE(EC_SAMPLE_LATENCY); // Specifies how far behind schedule a component is for processing samples. - //CASE(EC_SAMPLE_NEEDED); // Requests a new input sample from the Enhanced Video Renderer (EVR) filter. - CASE(EC_SCRUB_TIME); // Specifies the time stamp for the most recent frame step. - CASE(EC_SEGMENT_STARTED); // A new segment has started. - CASE(EC_SHUTTING_DOWN); // The filter graph is shutting down, prior to being destroyed. - CASE(EC_SNDDEV_IN_ERROR); // A device error has occurred in an audio capture filter. - CASE(EC_SNDDEV_OUT_ERROR); // A device error has occurred in an audio renderer filter. - CASE(EC_STARVATION); // A filter is not receiving enough data. - CASE(EC_STATE_CHANGE); // The filter graph has changed state. - CASE(EC_STATUS); // Contains two arbitrary status strings. - CASE(EC_STEP_COMPLETE); // A filter performing frame stepping has stepped the specified number of frames. - CASE(EC_STREAM_CONTROL_STARTED); // A stream-control start command has taken effect. - CASE(EC_STREAM_CONTROL_STOPPED); // A stream-control stop command has taken effect. - CASE(EC_STREAM_ERROR_STILLPLAYING); // An error has occurred in a stream. The stream is still playing. - CASE(EC_STREAM_ERROR_STOPPED); // A stream has stopped because of an error. - CASE(EC_TIMECODE_AVAILABLE); // Not supported. - CASE(EC_UNBUILT); // Send by the Video Control when a graph has been torn down. Not forwarded to applications. - CASE(EC_USERABORT); // The user has terminated playback. - CASE(EC_VIDEO_SIZE_CHANGED); // The native video size has changed. - CASE(EC_VIDEOFRAMEREADY); // A video frame is ready for display. - CASE(EC_VMR_RECONNECTION_FAILED); // Sent by the VMR-7 and the VMR-9 when it was unable to accept a dynamic format change request from the upstream decoder. - CASE(EC_VMR_RENDERDEVICE_SET); // Sent when the VMR has selected its rendering mechanism. - CASE(EC_VMR_SURFACE_FLIPPED); // Sent when the VMR-7's allocator presenter has called the DirectDraw Flip method on the surface being presented. - CASE(EC_WINDOW_DESTROYED); // The video renderer was destroyed or removed from the graph. - CASE(EC_WMT_EVENT); // Sent by the WM ASF Reader filter when it reads ASF files protected by digital rights management (DRM). - CASE(EC_WMT_INDEX_EVENT); // Sent when an application uses the WM ASF Writer to index Windows Media Video files. - CASE(S_OK); // Success. - CASE(VFW_S_AUDIO_NOT_RENDERED); // Partial success; the audio was not rendered. - CASE(VFW_S_DUPLICATE_NAME); // Success; the Filter Graph Manager modified a filter name to avoid duplication. - CASE(VFW_S_PARTIAL_RENDER); // Partial success; some of the streams in this movie are in an unsupported format. - CASE(VFW_S_VIDEO_NOT_RENDERED); // Partial success; the video was not rendered. - CASE(E_ABORT); // Operation aborted. - CASE(E_OUTOFMEMORY); // Insufficient memory. - CASE(E_POINTER); // Null pointer argument. - CASE(VFW_E_CANNOT_CONNECT); // No combination of intermediate filters could be found to make the connection. - CASE(VFW_E_CANNOT_RENDER); // No combination of filters could be found to render the stream. - CASE(VFW_E_NO_ACCEPTABLE_TYPES); // There is no common media type between these pins. - CASE(VFW_E_NOT_IN_GRAPH); - - default: - return "Unknown Code"; - }; -#undef CASE -} - -HRESULT -CreateAndAddFilter(IGraphBuilder* aGraph, - REFGUID aFilterClsId, - LPCWSTR aFilterName, - IBaseFilter **aOutFilter) -{ - NS_ENSURE_TRUE(aGraph, E_POINTER); - NS_ENSURE_TRUE(aOutFilter, E_POINTER); - HRESULT hr; - - RefPtr filter; - hr = CoCreateInstance(aFilterClsId, - nullptr, - CLSCTX_INPROC_SERVER, - IID_IBaseFilter, - getter_AddRefs(filter)); - if (FAILED(hr)) { - // Object probably not available on this system. - WARN("CoCreateInstance failed, hr=%x", hr); - return hr; - } - - hr = aGraph->AddFilter(filter, aFilterName); - if (FAILED(hr)) { - WARN("AddFilter failed, hr=%x", hr); - return hr; - } - - filter.forget(aOutFilter); - - return S_OK; -} - -HRESULT -CreateMP3DMOWrapperFilter(IBaseFilter **aOutFilter) -{ - NS_ENSURE_TRUE(aOutFilter, E_POINTER); - HRESULT hr; - - // Create the wrapper filter. - RefPtr filter; - hr = CoCreateInstance(CLSID_DMOWrapperFilter, - nullptr, - CLSCTX_INPROC_SERVER, - IID_IBaseFilter, - getter_AddRefs(filter)); - if (FAILED(hr)) { - WARN("CoCreateInstance failed, hr=%x", hr); - return hr; - } - - // Query for IDMOWrapperFilter. - RefPtr dmoWrapper; - hr = filter->QueryInterface(IID_IDMOWrapperFilter, - getter_AddRefs(dmoWrapper)); - if (FAILED(hr)) { - WARN("QueryInterface failed, hr=%x", hr); - return hr; - } - - hr = dmoWrapper->Init(CLSID_CMP3DecMediaObject, DMOCATEGORY_AUDIO_DECODER); - if (FAILED(hr)) { - // Can't instantiate MP3 DMO. It doesn't exist on Windows XP, we're - // probably hitting that. Don't log warning to console, this is an - // expected error. - WARN("dmoWrapper Init failed, hr=%x", hr); - return hr; - } - - filter.forget(aOutFilter); - - return S_OK; -} - -HRESULT -AddMP3DMOWrapperFilter(IGraphBuilder* aGraph, - IBaseFilter **aOutFilter) -{ - NS_ENSURE_TRUE(aGraph, E_POINTER); - NS_ENSURE_TRUE(aOutFilter, E_POINTER); - HRESULT hr; - - // Create the wrapper filter. - RefPtr filter; - hr = CreateMP3DMOWrapperFilter(getter_AddRefs(filter)); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - // Add the wrapper filter to graph. - hr = aGraph->AddFilter(filter, L"MP3 Decoder DMO"); - if (FAILED(hr)) { - WARN("AddFilter failed, hr=%x", hr); - return hr; - } - - filter.forget(aOutFilter); - - return S_OK; -} - -bool -CanDecodeMP3UsingDirectShow() -{ - RefPtr filter; - - // Can we create the MP3 demuxer filter? - if (FAILED(CoCreateInstance(CLSID_MPEG1Splitter, - nullptr, - CLSCTX_INPROC_SERVER, - IID_IBaseFilter, - getter_AddRefs(filter)))) { - return false; - } - - // Can we create either the WinXP MP3 decoder filter or the MP3 DMO decoder? - if (FAILED(CoCreateInstance(DirectShowReader::CLSID_MPEG_LAYER_3_DECODER_FILTER, - nullptr, - CLSCTX_INPROC_SERVER, - IID_IBaseFilter, - getter_AddRefs(filter))) && - FAILED(CreateMP3DMOWrapperFilter(getter_AddRefs(filter)))) { - return false; - } - - // Else, we can create all of the components we need. Assume - // DirectShow is going to work... - return true; -} - -// Match a pin by pin direction and connection state. -HRESULT -MatchUnconnectedPin(IPin* aPin, - PIN_DIRECTION aPinDir, - bool *aOutMatches) -{ - NS_ENSURE_TRUE(aPin, E_POINTER); - NS_ENSURE_TRUE(aOutMatches, E_POINTER); - - // Ensure the pin is unconnected. - RefPtr peer; - HRESULT hr = aPin->ConnectedTo(getter_AddRefs(peer)); - if (hr != VFW_E_NOT_CONNECTED) { - *aOutMatches = false; - return hr; - } - - // Ensure the pin is of the specified direction. - PIN_DIRECTION pinDir; - hr = aPin->QueryDirection(&pinDir); - NS_ENSURE_TRUE(SUCCEEDED(hr), hr); - - *aOutMatches = (pinDir == aPinDir); - return S_OK; -} - -// Return the first unconnected input pin or output pin. -already_AddRefed -GetUnconnectedPin(IBaseFilter* aFilter, PIN_DIRECTION aPinDir) -{ - RefPtr enumPins; - - HRESULT hr = aFilter->EnumPins(getter_AddRefs(enumPins)); - NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); - - // Test each pin to see if it matches the direction we're looking for. - RefPtr pin; - while (S_OK == enumPins->Next(1, getter_AddRefs(pin), nullptr)) { - bool matches = FALSE; - if (SUCCEEDED(MatchUnconnectedPin(pin, aPinDir, &matches)) && - matches) { - return pin.forget(); - } - } - - return nullptr; -} - -HRESULT -ConnectFilters(IGraphBuilder* aGraph, - IBaseFilter* aOutputFilter, - IBaseFilter* aInputFilter) -{ - RefPtr output = GetUnconnectedPin(aOutputFilter, PINDIR_OUTPUT); - NS_ENSURE_TRUE(output, E_FAIL); - - RefPtr input = GetUnconnectedPin(aInputFilter, PINDIR_INPUT); - NS_ENSURE_TRUE(output, E_FAIL); - - return aGraph->Connect(output, input); -} - -} // namespace mozilla - -// avoid redefined macro in unified build -#undef WARN diff --git a/dom/media/directshow/DirectShowUtils.h b/dom/media/directshow/DirectShowUtils.h deleted file mode 100644 index 3bbc122fc..000000000 --- a/dom/media/directshow/DirectShowUtils.h +++ /dev/null @@ -1,125 +0,0 @@ -/* 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/. */ - -#ifndef _DirectShowUtils_h_ -#define _DirectShowUtils_h_ - -#include -#include "dshow.h" - -// XXXbz windowsx.h defines GetFirstChild, GetNextSibling, -// GetPrevSibling are macros, apparently... Eeevil. We have functions -// called that on some classes, so undef them. -#undef GetFirstChild -#undef GetNextSibling -#undef GetPrevSibling - -#include "DShowTools.h" -#include "mozilla/Logging.h" - -namespace mozilla { - -// Win32 "Event" wrapper. Must be paired with a CriticalSection to create a -// Java-style "monitor". -class Signal { -public: - - Signal(CriticalSection* aLock) - : mLock(aLock) - { - CriticalSectionAutoEnter lock(*mLock); - mEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - } - - ~Signal() { - CriticalSectionAutoEnter lock(*mLock); - CloseHandle(mEvent); - } - - // Lock must be held. - void Notify() { - SetEvent(mEvent); - } - - // Lock must be held. Check the wait condition before waiting! - HRESULT Wait() { - mLock->Leave(); - DWORD result = WaitForSingleObject(mEvent, INFINITE); - mLock->Enter(); - return result == WAIT_OBJECT_0 ? S_OK : E_FAIL; - } - -private: - CriticalSection* mLock; - HANDLE mEvent; -}; - -HRESULT -AddGraphToRunningObjectTable(IUnknown *aUnkGraph, DWORD *aOutRotRegister); - -void -RemoveGraphFromRunningObjectTable(DWORD aRotRegister); - -const char* -GetGraphNotifyString(long evCode); - -// Creates a filter and adds it to a graph. -HRESULT -CreateAndAddFilter(IGraphBuilder* aGraph, - REFGUID aFilterClsId, - LPCWSTR aFilterName, - IBaseFilter **aOutFilter); - -HRESULT -AddMP3DMOWrapperFilter(IGraphBuilder* aGraph, - IBaseFilter **aOutFilter); - -// Connects the output pin on aOutputFilter to an input pin on -// aInputFilter, in aGraph. -HRESULT -ConnectFilters(IGraphBuilder* aGraph, - IBaseFilter* aOutputFilter, - IBaseFilter* aInputFilter); - -HRESULT -MatchUnconnectedPin(IPin* aPin, - PIN_DIRECTION aPinDir, - bool *aOutMatches); - -// Converts from microseconds to DirectShow "Reference Time" -// (hundreds of nanoseconds). -inline int64_t -UsecsToRefTime(const int64_t aUsecs) -{ - return aUsecs * 10; -} - -// Converts from DirectShow "Reference Time" (hundreds of nanoseconds) -// to microseconds. -inline int64_t -RefTimeToUsecs(const int64_t hRefTime) -{ - return hRefTime / 10; -} - -// Converts from DirectShow "Reference Time" (hundreds of nanoseconds) -// to seconds. -inline double -RefTimeToSeconds(const REFERENCE_TIME aRefTime) -{ - return double(aRefTime) / 10000000; -} - -const char* -GetDirectShowGuidName(const GUID& aGuid); - -// Returns true if we can instantiate an MP3 demuxer and decoder filters. -// Use this to detect whether MP3 support is installed. -bool -CanDecodeMP3UsingDirectShow(); - -} // namespace mozilla - -#endif diff --git a/dom/media/directshow/SampleSink.cpp b/dom/media/directshow/SampleSink.cpp deleted file mode 100644 index fa5dc8d19..000000000 --- a/dom/media/directshow/SampleSink.cpp +++ /dev/null @@ -1,159 +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 "SampleSink.h" -#include "AudioSinkFilter.h" -#include "AudioSinkInputPin.h" -#include "VideoUtils.h" -#include "mozilla/Logging.h" - -using namespace mozilla::media; - -namespace mozilla { - -static LazyLogModule gDirectShowLog("DirectShowDecoder"); -#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) - -SampleSink::SampleSink() - : mMonitor("SampleSink"), - mIsFlushing(false), - mAtEOS(false) -{ - MOZ_COUNT_CTOR(SampleSink); -} - -SampleSink::~SampleSink() -{ - MOZ_COUNT_DTOR(SampleSink); -} - -void -SampleSink::SetAudioFormat(const WAVEFORMATEX* aInFormat) -{ - NS_ENSURE_TRUE(aInFormat, ); - ReentrantMonitorAutoEnter mon(mMonitor); - memcpy(&mAudioFormat, aInFormat, sizeof(WAVEFORMATEX)); -} - -void -SampleSink::GetAudioFormat(WAVEFORMATEX* aOutFormat) -{ - MOZ_ASSERT(aOutFormat); - ReentrantMonitorAutoEnter mon(mMonitor); - memcpy(aOutFormat, &mAudioFormat, sizeof(WAVEFORMATEX)); -} - -HRESULT -SampleSink::Receive(IMediaSample* aSample) -{ - ReentrantMonitorAutoEnter mon(mMonitor); - - while (true) { - if (mIsFlushing) { - return S_FALSE; - } - if (!mSample) { - break; - } - if (mAtEOS) { - return E_UNEXPECTED; - } - // Wait until the consumer thread consumes the sample. - mon.Wait(); - } - - if (MOZ_LOG_TEST(gDirectShowLog, LogLevel::Debug)) { - REFERENCE_TIME start = 0, end = 0; - HRESULT hr = aSample->GetMediaTime(&start, &end); - LOG("SampleSink::Receive() [%4.2lf-%4.2lf]", - (double)RefTimeToUsecs(start) / USECS_PER_S, - (double)RefTimeToUsecs(end) / USECS_PER_S); - } - - mSample = aSample; - // Notify the signal, to awaken the consumer thread in WaitForSample() - // if necessary. - mon.NotifyAll(); - return S_OK; -} - -HRESULT -SampleSink::Extract(RefPtr& aOutSample) -{ - ReentrantMonitorAutoEnter mon(mMonitor); - // Loop until we have a sample, or we should abort. - while (true) { - if (mIsFlushing) { - return S_FALSE; - } - if (mSample) { - break; - } - if (mAtEOS) { - // Order is important here, if we have a sample, we should return it - // before reporting EOS. - return E_UNEXPECTED; - } - // Wait until the producer thread gives us a sample. - mon.Wait(); - } - aOutSample = mSample; - - if (MOZ_LOG_TEST(gDirectShowLog, LogLevel::Debug)) { - int64_t start = 0, end = 0; - mSample->GetMediaTime(&start, &end); - LOG("SampleSink::Extract() [%4.2lf-%4.2lf]", - (double)RefTimeToUsecs(start) / USECS_PER_S, - (double)RefTimeToUsecs(end) / USECS_PER_S); - } - - mSample = nullptr; - // Notify the signal, to awaken the producer thread in Receive() - // if necessary. - mon.NotifyAll(); - return S_OK; -} - -void -SampleSink::Flush() -{ - LOG("SampleSink::Flush()"); - ReentrantMonitorAutoEnter mon(mMonitor); - mIsFlushing = true; - mSample = nullptr; - mon.NotifyAll(); -} - -void -SampleSink::Reset() -{ - LOG("SampleSink::Reset()"); - ReentrantMonitorAutoEnter mon(mMonitor); - mIsFlushing = false; - mAtEOS = false; -} - -void -SampleSink::SetEOS() -{ - LOG("SampleSink::SetEOS()"); - ReentrantMonitorAutoEnter mon(mMonitor); - mAtEOS = true; - // Notify to unblock any threads waiting for samples in - // Extract() or Receive(). Now that we're at EOS, no more samples - // will come! - mon.NotifyAll(); -} - -bool -SampleSink::AtEOS() -{ - ReentrantMonitorAutoEnter mon(mMonitor); - return mAtEOS && !mSample; -} - -} // namespace mozilla - diff --git a/dom/media/directshow/SampleSink.h b/dom/media/directshow/SampleSink.h deleted file mode 100644 index 6a1af9fee..000000000 --- a/dom/media/directshow/SampleSink.h +++ /dev/null @@ -1,67 +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(SampleSink_h_) -#define SampleSink_h_ - -#include "BaseFilter.h" -#include "DirectShowUtils.h" -#include "mozilla/RefPtr.h" -#include "mozilla/ReentrantMonitor.h" - -namespace mozilla { - -class SampleSink { -public: - SampleSink(); - virtual ~SampleSink(); - - // Sets the audio format of the incoming samples. The upstream filter - // calls this. This makes a copy. - void SetAudioFormat(const WAVEFORMATEX* aInFormat); - - // Copies the format of incoming audio samples into into *aOutFormat. - void GetAudioFormat(WAVEFORMATEX* aOutFormat); - - // Called when a sample is delivered by the DirectShow graph to the sink. - // The decode thread retrieves the sample by calling WaitForSample(). - // Blocks if there's already a sample waiting to be consumed by the decode - // thread. - HRESULT Receive(IMediaSample* aSample); - - // Retrieves a sample from the sample queue, blocking until one becomes - // available, or until an error occurs. Returns S_FALSE on EOS. - HRESULT Extract(RefPtr& aOutSample); - - // Unblocks any threads waiting in GetSample(). - // Clears mSample, which unblocks upstream stream. - void Flush(); - - // Opens up the sink to receive more samples in PutSample(). - // Clears EOS flag. - void Reset(); - - // Marks that we've reacehd the end of stream. - void SetEOS(); - - // Returns whether we're at end of stream. - bool AtEOS(); - -private: - // All data in this class is syncronized by mMonitor. - ReentrantMonitor mMonitor; - RefPtr mSample; - - // Format of the audio stream we're receiving. - WAVEFORMATEX mAudioFormat; - - bool mIsFlushing; - bool mAtEOS; -}; - -} // namespace mozilla - -#endif // SampleSink_h_ diff --git a/dom/media/directshow/SourceFilter.cpp b/dom/media/directshow/SourceFilter.cpp deleted file mode 100644 index 4c5a0882c..000000000 --- a/dom/media/directshow/SourceFilter.cpp +++ /dev/null @@ -1,683 +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 "SourceFilter.h" -#include "MediaResource.h" -#include "mozilla/RefPtr.h" -#include "DirectShowUtils.h" -#include "MP3FrameParser.h" -#include "mozilla/Logging.h" -#include - -using namespace mozilla::media; - -namespace mozilla { - -// Define to trace what's on... -//#define DEBUG_SOURCE_TRACE 1 - -#if defined (DEBUG_SOURCE_TRACE) -static LazyLogModule gDirectShowLog("DirectShowDecoder"); -#define DIRECTSHOW_LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) -#else -#define DIRECTSHOW_LOG(...) -#endif - -static HRESULT -DoGetInterface(IUnknown* aUnknown, void** aInterface) -{ - if (!aInterface) - return E_POINTER; - *aInterface = aUnknown; - aUnknown->AddRef(); - return S_OK; -} - -// Stores details of IAsyncReader::Request(). -class ReadRequest { -public: - - ReadRequest(IMediaSample* aSample, - DWORD_PTR aDwUser, - uint32_t aOffset, - uint32_t aCount) - : mSample(aSample), - mDwUser(aDwUser), - mOffset(aOffset), - mCount(aCount) - { - MOZ_COUNT_CTOR(ReadRequest); - } - - ~ReadRequest() { - MOZ_COUNT_DTOR(ReadRequest); - } - - RefPtr mSample; - DWORD_PTR mDwUser; - uint32_t mOffset; - uint32_t mCount; -}; - -// A wrapper around media resource that presents only a partition of the -// underlying resource to the caller to use. The partition returned is from -// an offset to the end of stream, and this object deals with ensuring -// the offsets and lengths etc are translated from the reduced partition -// exposed to the caller, to the absolute offsets of the underlying stream. -class MediaResourcePartition { -public: - MediaResourcePartition(MediaResource* aResource, - int64_t aDataStart) - : mResource(aResource), - mDataOffset(aDataStart) - {} - - int64_t GetLength() { - int64_t len = mResource.GetLength(); - if (len == -1) { - return len; - } - return std::max(0, len - mDataOffset); - } - nsresult ReadAt(int64_t aOffset, char* aBuffer, - uint32_t aCount, uint32_t* aBytes) - { - return mResource.ReadAt(aOffset + mDataOffset, - aBuffer, - aCount, - aBytes); - } - int64_t GetCachedDataEnd() { - int64_t tell = mResource.GetResource()->Tell(); - int64_t dataEnd = - mResource.GetResource()->GetCachedDataEnd(tell) - mDataOffset; - return dataEnd; - } -private: - // MediaResource from which we read data. - MediaResourceIndex mResource; - int64_t mDataOffset; -}; - - -// Output pin for SourceFilter, which implements IAsyncReader, to -// allow downstream filters to pull/read data from it. Downstream pins -// register to read data using Request(), and asynchronously wait for the -// reads to complete using WaitForNext(). They may also synchronously read -// using SyncRead(). This class is a delegate (tear off) of -// SourceFilter. -// -// We can expose only a segment of the MediaResource to the filter graph. -// This is used to strip off the ID3v2 tags from the stream, as DirectShow -// has trouble parsing some headers. -// -// Implements: -// * IAsyncReader -// * IPin -// * IQualityControl -// * IUnknown -// -class DECLSPEC_UUID("18e5cfb2-1015-440c-a65c-e63853235894") -OutputPin : public IAsyncReader, - public BasePin -{ -public: - - OutputPin(MediaResource* aMediaResource, - SourceFilter* aParent, - CriticalSection& aFilterLock, - int64_t aMP3DataStart); - virtual ~OutputPin(); - - // IUnknown - // Defer to ref counting to BasePin, which defers to owning nsBaseFilter. - STDMETHODIMP_(ULONG) AddRef() override { return BasePin::AddRef(); } - STDMETHODIMP_(ULONG) Release() override { return BasePin::Release(); } - STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override; - - // BasePin Overrides. - // Determines if the pin accepts a specific media type. - HRESULT CheckMediaType(const MediaType* aMediaType) override; - - // Retrieves a preferred media type, by index value. - HRESULT GetMediaType(int aPosition, MediaType* aMediaType) override; - - // Releases the pin from a connection. - HRESULT BreakConnect(void) override; - - // Determines whether a pin connection is suitable. - HRESULT CheckConnect(IPin* aPin) override; - - - // IAsyncReader overrides - - // The RequestAllocator method requests an allocator during the - // pin connection. - STDMETHODIMP RequestAllocator(IMemAllocator* aPreferred, - ALLOCATOR_PROPERTIES* aProps, - IMemAllocator** aActual) override; - - // The Request method queues an asynchronous request for data. Downstream - // will call WaitForNext() when they want to retrieve the result. - STDMETHODIMP Request(IMediaSample* aSample, DWORD_PTR aUserData) override; - - // The WaitForNext method waits for the next pending read request - // to complete. This method fails if the graph is flushing. - // Defers to SyncRead/5. - STDMETHODIMP WaitForNext(DWORD aTimeout, - IMediaSample** aSamples, - DWORD_PTR* aUserData) override; - - // The SyncReadAligned method performs a synchronous read. The method - // blocks until the request is completed. Defers to SyncRead/5. This - // method does not fail if the graph is flushing. - STDMETHODIMP SyncReadAligned(IMediaSample* aSample) override; - - // The SyncRead method performs a synchronous read. The method blocks - // until the request is completed. Defers to SyncRead/5. This - // method does not fail if the graph is flushing. - STDMETHODIMP SyncRead(LONGLONG aPosition, LONG aLength, BYTE* aBuffer) override; - - // The Length method retrieves the total length of the stream. - STDMETHODIMP Length(LONGLONG* aTotal, LONGLONG* aAvailable) override; - - // IPin Overrides - STDMETHODIMP BeginFlush(void) override; - STDMETHODIMP EndFlush(void) override; - - uint32_t GetAndResetBytesConsumedCount(); - -private: - - // Protects thread-shared data/structures (mFlushCount, mPendingReads). - // WaitForNext() also waits on this monitor - CriticalSection& mPinLock; - - // Signal used with mPinLock to implement WaitForNext(). - Signal mSignal; - - // The filter that owns us. Weak reference, as we're a delegate (tear off). - SourceFilter* mParentSource; - - MediaResourcePartition mResource; - - // Counter, inc'd in BeginFlush(), dec'd in EndFlush(). Calls to this can - // come from multiple threads and can interleave, hence the counter. - int32_t mFlushCount; - - // Number of bytes that have been read from the output pin since the last - // time GetAndResetBytesConsumedCount() was called. - uint32_t mBytesConsumed; - - // Deque of ReadRequest* for reads that are yet to be serviced. - // nsReadRequest's are stored on the heap, popper must delete them. - nsDeque mPendingReads; - - // Flags if the downstream pin has QI'd for IAsyncReader. We refuse - // connection if they don't query, as it means they're assuming that we're - // a push filter, and we're not. - bool mQueriedForAsyncReader; - -}; - -// For mingw __uuidof support -#ifdef __CRT_UUID_DECL -} -__CRT_UUID_DECL(mozilla::OutputPin, 0x18e5cfb2,0x1015,0x440c,0xa6,0x5c,0xe6,0x38,0x53,0x23,0x58,0x94); -namespace mozilla { -#endif - -OutputPin::OutputPin(MediaResource* aResource, - SourceFilter* aParent, - CriticalSection& aFilterLock, - int64_t aMP3DataStart) - : BasePin(static_cast(aParent), - &aFilterLock, - L"MozillaOutputPin", - PINDIR_OUTPUT), - mPinLock(aFilterLock), - mSignal(&mPinLock), - mParentSource(aParent), - mResource(aResource, aMP3DataStart), - mFlushCount(0), - mBytesConsumed(0), - mQueriedForAsyncReader(false) -{ - MOZ_COUNT_CTOR(OutputPin); - DIRECTSHOW_LOG("OutputPin::OutputPin()"); -} - -OutputPin::~OutputPin() -{ - MOZ_COUNT_DTOR(OutputPin); - DIRECTSHOW_LOG("OutputPin::~OutputPin()"); -} - -HRESULT -OutputPin::BreakConnect() -{ - mQueriedForAsyncReader = false; - return BasePin::BreakConnect(); -} - -STDMETHODIMP -OutputPin::QueryInterface(REFIID aIId, void** aInterface) -{ - if (aIId == IID_IAsyncReader) { - mQueriedForAsyncReader = true; - return DoGetInterface(static_cast(this), aInterface); - } - - if (aIId == __uuidof(OutputPin)) { - AddRef(); - *aInterface = this; - return S_OK; - } - - return BasePin::QueryInterface(aIId, aInterface); -} - -HRESULT -OutputPin::CheckConnect(IPin* aPin) -{ - // Our connection is only suitable if the downstream pin knows - // that we're asynchronous (i.e. it queried for IAsyncReader). - return mQueriedForAsyncReader ? S_OK : S_FALSE; -} - -HRESULT -OutputPin::CheckMediaType(const MediaType* aMediaType) -{ - const MediaType *myMediaType = mParentSource->GetMediaType(); - - if (IsEqualGUID(aMediaType->majortype, myMediaType->majortype) && - IsEqualGUID(aMediaType->subtype, myMediaType->subtype) && - IsEqualGUID(aMediaType->formattype, myMediaType->formattype)) - { - DIRECTSHOW_LOG("OutputPin::CheckMediaType() Match: major=%s minor=%s TC=%d FSS=%d SS=%u", - GetDirectShowGuidName(aMediaType->majortype), - GetDirectShowGuidName(aMediaType->subtype), - aMediaType->TemporalCompression(), - aMediaType->bFixedSizeSamples, - aMediaType->SampleSize()); - return S_OK; - } - - DIRECTSHOW_LOG("OutputPin::CheckMediaType() Failed to match: major=%s minor=%s TC=%d FSS=%d SS=%u", - GetDirectShowGuidName(aMediaType->majortype), - GetDirectShowGuidName(aMediaType->subtype), - aMediaType->TemporalCompression(), - aMediaType->bFixedSizeSamples, - aMediaType->SampleSize()); - return S_FALSE; -} - -HRESULT -OutputPin::GetMediaType(int aPosition, MediaType* aMediaType) -{ - if (!aMediaType) - return E_POINTER; - - if (aPosition == 0) { - aMediaType->Assign(mParentSource->GetMediaType()); - return S_OK; - } - return VFW_S_NO_MORE_ITEMS; -} - -static inline bool -IsPowerOf2(int32_t x) { - return ((-x & x) != x); -} - -STDMETHODIMP -OutputPin::RequestAllocator(IMemAllocator* aPreferred, - ALLOCATOR_PROPERTIES* aProps, - IMemAllocator** aActual) -{ - // Require the downstream pin to suggest what they want... - if (!aPreferred) return E_POINTER; - if (!aProps) return E_POINTER; - if (!aActual) return E_POINTER; - - // We only care about alignment - our allocator will reject anything - // which isn't power-of-2 aligned, so so try a 4-byte aligned allocator. - ALLOCATOR_PROPERTIES props; - memcpy(&props, aProps, sizeof(ALLOCATOR_PROPERTIES)); - if (aProps->cbAlign == 0 || IsPowerOf2(aProps->cbAlign)) { - props.cbAlign = 4; - } - - // Limit allocator's number of buffers. We know that the media will most - // likely be bound by network speed, not by decoding speed. We also - // store the incoming data in a Gecko stream, if we don't limit buffers - // here we'll end up duplicating a lot of storage. We must have enough - // space for audio key frames to fit in the first batch of buffers however, - // else pausing may fail for some downstream decoders. - if (props.cBuffers > BaseFilter::sMaxNumBuffers) { - props.cBuffers = BaseFilter::sMaxNumBuffers; - } - - // The allocator properties that are actually used. We don't store - // this, we need it for SetProperties() below to succeed. - ALLOCATOR_PROPERTIES actualProps; - HRESULT hr; - - if (aPreferred) { - // Play nice and prefer the downstream pin's preferred allocator. - hr = aPreferred->SetProperties(&props, &actualProps); - if (SUCCEEDED(hr)) { - aPreferred->AddRef(); - *aActual = aPreferred; - return S_OK; - } - } - - // Else downstream hasn't requested a specific allocator, so create one... - - // Just create a default allocator. It's highly unlikely that we'll use - // this anyway, as most parsers insist on using their own allocators. - RefPtr allocator; - hr = CoCreateInstance(CLSID_MemoryAllocator, - 0, - CLSCTX_INPROC_SERVER, - IID_IMemAllocator, - getter_AddRefs(allocator)); - if(FAILED(hr) || (allocator == nullptr)) { - NS_WARNING("Can't create our own DirectShow allocator."); - return hr; - } - - // See if we can make it suitable - hr = allocator->SetProperties(&props, &actualProps); - if (SUCCEEDED(hr)) { - // We need to release our refcount on pAlloc, and addref - // it to pass a refcount to the caller - this is a net nothing. - allocator.forget(aActual); - return S_OK; - } - - NS_WARNING("Failed to pick an allocator"); - return hr; -} - -STDMETHODIMP -OutputPin::Request(IMediaSample* aSample, DWORD_PTR aDwUser) -{ - if (!aSample) return E_FAIL; - - CriticalSectionAutoEnter lock(*mLock); - NS_ASSERTION(!mFlushCount, "Request() while flushing"); - - if (mFlushCount) - return VFW_E_WRONG_STATE; - - REFERENCE_TIME refStart = 0, refEnd = 0; - if (FAILED(aSample->GetTime(&refStart, &refEnd))) { - NS_WARNING("Sample incorrectly timestamped"); - return VFW_E_SAMPLE_TIME_NOT_SET; - } - - // Convert reference time to bytes. - uint32_t start = (uint32_t)(refStart / 10000000); - uint32_t end = (uint32_t)(refEnd / 10000000); - - uint32_t numBytes = end - start; - - ReadRequest* request = new ReadRequest(aSample, - aDwUser, - start, - numBytes); - // Memory for |request| is free when it's popped from the completed - // reads list. - - // Push this onto the queue of reads to be serviced. - mPendingReads.Push(request); - - // Notify any threads blocked in WaitForNext() which are waiting for mPendingReads - // to become non-empty. - mSignal.Notify(); - - return S_OK; -} - -STDMETHODIMP -OutputPin::WaitForNext(DWORD aTimeout, - IMediaSample** aOutSample, - DWORD_PTR* aOutDwUser) -{ - NS_ASSERTION(aTimeout == 0 || aTimeout == INFINITE, - "Oops, we don't handle this!"); - - *aOutSample = nullptr; - *aOutDwUser = 0; - - LONGLONG offset = 0; - LONG count = 0; - BYTE* buf = nullptr; - - { - CriticalSectionAutoEnter lock(*mLock); - - // Wait until there's a pending read to service. - while (aTimeout && mPendingReads.GetSize() == 0 && !mFlushCount) { - // Note: No need to guard against shutdown-during-wait here, as - // typically the thread doing the pull will have already called - // Request(), so we won't Wait() here anyway. SyncRead() will fail - // on shutdown. - mSignal.Wait(); - } - - nsAutoPtr request(reinterpret_cast(mPendingReads.PopFront())); - if (!request) - return VFW_E_WRONG_STATE; - - *aOutSample = request->mSample; - *aOutDwUser = request->mDwUser; - - offset = request->mOffset; - count = request->mCount; - buf = nullptr; - request->mSample->GetPointer(&buf); - NS_ASSERTION(buf != nullptr, "Invalid buffer!"); - - if (mFlushCount) { - return VFW_E_TIMEOUT; - } - } - - return SyncRead(offset, count, buf); -} - -STDMETHODIMP -OutputPin::SyncReadAligned(IMediaSample* aSample) -{ - { - // Ignore reads while flushing. - CriticalSectionAutoEnter lock(*mLock); - if (mFlushCount) { - return S_FALSE; - } - } - - if (!aSample) - return E_FAIL; - - REFERENCE_TIME lStart = 0, lEnd = 0; - if (FAILED(aSample->GetTime(&lStart, &lEnd))) { - NS_WARNING("Sample incorrectly timestamped"); - return VFW_E_SAMPLE_TIME_NOT_SET; - } - - // Convert reference time to bytes. - int32_t start = (int32_t)(lStart / 10000000); - int32_t end = (int32_t)(lEnd / 10000000); - - int32_t numBytes = end - start; - - // If the range extends off the end of stream, truncate to the end of stream - // as per IAsyncReader specificiation. - int64_t streamLength = mResource.GetLength(); - if (streamLength != -1) { - // We know the exact length of the stream, fail if the requested offset - // is beyond it. - if (start > streamLength) { - return VFW_E_BADALIGN; - } - - // If the end of the chunk to read is off the end of the stream, - // truncate it to the end of the stream. - if ((start + numBytes) > streamLength) { - numBytes = (uint32_t)(streamLength - start); - } - } - - BYTE* buf=0; - aSample->GetPointer(&buf); - - return SyncRead(start, numBytes, buf); -} - -STDMETHODIMP -OutputPin::SyncRead(LONGLONG aPosition, - LONG aLength, - BYTE* aBuffer) -{ - MOZ_ASSERT(!NS_IsMainThread()); - NS_ENSURE_TRUE(aPosition >= 0, E_FAIL); - NS_ENSURE_TRUE(aLength > 0, E_FAIL); - NS_ENSURE_TRUE(aBuffer, E_POINTER); - - DIRECTSHOW_LOG("OutputPin::SyncRead(%lld, %d)", aPosition, aLength); - { - // Ignore reads while flushing. - CriticalSectionAutoEnter lock(*mLock); - if (mFlushCount) { - return S_FALSE; - } - } - - uint32_t totalBytesRead = 0; - nsresult rv = mResource.ReadAt(aPosition, - reinterpret_cast(aBuffer), - aLength, - &totalBytesRead); - if (NS_FAILED(rv)) { - return E_FAIL; - } - if (totalBytesRead > 0) { - CriticalSectionAutoEnter lock(*mLock); - mBytesConsumed += totalBytesRead; - } - return (totalBytesRead == aLength) ? S_OK : S_FALSE; -} - -STDMETHODIMP -OutputPin::Length(LONGLONG* aTotal, LONGLONG* aAvailable) -{ - HRESULT hr = S_OK; - int64_t length = mResource.GetLength(); - if (length == -1) { - hr = VFW_S_ESTIMATED; - // Don't have a length. Just lie, it seems to work... - *aTotal = INT32_MAX; - } else { - *aTotal = length; - } - if (aAvailable) { - *aAvailable = mResource.GetCachedDataEnd(); - } - - DIRECTSHOW_LOG("OutputPin::Length() len=%lld avail=%lld", *aTotal, *aAvailable); - - return hr; -} - -STDMETHODIMP -OutputPin::BeginFlush() -{ - CriticalSectionAutoEnter lock(*mLock); - mFlushCount++; - mSignal.Notify(); - return S_OK; -} - -STDMETHODIMP -OutputPin::EndFlush(void) -{ - CriticalSectionAutoEnter lock(*mLock); - mFlushCount--; - return S_OK; -} - -uint32_t -OutputPin::GetAndResetBytesConsumedCount() -{ - CriticalSectionAutoEnter lock(*mLock); - uint32_t bytesConsumed = mBytesConsumed; - mBytesConsumed = 0; - return bytesConsumed; -} - -SourceFilter::SourceFilter(const GUID& aMajorType, - const GUID& aSubType) - : BaseFilter(L"MozillaDirectShowSource", __uuidof(SourceFilter)) -{ - MOZ_COUNT_CTOR(SourceFilter); - mMediaType.majortype = aMajorType; - mMediaType.subtype = aSubType; - - DIRECTSHOW_LOG("SourceFilter Constructor(%s, %s)", - GetDirectShowGuidName(aMajorType), - GetDirectShowGuidName(aSubType)); -} - -SourceFilter::~SourceFilter() -{ - MOZ_COUNT_DTOR(SourceFilter); - DIRECTSHOW_LOG("SourceFilter Destructor()"); -} - -BasePin* -SourceFilter::GetPin(int n) -{ - if (n == 0) { - NS_ASSERTION(mOutputPin != 0, "GetPin with no pin!"); - return static_cast(mOutputPin); - } else { - return nullptr; - } -} - -// Get's the media type we're supplying. -const MediaType* -SourceFilter::GetMediaType() const -{ - return &mMediaType; -} - -nsresult -SourceFilter::Init(MediaResource* aResource, int64_t aMP3Offset) -{ - DIRECTSHOW_LOG("SourceFilter::Init()"); - - mOutputPin = new OutputPin(aResource, - this, - mLock, - aMP3Offset); - NS_ENSURE_TRUE(mOutputPin != nullptr, NS_ERROR_FAILURE); - - return NS_OK; -} - -uint32_t -SourceFilter::GetAndResetBytesConsumedCount() -{ - return mOutputPin->GetAndResetBytesConsumedCount(); -} - - -} // namespace mozilla diff --git a/dom/media/directshow/SourceFilter.h b/dom/media/directshow/SourceFilter.h deleted file mode 100644 index d5ce2770e..000000000 --- a/dom/media/directshow/SourceFilter.h +++ /dev/null @@ -1,75 +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(nsDirectShowSource_h___) -#define nsDirectShowSource_h___ - -#include "BaseFilter.h" -#include "BasePin.h" -#include "MediaType.h" - -#include "nsDeque.h" -#include "nsAutoPtr.h" -#include "DirectShowUtils.h" -#include "mozilla/RefPtr.h" - -namespace mozilla { - -class MediaResource; -class OutputPin; - - -// SourceFilter is an asynchronous DirectShow source filter which -// reads from an MediaResource, and supplies data via a pull model downstream -// using OutputPin. It us used to supply a generic byte stream into -// DirectShow. -// -// Implements: -// * IBaseFilter -// * IMediaFilter -// * IPersist -// * IUnknown -// -class DECLSPEC_UUID("5c2a7ad0-ba82-4659-9178-c4719a2765d6") -SourceFilter : public media::BaseFilter -{ -public: - - // Constructs source filter to deliver given media type. - SourceFilter(const GUID& aMajorType, const GUID& aSubType); - ~SourceFilter(); - - nsresult Init(MediaResource *aResource, int64_t aMP3Offset); - - // BaseFilter overrides. - // Only one output - the byte stream. - int GetPinCount() override { return 1; } - - media::BasePin* GetPin(int n) override; - - // Get's the media type we're supplying. - const media::MediaType* GetMediaType() const; - - uint32_t GetAndResetBytesConsumedCount(); - -protected: - - // Our async pull output pin. - nsAutoPtr mOutputPin; - - // Type of byte stream we output. - media::MediaType mMediaType; - -}; - -} // namespace mozilla - -// For mingw __uuidof support -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(mozilla::SourceFilter, 0x5c2a7ad0,0xba82,0x4659,0x91,0x78,0xc4,0x71,0x9a,0x27,0x65,0xd6); -#endif - -#endif diff --git a/dom/media/directshow/moz.build b/dom/media/directshow/moz.build deleted file mode 100644 index 8a9b76200..000000000 --- a/dom/media/directshow/moz.build +++ /dev/null @@ -1,41 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXPORTS += [ - 'AudioSinkFilter.h', - 'AudioSinkInputPin.h', - 'DirectShowDecoder.h', - 'DirectShowReader.h', - 'DirectShowUtils.h', -] - -UNIFIED_SOURCES += [ - 'DirectShowDecoder.cpp', - 'DirectShowUtils.cpp', - 'SourceFilter.cpp', -] - -SOURCES += [ - 'AudioSinkFilter.cpp', - 'AudioSinkInputPin.cpp', - 'DirectShowReader.cpp', - 'SampleSink.cpp', -] - -# If WebRTC isn't being built, we need to compile the DirectShow base classes so that -# they're available at link time. -if not CONFIG['MOZ_WEBRTC']: - SOURCES += [ - '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseFilter.cpp', - '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseInputPin.cpp', - '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BasePin.cpp', - '/media/webrtc/trunk/webrtc/modules/video_capture/windows/MediaType.cpp', - ] - -FINAL_LIBRARY = 'xul' -LOCAL_INCLUDES += [ - '/media/webrtc/trunk/webrtc/modules/video_capture/windows', -] -- cgit v1.2.3 From caabbb2a094d69852ca7e9a4aca63f53ed517596 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sat, 1 Dec 2018 09:42:39 -0600 Subject: Remove DirectShowReader Part 4: Tests --- dom/media/test/manifest.js | 4 ++-- dom/media/test/test_can_play_type_mpeg.html | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'dom/media') diff --git a/dom/media/test/manifest.js b/dom/media/test/manifest.js index 7e30cc97d..52e53a271 100644 --- a/dom/media/test/manifest.js +++ b/dom/media/test/manifest.js @@ -266,10 +266,10 @@ var gPlayTests = [ { name:"small-shot.mp3", type:"audio/mpeg", duration:0.27 }, { name:"owl.mp3", type:"audio/mpeg", duration:3.343 }, // owl.mp3 as above, but with something funny going on in the ID3v2 tag - // that causes DirectShow to fail. + // that caused DirectShow to fail. { name:"owl-funny-id3.mp3", type:"audio/mpeg", duration:3.343 }, // owl.mp3 as above, but with something even funnier going on in the ID3v2 tag - // that causes DirectShow to fail. + // that caused DirectShow to fail. { name:"owl-funnier-id3.mp3", type:"audio/mpeg", duration:3.343 }, // One second of silence with ~140KB of ID3 tags. Usually when the first MP3 // frame is at such a high offset into the file, MP3FrameParser will give up diff --git a/dom/media/test/test_can_play_type_mpeg.html b/dom/media/test/test_can_play_type_mpeg.html index 89e5fabef..514b5cc2f 100644 --- a/dom/media/test/test_can_play_type_mpeg.html +++ b/dom/media/test/test_can_play_type_mpeg.html @@ -151,8 +151,7 @@ var haveMp4 = (getPref("media.wmf.enabled") && IsWindowsVistaOrLater()) || check_mp4(document.getElementById('v'), haveMp4); -var haveMp3 = getPref("media.directshow.enabled") || - (getPref("media.wmf.enabled") && IsWindowsVistaOrLater()) || +var haveMp3 = getPref("media.wmf.enabled") || (IsLinux() && getPref("media.ffmpeg.enabled")) || (IsSupportedAndroid() && ((IsJellyBeanOrLater() && getPref("media.android-media-codec.enabled")) || -- cgit v1.2.3 From b78915e46c137cbba048f8e8deac4a8e3282a515 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sun, 2 Dec 2018 09:47:20 -0600 Subject: Remove MediaTelemetryConstants --- dom/media/platforms/MediaTelemetryConstants.h | 22 ---------------------- dom/media/platforms/moz.build | 1 - dom/media/platforms/wmf/DXVA2Manager.cpp | 1 - dom/media/platforms/wmf/WMFVideoMFTManager.cpp | 1 - 4 files changed, 25 deletions(-) delete mode 100644 dom/media/platforms/MediaTelemetryConstants.h (limited to 'dom/media') diff --git a/dom/media/platforms/MediaTelemetryConstants.h b/dom/media/platforms/MediaTelemetryConstants.h deleted file mode 100644 index 5024949a8..000000000 --- a/dom/media/platforms/MediaTelemetryConstants.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * 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/. */ -#ifndef dom_media_platforms_MediaTelemetryConstants_h___ -#define dom_media_platforms_MediaTelemetryConstants_h___ - -namespace mozilla { -namespace media { - -enum class MediaDecoderBackend : uint32_t -{ - WMFSoftware = 0, - WMFDXVA2D3D9 = 1, - WMFDXVA2D3D11 = 2 -}; - -} // namespace media -} // namespace mozilla - -#endif // dom_media_platforms_MediaTelemetryConstants_h___ diff --git a/dom/media/platforms/moz.build b/dom/media/platforms/moz.build index be13d31c4..f5fb72c5d 100644 --- a/dom/media/platforms/moz.build +++ b/dom/media/platforms/moz.build @@ -10,7 +10,6 @@ EXPORTS += [ 'agnostic/TheoraDecoder.h', 'agnostic/VorbisDecoder.h', 'agnostic/VPXDecoder.h', - 'MediaTelemetryConstants.h', 'PDMFactory.h', 'PlatformDecoderModule.h', 'wrappers/FuzzingWrapper.h', diff --git a/dom/media/platforms/wmf/DXVA2Manager.cpp b/dom/media/platforms/wmf/DXVA2Manager.cpp index 1226ea621..69e002f7f 100644 --- a/dom/media/platforms/wmf/DXVA2Manager.cpp +++ b/dom/media/platforms/wmf/DXVA2Manager.cpp @@ -14,7 +14,6 @@ #include "mozilla/layers/D3D11ShareHandleImage.h" #include "mozilla/layers/ImageBridgeChild.h" #include "mozilla/layers/TextureForwarder.h" -#include "MediaTelemetryConstants.h" #include "mfapi.h" #include "gfxPrefs.h" #include "MFTDecoder.h" diff --git a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp index 8a51f817a..a7633a7de 100644 --- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp @@ -27,7 +27,6 @@ #include "mozilla/WindowsVersion.h" #include "mozilla/Telemetry.h" #include "nsPrintfCString.h" -#include "MediaTelemetryConstants.h" #include "GMPUtils.h" // For SplitAt. TODO: Move SplitAt to a central place. #include "MP4Decoder.h" #include "VPXDecoder.h" -- cgit v1.2.3 From d6867e583c284b81dbb347e7bd1bb3d797e75404 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sat, 8 Dec 2018 15:43:58 -0600 Subject: Remove Rust files from media gtests --- dom/media/gtest/Cargo.toml | 7 ------- dom/media/gtest/TestRust.cpp | 9 --------- dom/media/gtest/hello.rs | 6 ------ 3 files changed, 22 deletions(-) delete mode 100644 dom/media/gtest/Cargo.toml delete mode 100644 dom/media/gtest/TestRust.cpp delete mode 100644 dom/media/gtest/hello.rs (limited to 'dom/media') diff --git a/dom/media/gtest/Cargo.toml b/dom/media/gtest/Cargo.toml deleted file mode 100644 index a55f8fb68..000000000 --- a/dom/media/gtest/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "mp4parse-gtest" -version = "0.1.0" -authors = ["nobody@mozilla.org"] - -[lib] -path = "hello.rs" diff --git a/dom/media/gtest/TestRust.cpp b/dom/media/gtest/TestRust.cpp deleted file mode 100644 index 86d0e99b8..000000000 --- a/dom/media/gtest/TestRust.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include -#include "gtest/gtest.h" - -extern "C" uint8_t* test_rust(); - -TEST(rust, CallFromCpp) { - auto greeting = test_rust(); - EXPECT_STREQ(reinterpret_cast(greeting), "hello from rust."); -} diff --git a/dom/media/gtest/hello.rs b/dom/media/gtest/hello.rs deleted file mode 100644 index cd111882a..000000000 --- a/dom/media/gtest/hello.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[no_mangle] -pub extern fn test_rust() -> *const u8 { - // NB: rust &str aren't null terminated. - let greeting = "hello from rust.\0"; - greeting.as_ptr() -} -- cgit v1.2.3 From 9ba2b33da473e128aef5b553c05c615cf9e45104 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sat, 8 Dec 2018 15:51:28 -0600 Subject: Remove files and comments related to MP4Reader The MP4Reader no longer exists in the tree. --- dom/media/fmp4/MP4Stream.cpp | 3 - dom/media/gtest/TestMP4Reader.cpp | 217 -------------------------- dom/media/gtest/moz.build | 1 - dom/media/platforms/wmf/WMFMediaDataDecoder.h | 2 +- 4 files changed, 1 insertion(+), 222 deletions(-) delete mode 100644 dom/media/gtest/TestMP4Reader.cpp (limited to 'dom/media') diff --git a/dom/media/fmp4/MP4Stream.cpp b/dom/media/fmp4/MP4Stream.cpp index 615a7dc01..9a79cac7a 100644 --- a/dom/media/fmp4/MP4Stream.cpp +++ b/dom/media/fmp4/MP4Stream.cpp @@ -48,9 +48,6 @@ MP4Stream::BlockingReadIntoCache(int64_t aOffset, size_t aCount, Monitor* aToUnl return true; } -// We surreptitiously reimplement the supposedly-blocking ReadAt as a non- -// blocking CachedReadAt, and record when it fails. This allows MP4Reader -// to retry the read as an actual blocking read without holding the lock. bool MP4Stream::ReadAt(int64_t aOffset, void* aBuffer, size_t aCount, size_t* aBytesRead) diff --git a/dom/media/gtest/TestMP4Reader.cpp b/dom/media/gtest/TestMP4Reader.cpp deleted file mode 100644 index f08f7a40d..000000000 --- a/dom/media/gtest/TestMP4Reader.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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 "gtest/gtest.h" -#include "MP4Reader.h" -#include "MP4Decoder.h" -#include "mozilla/SharedThreadPool.h" -#include "MockMediaResource.h" -#include "MockMediaDecoderOwner.h" -#include "mozilla/Preferences.h" -#include "TimeUnits.h" - -using namespace mozilla; -using namespace mozilla::dom; - -class TestBinding -{ -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestBinding); - - RefPtr decoder; - RefPtr resource; - RefPtr reader; - - explicit TestBinding(const char* aFileName = "gizmo.mp4") - : decoder(new MP4Decoder()) - , resource(new MockMediaResource(aFileName)) - , reader(new MP4Reader(decoder)) - { - EXPECT_EQ(NS_OK, Preferences::SetBool( - "media.use-blank-decoder", true)); - - EXPECT_EQ(NS_OK, resource->Open(nullptr)); - decoder->SetResource(resource); - - reader->Init(nullptr); - // This needs to be done before invoking GetBuffered. This is normally - // done by MediaDecoderStateMachine. - reader->DispatchSetStartTime(0); - } - - void Init() { - nsCOMPtr thread; - nsCOMPtr r = NewRunnableMethod(this, &TestBinding::ReadMetadata); - nsresult rv = NS_NewThread(getter_AddRefs(thread), r); - EXPECT_EQ(NS_OK, rv); - thread->Shutdown(); - } - -private: - virtual ~TestBinding() - { - { - RefPtr queue = reader->OwnerThread(); - nsCOMPtr task = NewRunnableMethod(reader, &MP4Reader::Shutdown); - // Hackily bypass the tail dispatcher so that we can AwaitShutdownAndIdle. - // In production code we'd use BeginShutdown + promises. - queue->Dispatch(task.forget(), AbstractThread::AssertDispatchSuccess, - AbstractThread::TailDispatch); - queue->AwaitShutdownAndIdle(); - } - decoder = nullptr; - resource = nullptr; - reader = nullptr; - SharedThreadPool::SpinUntilEmpty(); - } - - void ReadMetadata() - { - MediaInfo info; - MetadataTags* tags; - EXPECT_EQ(NS_OK, reader->ReadMetadata(&info, &tags)); - } -}; - -TEST(MP4Reader, BufferedRange) -{ - RefPtr b = new TestBinding(); - b->Init(); - - // Video 3-4 sec, audio 2.986666-4.010666 sec - b->resource->MockAddBufferedRange(248400, 327455); - - media::TimeIntervals ranges = b->reader->GetBuffered(); - EXPECT_EQ(1U, ranges.Length()); - EXPECT_NEAR(270000 / 90000.0, ranges.Start(0).ToSeconds(), 0.000001); - EXPECT_NEAR(360000 / 90000.0, ranges.End(0).ToSeconds(), 0.000001); -} - -TEST(MP4Reader, BufferedRangeMissingLastByte) -{ - RefPtr b = new TestBinding(); - b->Init(); - - // Dropping the last byte of the video - b->resource->MockClearBufferedRanges(); - b->resource->MockAddBufferedRange(248400, 324912); - b->resource->MockAddBufferedRange(324913, 327455); - - media::TimeIntervals ranges = b->reader->GetBuffered(); - EXPECT_EQ(1U, ranges.Length()); - EXPECT_NEAR(270000.0 / 90000.0, ranges.Start(0).ToSeconds(), 0.000001); - EXPECT_NEAR(357000 / 90000.0, ranges.End(0).ToSeconds(), 0.000001); -} - -TEST(MP4Reader, BufferedRangeSyncFrame) -{ - RefPtr b = new TestBinding(); - b->Init(); - - // Check that missing the first byte at 2 seconds skips right through to 3 - // seconds because of a missing sync frame - b->resource->MockClearBufferedRanges(); - b->resource->MockAddBufferedRange(146336, 327455); - - media::TimeIntervals ranges = b->reader->GetBuffered(); - EXPECT_EQ(1U, ranges.Length()); - EXPECT_NEAR(270000.0 / 90000.0, ranges.Start(0).ToSeconds(), 0.000001); - EXPECT_NEAR(360000 / 90000.0, ranges.End(0).ToSeconds(), 0.000001); -} - -TEST(MP4Reader, CompositionOrder) -{ - RefPtr b = new TestBinding("mediasource_test.mp4"); - b->Init(); - - // The first 5 video samples of this file are: - // Video timescale=2500 - // Frame Start Size Time Duration Sync - // 1 48 5455 166 83 Yes - // 2 5503 145 249 83 - // 3 6228 575 581 83 - // 4 7383 235 415 83 - // 5 8779 183 332 83 - // 6 9543 191 498 83 - // - // Audio timescale=44100 - // 1 5648 580 0 1024 Yes - // 2 6803 580 1024 1058 Yes - // 3 7618 581 2082 1014 Yes - // 4 8199 580 3096 1015 Yes - // 5 8962 581 4111 1014 Yes - // 6 9734 580 5125 1014 Yes - // 7 10314 581 6139 1059 Yes - // 8 11207 580 7198 1014 Yes - // 9 12035 581 8212 1014 Yes - // 10 12616 580 9226 1015 Yes - // 11 13220 581 10241 1014 Yes - - b->resource->MockClearBufferedRanges(); - // First two frames in decoding + first audio frame - b->resource->MockAddBufferedRange(48, 5503); // Video 1 - b->resource->MockAddBufferedRange(5503, 5648); // Video 2 - b->resource->MockAddBufferedRange(6228, 6803); // Video 3 - - // Audio - 5 frames; 0 - 139206 us - b->resource->MockAddBufferedRange(5648, 6228); - b->resource->MockAddBufferedRange(6803, 7383); - b->resource->MockAddBufferedRange(7618, 8199); - b->resource->MockAddBufferedRange(8199, 8779); - b->resource->MockAddBufferedRange(8962, 9563); - b->resource->MockAddBufferedRange(9734, 10314); - b->resource->MockAddBufferedRange(10314, 10895); - b->resource->MockAddBufferedRange(11207, 11787); - b->resource->MockAddBufferedRange(12035, 12616); - b->resource->MockAddBufferedRange(12616, 13196); - b->resource->MockAddBufferedRange(13220, 13901); - - media::TimeIntervals ranges = b->reader->GetBuffered(); - EXPECT_EQ(2U, ranges.Length()); - - EXPECT_NEAR(166.0 / 2500.0, ranges.Start(0).ToSeconds(), 0.000001); - EXPECT_NEAR(332.0 / 2500.0, ranges.End(0).ToSeconds(), 0.000001); - - EXPECT_NEAR(581.0 / 2500.0, ranges.Start(1).ToSeconds(), 0.000001); - EXPECT_NEAR(11255.0 / 44100.0, ranges.End(1).ToSeconds(), 0.000001); -} - -TEST(MP4Reader, Normalised) -{ - RefPtr b = new TestBinding("mediasource_test.mp4"); - b->Init(); - - // The first 5 video samples of this file are: - // Video timescale=2500 - // Frame Start Size Time Duration Sync - // 1 48 5455 166 83 Yes - // 2 5503 145 249 83 - // 3 6228 575 581 83 - // 4 7383 235 415 83 - // 5 8779 183 332 83 - // 6 9543 191 498 83 - // - // Audio timescale=44100 - // 1 5648 580 0 1024 Yes - // 2 6803 580 1024 1058 Yes - // 3 7618 581 2082 1014 Yes - // 4 8199 580 3096 1015 Yes - // 5 8962 581 4111 1014 Yes - // 6 9734 580 5125 1014 Yes - // 7 10314 581 6139 1059 Yes - // 8 11207 580 7198 1014 Yes - // 9 12035 581 8212 1014 Yes - // 10 12616 580 9226 1015 Yes - // 11 13220 581 10241 1014 Yes - - b->resource->MockClearBufferedRanges(); - b->resource->MockAddBufferedRange(48, 13901); - - media::TimeIntervals ranges = b->reader->GetBuffered(); - EXPECT_EQ(1U, ranges.Length()); - - EXPECT_NEAR(166.0 / 2500.0, ranges.Start(0).ToSeconds(), 0.000001); - EXPECT_NEAR(11255.0 / 44100.0, ranges.End(0).ToSeconds(), 0.000001); -} diff --git a/dom/media/gtest/moz.build b/dom/media/gtest/moz.build index d5d02bced..a7ea73807 100644 --- a/dom/media/gtest/moz.build +++ b/dom/media/gtest/moz.build @@ -21,7 +21,6 @@ UNIFIED_SOURCES += [ 'TestMozPromise.cpp', 'TestMP3Demuxer.cpp', 'TestMP4Demuxer.cpp', - # 'TestMP4Reader.cpp', disabled so we can turn check tests back on (bug 1175752) 'TestTrackEncoder.cpp', 'TestVideoSegment.cpp', 'TestVideoUtils.cpp', diff --git a/dom/media/platforms/wmf/WMFMediaDataDecoder.h b/dom/media/platforms/wmf/WMFMediaDataDecoder.h index a4dd49f56..75571d61e 100644 --- a/dom/media/platforms/wmf/WMFMediaDataDecoder.h +++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.h @@ -33,7 +33,7 @@ public: // Returns S_OK on success, or MF_E_TRANSFORM_NEED_MORE_INPUT if there's not // enough data to produce more output. If this returns a failure code other // than MF_E_TRANSFORM_NEED_MORE_INPUT, an error will be reported to the - // MP4Reader. + // MP4Demuxer. virtual HRESULT Output(int64_t aStreamOffset, RefPtr& aOutput) = 0; -- cgit v1.2.3 From eb6fdca18bc8858bd5e85a0c2543985d01cf78e6 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sat, 8 Dec 2018 16:28:34 -0600 Subject: Remove the old MP3FrameParser All former users of the old MP3 parsing code are gone, so we can now just remove the parser itself as well. --- dom/media/MP3FrameParser.cpp | 591 ------------------------------------------- dom/media/MP3FrameParser.h | 219 ---------------- dom/media/moz.build | 2 - 3 files changed, 812 deletions(-) delete mode 100644 dom/media/MP3FrameParser.cpp delete mode 100644 dom/media/MP3FrameParser.h (limited to 'dom/media') diff --git a/dom/media/MP3FrameParser.cpp b/dom/media/MP3FrameParser.cpp deleted file mode 100644 index 242e3df00..000000000 --- a/dom/media/MP3FrameParser.cpp +++ /dev/null @@ -1,591 +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 - -#include "nsMemory.h" -#include "MP3FrameParser.h" -#include "VideoUtils.h" - - -#define FROM_BIG_ENDIAN(X) ((uint32_t)((uint8_t)(X)[0] << 24 | (uint8_t)(X)[1] << 16 | \ - (uint8_t)(X)[2] << 8 | (uint8_t)(X)[3])) - - -namespace mozilla { - -/* - * Following code taken from http://www.hydrogenaudio.org/forums/index.php?showtopic=85125 - * with permission from the author, Nick Wallette . - */ - -/* BEGIN shameless copy and paste */ - -// Bitrates - use [version][layer][bitrate] -const uint16_t mpeg_bitrates[4][4][16] = { - { // Version 2.5 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3 - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2 - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1 - }, - { // Reserved - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // Invalid - }, - { // Version 2 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3 - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2 - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1 - }, - { // Version 1 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved - { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer 3 - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer 2 - { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // Layer 1 - } -}; - -// Sample rates - use [version][srate] -const uint16_t mpeg_srates[4][4] = { - { 11025, 12000, 8000, 0 }, // MPEG 2.5 - { 0, 0, 0, 0 }, // Reserved - { 22050, 24000, 16000, 0 }, // MPEG 2 - { 44100, 48000, 32000, 0 } // MPEG 1 -}; - -// Samples per frame - use [version][layer] -const uint16_t mpeg_frame_samples[4][4] = { -// Rsvd 3 2 1 < Layer v Version - { 0, 576, 1152, 384 }, // 2.5 - { 0, 0, 0, 0 }, // Reserved - { 0, 576, 1152, 384 }, // 2 - { 0, 1152, 1152, 384 } // 1 -}; - -// Slot size (MPEG unit of measurement) - use [layer] -const uint8_t mpeg_slot_size[4] = { 0, 1, 1, 4 }; // Rsvd, 3, 2, 1 - -uint16_t -MP3Frame::CalculateLength() -{ - // Lookup real values of these fields - uint32_t bitrate = mpeg_bitrates[mVersion][mLayer][mBitrate] * 1000; - uint32_t samprate = mpeg_srates[mVersion][mSampleRate]; - uint16_t samples = mpeg_frame_samples[mVersion][mLayer]; - uint8_t slot_size = mpeg_slot_size[mLayer]; - - // In-between calculations - float bps = (float)samples / 8.0; - float fsize = ( (bps * (float)bitrate) / (float)samprate ) - + ( (mPad) ? slot_size : 0 ); - - // Frame sizes are truncated integers - return (uint16_t)fsize; -} - -/* END shameless copy and paste */ - - -/** MP3Parser methods **/ - -MP3Parser::MP3Parser() - : mCurrentChar(0) -{ } - -void -MP3Parser::Reset() -{ - mCurrentChar = 0; -} - -uint16_t -MP3Parser::ParseFrameLength(uint8_t ch) -{ - mData.mRaw[mCurrentChar] = ch; - - MP3Frame &frame = mData.mFrame; - - // Validate MP3 header as we read. We can't mistake the start of an MP3 frame - // for the middle of another frame due to the sync byte at the beginning - // of the frame. - - // The only valid position for an all-high byte is the sync byte at the - // beginning of the frame. - if (ch == 0xff) { - mCurrentChar = 0; - } - - // Make sure the current byte is valid in context. If not, reset the parser. - if (mCurrentChar == 2) { - if (frame.mBitrate == 0x0f) { - goto fail; - } - } else if (mCurrentChar == 1) { - if (frame.mSync2 != 0x07 - || frame.mVersion == 0x01 - || frame.mLayer == 0x00) { - goto fail; - } - } - - // The only valid character at the beginning of the header is 0xff. Fail if - // it's different. - if (mCurrentChar == 0 && frame.mSync1 != 0xff) { - // Couldn't find the sync byte. Fail. - return 0; - } - - mCurrentChar++; - MOZ_ASSERT(mCurrentChar <= sizeof(MP3Frame)); - - // Don't have a full header yet. - if (mCurrentChar < sizeof(MP3Frame)) { - return 0; - } - - // Woo, valid header. Return the length. - mCurrentChar = 0; - return frame.CalculateLength(); - -fail: - Reset(); - return 0; -} - -uint32_t -MP3Parser::GetSampleRate() -{ - MP3Frame &frame = mData.mFrame; - return mpeg_srates[frame.mVersion][frame.mSampleRate]; -} - -uint32_t -MP3Parser::GetSamplesPerFrame() -{ - MP3Frame &frame = mData.mFrame; - return mpeg_frame_samples[frame.mVersion][frame.mLayer]; -} - - -/** ID3Parser methods **/ - -const char sID3Head[3] = { 'I', 'D', '3' }; -const uint32_t ID3_HEADER_LENGTH = 10; -const uint32_t ID3_FOOTER_LENGTH = 10; -const uint8_t ID3_FOOTER_PRESENT = 0x10; - -ID3Parser::ID3Parser() - : mCurrentChar(0) - , mVersion(0) - , mFlags(0) - , mHeaderLength(0) -{ } - -void -ID3Parser::Reset() -{ - mCurrentChar = mVersion = mFlags = mHeaderLength = 0; -} - -bool -ID3Parser::ParseChar(char ch) -{ - switch (mCurrentChar) { - // The first three bytes of an ID3v2 header must match the string "ID3". - case 0: case 1: case 2: - if (ch != sID3Head[mCurrentChar]) { - goto fail; - } - break; - // The fourth and fifth bytes give the version, between 2 and 4. - case 3: - if (ch < '\2' || ch > '\4') { - goto fail; - } - mVersion = uint8_t(ch); - break; - case 4: - if (ch != '\0') { - goto fail; - } - break; - // The sixth byte gives the flags; valid flags depend on the version. - case 5: - if ((ch & (0xff >> mVersion)) != '\0') { - goto fail; - } - mFlags = uint8_t(ch); - break; - // Bytes seven through ten give the sum of the byte length of the extended - // header, the padding and the frames after unsynchronisation. - // These bytes form a 28-bit integer, with the high bit of each byte unset. - case 6: case 7: case 8: case 9: - if (ch & 0x80) { - goto fail; - } - mHeaderLength <<= 7; - mHeaderLength |= ch; - if (mCurrentChar == 9) { - mHeaderLength += ID3_HEADER_LENGTH; - mHeaderLength += (mFlags & ID3_FOOTER_PRESENT) ? ID3_FOOTER_LENGTH : 0; - } - break; - default: - MOZ_CRASH("Header already fully parsed!"); - } - - mCurrentChar++; - - return IsParsed(); - -fail: - if (mCurrentChar) { - Reset(); - return ParseChar(ch); - } - Reset(); - return false; -} - -bool -ID3Parser::IsParsed() const -{ - return mCurrentChar >= ID3_HEADER_LENGTH; -} - -uint32_t -ID3Parser::GetHeaderLength() const -{ - MOZ_ASSERT(IsParsed(), - "Queried length of ID3 header before parsing finished."); - return mHeaderLength; -} - - -/** VBR header helper stuff **/ - -// Helper function to find a VBR header in an MP3 frame. -// Based on information from -// http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header - -const uint32_t VBRI_TAG = FROM_BIG_ENDIAN("VBRI"); -const uint32_t VBRI_OFFSET = 32 - sizeof(MP3Frame); -const uint32_t VBRI_FRAME_COUNT_OFFSET = VBRI_OFFSET + 14; -const uint32_t VBRI_MIN_FRAME_SIZE = VBRI_OFFSET + 26; - -const uint32_t XING_TAG = FROM_BIG_ENDIAN("Xing"); -enum XingFlags { - XING_HAS_NUM_FRAMES = 0x01, - XING_HAS_NUM_BYTES = 0x02, - XING_HAS_TOC = 0x04, - XING_HAS_VBR_SCALE = 0x08 -}; - -static int64_t -ParseXing(const char *aBuffer) -{ - uint32_t flags = FROM_BIG_ENDIAN(aBuffer + 4); - - if (!(flags & XING_HAS_NUM_FRAMES)) { - NS_WARNING("VBR file without frame count. Duration estimation likely to " - "be totally wrong."); - return -1; - } - - int64_t numFrames = -1; - if (flags & XING_HAS_NUM_FRAMES) { - numFrames = FROM_BIG_ENDIAN(aBuffer + 8); - } - - return numFrames; -} - -static int64_t -FindNumVBRFrames(const nsCString& aFrame) -{ - const char *buffer = aFrame.get(); - const char *bufferEnd = aFrame.get() + aFrame.Length(); - - // VBRI header is nice and well-defined; let's try to find that first. - if (aFrame.Length() > VBRI_MIN_FRAME_SIZE && - FROM_BIG_ENDIAN(buffer + VBRI_OFFSET) == VBRI_TAG) { - return FROM_BIG_ENDIAN(buffer + VBRI_FRAME_COUNT_OFFSET); - } - - // We have to search for the Xing header as its position can change. - for (; buffer + sizeof(XING_TAG) < bufferEnd; buffer++) { - if (FROM_BIG_ENDIAN(buffer) == XING_TAG) { - return ParseXing(buffer); - } - } - - return -1; -} - - -/** MP3FrameParser methods **/ - -// Some MP3's have large ID3v2 tags, up to 150KB, so we allow lots of -// skipped bytes to be read, just in case, before we give up and assume -// we're not parsing an MP3 stream. -static const uint32_t MAX_SKIPPED_BYTES = 4096; - -enum { - MP3_HEADER_LENGTH = 4, -}; - -MP3FrameParser::MP3FrameParser(int64_t aLength) -: mLock("MP3FrameParser.mLock"), - mTotalID3Size(0), - mTotalFrameSize(0), - mFrameCount(0), - mOffset(0), - mLength(aLength), - mMP3Offset(-1), - mSamplesPerSecond(0), - mFirstFrameEnd(-1), - mIsMP3(MAYBE_MP3) -{ } - -nsresult MP3FrameParser::ParseBuffer(const uint8_t* aBuffer, - uint32_t aLength, - int64_t aStreamOffset, - uint32_t* aOutBytesRead) -{ - // Iterate forwards over the buffer, looking for ID3 tag, or MP3 - // Frame headers. - const uint8_t *buffer = aBuffer; - const uint8_t *bufferEnd = aBuffer + aLength; - - // If we haven't found any MP3 frame data yet, there might be ID3 headers - // we can skip over. - if (mMP3Offset < 0) { - for (const uint8_t *ch = buffer; ch < bufferEnd; ch++) { - if (mID3Parser.ParseChar(*ch)) { - // Found an ID3 header. We don't care about the body of the header, so - // just skip past. - buffer = ch + mID3Parser.GetHeaderLength() - (ID3_HEADER_LENGTH - 1); - - if (buffer <= ch) { - return NS_ERROR_FAILURE; - } - - ch = buffer; - - mTotalID3Size += mID3Parser.GetHeaderLength(); - - // Yes, this is an MP3! - mIsMP3 = DEFINITELY_MP3; - - mID3Parser.Reset(); - } - } - } - - // The first MP3 frame in a variable bitrate stream can contain metadata - // for duration estimation and seeking, so we buffer that first frame here. - if (aStreamOffset < mFirstFrameEnd) { - uint64_t copyLen = std::min((int64_t)aLength, mFirstFrameEnd - aStreamOffset); - mFirstFrame.Append((const char *)buffer, copyLen); - buffer += copyLen; - } - - while (buffer < bufferEnd) { - uint16_t frameLen = mMP3Parser.ParseFrameLength(*buffer); - - if (frameLen) { - // We've found an MP3 frame! - // This is the first frame (and the only one we'll bother parsing), so: - // * Mark this stream as MP3; - // * Store the offset at which the MP3 data started; and - // * Start buffering the frame, as it might contain handy metadata. - - // We're now sure this is an MP3 stream. - mIsMP3 = DEFINITELY_MP3; - - // We need to know these to convert the number of frames in the stream - // to the length of the stream in seconds. - mSamplesPerSecond = mMP3Parser.GetSampleRate(); - mSamplesPerFrame = mMP3Parser.GetSamplesPerFrame(); - - // If the stream has a constant bitrate, we should only need the length - // of the first frame and the length (in bytes) of the stream to - // estimate the length (in seconds). - mTotalFrameSize += frameLen; - mFrameCount++; - - // If |mMP3Offset| isn't set then this is the first MP3 frame we have - // seen in the stream, which is useful for duration estimation. - if (mMP3Offset > -1) { - uint16_t skip = frameLen - sizeof(MP3Frame); - buffer += skip ? skip : 1; - continue; - } - - // Remember the offset of the MP3 stream. - // We're at the last byte of an MP3Frame, so MP3 data started - // sizeof(MP3Frame) - 1 bytes ago. - mMP3Offset = aStreamOffset - + (buffer - aBuffer) - - (sizeof(MP3Frame) - 1); - - buffer++; - - // If the stream has a variable bitrate, the first frame has metadata - // we need for duration estimation and seeking. Start buffering it so we - // can parse it later. - mFirstFrameEnd = mMP3Offset + frameLen; - uint64_t currOffset = buffer - aBuffer + aStreamOffset; - uint64_t copyLen = std::min(mFirstFrameEnd - currOffset, - (uint64_t)(bufferEnd - buffer)); - mFirstFrame.Append((const char *)buffer, copyLen); - - buffer += copyLen; - - } else { - // Nothing to see here. Move along. - buffer++; - } - } - - *aOutBytesRead = buffer - aBuffer; - - if (mFirstFrameEnd > -1 && mFirstFrameEnd <= aStreamOffset + buffer - aBuffer) { - // We have our whole first frame. Try to find a VBR header. - mNumFrames = FindNumVBRFrames(mFirstFrame); - mFirstFrameEnd = -1; - } - - return NS_OK; -} - -void MP3FrameParser::Parse(const uint8_t* aBuffer, uint32_t aLength, uint64_t aOffset) -{ - MutexAutoLock mon(mLock); - - if (HasExactDuration()) { - // We know the duration; nothing to do here. - return; - } - - const uint8_t* buffer = aBuffer; - int32_t length = aLength; - uint64_t offset = aOffset; - - // Got some data we have seen already. Skip forward to what we need. - if (aOffset < mOffset) { - buffer += mOffset - aOffset; - length -= mOffset - aOffset; - offset = mOffset; - - if (length <= 0) { - return; - } - } - - // If there is a discontinuity in the input stream, reset the state of the - // parsers so we don't get any partial headers. - if (mOffset < aOffset) { - if (!mID3Parser.IsParsed()) { - // Only reset this if it hasn't finished yet. - mID3Parser.Reset(); - } - - if (mFirstFrameEnd > -1) { - NS_WARNING("Discontinuity in input while buffering first frame."); - mFirstFrameEnd = -1; - } - - mMP3Parser.Reset(); - } - - uint32_t bytesRead = 0; - if (NS_FAILED(ParseBuffer(buffer, - length, - offset, - &bytesRead))) { - return; - } - - MOZ_ASSERT(length <= (int)bytesRead, "All bytes should have been consumed"); - - // Update next data offset - mOffset = offset + bytesRead; - - // If we've parsed lots of data and we still have nothing, just give up. - // We don't count ID3 headers towards the skipped bytes count, as MP3 files - // can have massive ID3 sections. - if (!mID3Parser.IsParsed() && mMP3Offset < 0 && - mOffset - mTotalID3Size > MAX_SKIPPED_BYTES) { - mIsMP3 = NOT_MP3; - } -} - -int64_t MP3FrameParser::GetDuration() -{ - MutexAutoLock mon(mLock); - - if (!ParsedHeaders() || !mSamplesPerSecond) { - // Not a single frame decoded yet. - return -1; - } - - MOZ_ASSERT(mFrameCount > 0 && mTotalFrameSize > 0, - "Frame parser should have seen at least one MP3 frame of positive length."); - - if (!mFrameCount || !mTotalFrameSize) { - // This should never happen. - return -1; - } - - double frames; - if (mNumFrames < 0) { - // Estimate the number of frames in the stream based on the average frame - // size and the length of the MP3 file. - double frameSize = (double)mTotalFrameSize / mFrameCount; - frames = (double)(mLength - mMP3Offset) / frameSize; - } else { - // We know the exact number of frames from the VBR header. - frames = mNumFrames; - } - - // The duration of each frame is constant over a given stream. - double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond; - - return frames * usPerFrame; -} - -int64_t MP3FrameParser::GetMP3Offset() -{ - MutexAutoLock mon(mLock); - return mMP3Offset; -} - -bool MP3FrameParser::ParsedHeaders() -{ - // We have seen both the beginning and the end of the first MP3 frame in the - // stream. - return mMP3Offset > -1 && mFirstFrameEnd < 0; -} - -bool MP3FrameParser::HasExactDuration() -{ - return ParsedHeaders() && mNumFrames > -1; -} - -bool MP3FrameParser::NeedsData() -{ - // If we don't know the duration exactly then either: - // - we're still waiting for a VBR header; or - // - we look at all frames to constantly update our duration estimate. - return IsMP3() && !HasExactDuration(); -} - -} // namespace mozilla diff --git a/dom/media/MP3FrameParser.h b/dom/media/MP3FrameParser.h deleted file mode 100644 index d2ba791fd..000000000 --- a/dom/media/MP3FrameParser.h +++ /dev/null @@ -1,219 +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/. */ - -#ifndef MP3FrameParser_h -#define MP3FrameParser_h - -#include - -#include "mozilla/Mutex.h" -#include "nsString.h" -#include "Intervals.h" - -namespace mozilla { - -// Simple parser to tell whether we've found an ID3 header and how long it is, -// so that we can skip it. -// XXX maybe actually parse this stuff? -class ID3Parser -{ -public: - ID3Parser(); - - void Reset(); - bool ParseChar(char ch); - bool IsParsed() const; - uint32_t GetHeaderLength() const; - -private: - uint32_t mCurrentChar; - uint8_t mVersion; - uint8_t mFlags; - uint32_t mHeaderLength; -}; - -struct MP3Frame { - uint16_t mSync1 : 8; // Always all set - uint16_t mProtected : 1; // Ignored - uint16_t mLayer : 2; - uint16_t mVersion : 2; - uint16_t mSync2 : 3; // Always all set - uint16_t mPrivate : 1; // Ignored - uint16_t mPad : 1; - uint16_t mSampleRate : 2; // Index into mpeg_srates above - uint16_t mBitrate : 4; // Index into mpeg_bitrates above - - uint16_t CalculateLength(); -}; - -// Buffering parser for MP3 frames. -class MP3Parser -{ -public: - MP3Parser(); - - // Forget all data the parser has seen so far. - void Reset(); - - // Parse the given byte. If we have found a frame header, return the length of - // the frame. - uint16_t ParseFrameLength(uint8_t ch); - - // Get the sample rate from the current header. - uint32_t GetSampleRate(); - - // Get the number of samples per frame. - uint32_t GetSamplesPerFrame(); - -private: - uint32_t mCurrentChar; - union { - uint8_t mRaw[3]; - MP3Frame mFrame; - } mData; -}; - - -// A description of the MP3 format and its extensions is available at -// -// http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header -// -// The data in MP3 streams is split into small frames, with each frame -// containing a fixed number of samples. The duration of a frame depends -// on the frame's bit rate and sample rate. Both values can vary among -// frames, so it is necessary to examine each individual frame of an MP3 -// stream to calculate the stream's overall duration. -// -// The MP3 frame parser extracts information from an MP3 data stream. It -// accepts a range of frames of an MP3 stream as input, and parses all -// frames for their duration. Callers can query the stream's overall -// duration from the parser. -// -// Call the methods NotifyDataArrived or Parse to add new data. If you added -// information for a certain stream position, you cannot go back to previous -// positions. The parser will simply ignore the input. If you skip stream -// positions, the duration of the related MP3 frames will be estimated from -// the stream's average. -// -// The method GetDuration returns calculated duration of the stream, including -// estimates for skipped ranges. -// -// All public methods are thread-safe. - -class MP3FrameParser -{ -public: - explicit MP3FrameParser(int64_t aLength=-1); - - bool IsMP3() { - MutexAutoLock mon(mLock); - return mIsMP3 != NOT_MP3; - } - - void Parse(const uint8_t* aBuffer, uint32_t aLength, uint64_t aStreamOffset); - - // Returns the duration, in microseconds. If the entire stream has not - // been parsed yet, this is an estimate based on the bitrate of the - // frames parsed so far. - int64_t GetDuration(); - - // Returns the offset of the first MP3 frame in the stream, or -1 of - // no MP3 frame has been detected yet. - int64_t GetMP3Offset(); - - // Returns true if we've seen the whole first frame of the MP3 stream, and - // therefore can make an estimate on the stream duration. - // Otherwise, returns false. - bool ParsedHeaders(); - - // Returns true if we know the exact duration of the MP3 stream; - // false otherwise. - bool HasExactDuration(); - - // Returns true if the parser needs more data for duration estimation. - bool NeedsData(); - // Assign the total lenght of this mp3 stream - void SetLength(int64_t aLength) { - MutexAutoLock mon(mLock); - mLength = aLength; - } -private: - - // Parses aBuffer, starting at offset 0. Returns the number of bytes - // parsed, relative to the start of the buffer. Note this may be - // greater than aLength if the headers in the buffer indicate that - // the frame or ID3 tag extends outside of aBuffer. Returns failure - // if too many non-MP3 bytes are parsed. - nsresult ParseBuffer(const uint8_t* aBuffer, - uint32_t aLength, - int64_t aStreamOffset, - uint32_t* aOutBytesRead); - - // A low-contention lock for protecting the parser results - Mutex mLock; - - // ID3 header parser. Keeps state between reads in case the header falls - // in between. - ID3Parser mID3Parser; - - // MP3 frame header parser. - MP3Parser mMP3Parser; - - // If we read |MAX_SKIPPED_BYTES| from the stream without finding any MP3 - // frames, we give up and report |NOT_MP3|. Here we track the cumulative size - // of any ID3 headers we've seen so big ID3 sections aren't counted towards - // skipped bytes. - uint32_t mTotalID3Size; - - // All fields below are protected by mLock - - // We keep stats on the size of all the frames we've seen, as well as how many - // so that we can estimate the duration of the rest of the stream. - uint64_t mTotalFrameSize; - uint64_t mFrameCount; - - // Offset of the last data parsed. This is the end offset of the last data - // block parsed, so it's the start offset we expect to get on the next - // call to Parse(). - uint64_t mOffset; - - // Total length of the stream in bytes. - int64_t mLength; - - // Offset of first MP3 frame in the bitstream. Has value -1 until the - // first MP3 frame is found. - int64_t mMP3Offset; - - // The exact number of frames in this stream, if we know it. -1 otherwise. - int64_t mNumFrames; - - // Number of audio samples per second and per frame. Fixed through the whole - // file. If we know these variables as well as the number of frames in the - // file, we can get an exact duration for the stream. - uint16_t mSamplesPerSecond; - uint16_t mSamplesPerFrame; - - // If the MP3 has a variable bitrate, then there *should* be metadata about - // the encoding in the first frame. We buffer the first frame here. - nsCString mFirstFrame; - - // While we are reading the first frame, this is the stream offset of the - // last byte of that frame. -1 at all other times. - int64_t mFirstFrameEnd; - - enum eIsMP3 { - MAYBE_MP3, // We're giving the stream the benefit of the doubt... - DEFINITELY_MP3, // We've hit at least one ID3 tag or MP3 frame. - NOT_MP3 // Not found any evidence of the stream being MP3. - }; - - eIsMP3 mIsMP3; - -}; - -} // namespace mozilla - -#endif diff --git a/dom/media/moz.build b/dom/media/moz.build index b3db05af1..772f27b35 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -127,7 +127,6 @@ EXPORTS += [ 'MediaTrackList.h', 'MP3Decoder.h', 'MP3Demuxer.h', - 'MP3FrameParser.h', 'NextFrameSeekTask.h', 'nsIDocumentActivity.h', 'PrincipalChangeObserver.h', @@ -236,7 +235,6 @@ UNIFIED_SOURCES += [ 'MediaTrackList.cpp', 'MP3Decoder.cpp', 'MP3Demuxer.cpp', - 'MP3FrameParser.cpp', 'NextFrameSeekTask.cpp', 'QueueObject.cpp', 'SeekJob.cpp', -- cgit v1.2.3 From 6b6a28ddb0d42a93e66578d598c08020b7e45985 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sat, 8 Dec 2018 16:29:04 -0600 Subject: Remove separate MP3 name space It is no longer needed. --- dom/media/DecoderTraits.cpp | 2 +- dom/media/MP3Decoder.cpp | 2 +- dom/media/MP3Demuxer.cpp | 2 -- dom/media/MP3Demuxer.h | 2 -- dom/media/gtest/TestMP3Demuxer.cpp | 1 - 5 files changed, 2 insertions(+), 7 deletions(-) (limited to 'dom/media') diff --git a/dom/media/DecoderTraits.cpp b/dom/media/DecoderTraits.cpp index 473ca9533..477ecf358 100644 --- a/dom/media/DecoderTraits.cpp +++ b/dom/media/DecoderTraits.cpp @@ -432,7 +432,7 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac } else #endif if (IsMP3SupportedType(aType)) { - decoderReader = new MediaFormatReader(aDecoder, new mp3::MP3Demuxer(aDecoder->GetResource())); + decoderReader = new MediaFormatReader(aDecoder, new MP3Demuxer(aDecoder->GetResource())); } else if (IsAACSupportedType(aType)) { decoderReader = new MediaFormatReader(aDecoder, new ADTSDemuxer(aDecoder->GetResource())); diff --git a/dom/media/MP3Decoder.cpp b/dom/media/MP3Decoder.cpp index b71111e79..074a0866d 100644 --- a/dom/media/MP3Decoder.cpp +++ b/dom/media/MP3Decoder.cpp @@ -24,7 +24,7 @@ MP3Decoder::Clone(MediaDecoderOwner* aOwner) { MediaDecoderStateMachine* MP3Decoder::CreateStateMachine() { RefPtr reader = - new MediaFormatReader(this, new mp3::MP3Demuxer(GetResource())); + new MediaFormatReader(this, new MP3Demuxer(GetResource())); return new MediaDecoderStateMachine(this, reader); } diff --git a/dom/media/MP3Demuxer.cpp b/dom/media/MP3Demuxer.cpp index 7d478a41b..5a98cabfe 100644 --- a/dom/media/MP3Demuxer.cpp +++ b/dom/media/MP3Demuxer.cpp @@ -33,7 +33,6 @@ using mozilla::media::TimeIntervals; using mp4_demuxer::ByteReader; namespace mozilla { -namespace mp3 { // MP3Demuxer @@ -1338,5 +1337,4 @@ ID3Parser::ID3Header::Update(uint8_t c) { return IsValid(mPos++); } -} // namespace mp3 } // namespace mozilla diff --git a/dom/media/MP3Demuxer.h b/dom/media/MP3Demuxer.h index 03e67b0d9..5331c4d54 100644 --- a/dom/media/MP3Demuxer.h +++ b/dom/media/MP3Demuxer.h @@ -13,7 +13,6 @@ #include namespace mozilla { -namespace mp3 { class MP3TrackDemuxer; @@ -468,7 +467,6 @@ private: UniquePtr mInfo; }; -} // namespace mp3 } // namespace mozilla #endif diff --git a/dom/media/gtest/TestMP3Demuxer.cpp b/dom/media/gtest/TestMP3Demuxer.cpp index 8d2109f00..934acb60e 100644 --- a/dom/media/gtest/TestMP3Demuxer.cpp +++ b/dom/media/gtest/TestMP3Demuxer.cpp @@ -11,7 +11,6 @@ #include "MockMediaResource.h" using namespace mozilla; -using namespace mozilla::mp3; using media::TimeUnit; -- cgit v1.2.3 From 8a9362ff04fbdb35c41f5b0fe969b5ab45fb00d8 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sat, 8 Dec 2018 16:30:00 -0600 Subject: Move the MP3 code to it's own directory --- dom/media/MP3Decoder.cpp | 50 -- dom/media/MP3Decoder.h | 29 - dom/media/MP3Demuxer.cpp | 1340 ------------------------------------------ dom/media/MP3Demuxer.h | 472 --------------- dom/media/moz.build | 5 +- dom/media/mp3/MP3Decoder.cpp | 50 ++ dom/media/mp3/MP3Decoder.h | 29 + dom/media/mp3/MP3Demuxer.cpp | 1340 ++++++++++++++++++++++++++++++++++++++++++ dom/media/mp3/MP3Demuxer.h | 472 +++++++++++++++ dom/media/mp3/moz.build | 17 + 10 files changed, 1909 insertions(+), 1895 deletions(-) delete mode 100644 dom/media/MP3Decoder.cpp delete mode 100644 dom/media/MP3Decoder.h delete mode 100644 dom/media/MP3Demuxer.cpp delete mode 100644 dom/media/MP3Demuxer.h create mode 100644 dom/media/mp3/MP3Decoder.cpp create mode 100644 dom/media/mp3/MP3Decoder.h create mode 100644 dom/media/mp3/MP3Demuxer.cpp create mode 100644 dom/media/mp3/MP3Demuxer.h create mode 100644 dom/media/mp3/moz.build (limited to 'dom/media') diff --git a/dom/media/MP3Decoder.cpp b/dom/media/MP3Decoder.cpp deleted file mode 100644 index 074a0866d..000000000 --- a/dom/media/MP3Decoder.cpp +++ /dev/null @@ -1,50 +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 "MP3Decoder.h" -#include "MediaDecoderStateMachine.h" -#include "MediaFormatReader.h" -#include "MP3Demuxer.h" -#include "PDMFactory.h" - -namespace mozilla { - -MediaDecoder* -MP3Decoder::Clone(MediaDecoderOwner* aOwner) { - if (!IsEnabled()) { - return nullptr; - } - return new MP3Decoder(aOwner); -} - -MediaDecoderStateMachine* -MP3Decoder::CreateStateMachine() { - RefPtr reader = - new MediaFormatReader(this, new MP3Demuxer(GetResource())); - return new MediaDecoderStateMachine(this, reader); -} - -/* static */ -bool -MP3Decoder::IsEnabled() { - RefPtr platform = new PDMFactory(); - return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mpeg"), - /* DecoderDoctorDiagnostics* */ nullptr); -} - -/* static */ -bool MP3Decoder::CanHandleMediaType(const nsACString& aType, - const nsAString& aCodecs) -{ - if (aType.EqualsASCII("audio/mp3") || aType.EqualsASCII("audio/mpeg")) { - return IsEnabled() && - (aCodecs.IsEmpty() || aCodecs.EqualsASCII("mp3")); - } - return false; -} - -} // namespace mozilla diff --git a/dom/media/MP3Decoder.h b/dom/media/MP3Decoder.h deleted file mode 100644 index 887251065..000000000 --- a/dom/media/MP3Decoder.h +++ /dev/null @@ -1,29 +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/. */ -#ifndef MP3Decoder_h_ -#define MP3Decoder_h_ - -#include "MediaDecoder.h" - -namespace mozilla { - -class MP3Decoder : public MediaDecoder { -public: - // MediaDecoder interface. - explicit MP3Decoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {} - MediaDecoder* Clone(MediaDecoderOwner* aOwner) override; - MediaDecoderStateMachine* CreateStateMachine() override; - - // Returns true if the MP3 backend is preffed 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 - -#endif diff --git a/dom/media/MP3Demuxer.cpp b/dom/media/MP3Demuxer.cpp deleted file mode 100644 index 5a98cabfe..000000000 --- a/dom/media/MP3Demuxer.cpp +++ /dev/null @@ -1,1340 +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 "MP3Demuxer.h" - -#include -#include - -#include "mozilla/Assertions.h" -#include "mozilla/EndianUtils.h" -#include "nsAutoPtr.h" -#include "VideoUtils.h" -#include "TimeUnits.h" -#include "prenv.h" - -#ifdef PR_LOGGING -extern mozilla::LazyLogModule gMediaDemuxerLog; -#define MP3LOG(msg, ...) \ - MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__)) -#define MP3LOGV(msg, ...) \ - MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("MP3Demuxer " msg, ##__VA_ARGS__)) -#else -#define MP3LOG(msg, ...) -#define MP3LOGV(msg, ...) -#endif - -using mozilla::media::TimeUnit; -using mozilla::media::TimeInterval; -using mozilla::media::TimeIntervals; -using mp4_demuxer::ByteReader; - -namespace mozilla { - -// MP3Demuxer - -MP3Demuxer::MP3Demuxer(MediaResource* aSource) - : mSource(aSource) -{} - -bool -MP3Demuxer::InitInternal() { - if (!mTrackDemuxer) { - mTrackDemuxer = new MP3TrackDemuxer(mSource); - } - return mTrackDemuxer->Init(); -} - -RefPtr -MP3Demuxer::Init() { - if (!InitInternal()) { - MP3LOG("MP3Demuxer::Init() failure: waiting for data"); - - return InitPromise::CreateAndReject( - NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__); - } - - MP3LOG("MP3Demuxer::Init() successful"); - return InitPromise::CreateAndResolve(NS_OK, __func__); -} - -bool -MP3Demuxer::HasTrackType(TrackInfo::TrackType aType) const { - return aType == TrackInfo::kAudioTrack; -} - -uint32_t -MP3Demuxer::GetNumberTracks(TrackInfo::TrackType aType) const { - return aType == TrackInfo::kAudioTrack ? 1u : 0u; -} - -already_AddRefed -MP3Demuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber) { - if (!mTrackDemuxer) { - return nullptr; - } - return RefPtr(mTrackDemuxer).forget(); -} - -bool -MP3Demuxer::IsSeekable() const { - return true; -} - -void -MP3Demuxer::NotifyDataArrived() { - // TODO: bug 1169485. - NS_WARNING("Unimplemented function NotifyDataArrived"); - MP3LOGV("NotifyDataArrived()"); -} - -void -MP3Demuxer::NotifyDataRemoved() { - // TODO: bug 1169485. - NS_WARNING("Unimplemented function NotifyDataRemoved"); - MP3LOGV("NotifyDataRemoved()"); -} - - -// MP3TrackDemuxer - -MP3TrackDemuxer::MP3TrackDemuxer(MediaResource* aSource) - : mSource(aSource) - , mOffset(0) - , mFirstFrameOffset(0) - , mNumParsedFrames(0) - , mFrameIndex(0) - , mTotalFrameLen(0) - , mSamplesPerFrame(0) - , mSamplesPerSecond(0) - , mChannels(0) -{ - Reset(); -} - -bool -MP3TrackDemuxer::Init() { - Reset(); - FastSeek(TimeUnit()); - // Read the first frame to fetch sample rate and other meta data. - RefPtr frame(GetNextFrame(FindFirstFrame())); - - MP3LOG("Init StreamLength()=%" PRId64 " first-frame-found=%d", - StreamLength(), !!frame); - - if (!frame) { - return false; - } - - // Rewind back to the stream begin to avoid dropping the first frame. - FastSeek(TimeUnit()); - - if (!mInfo) { - mInfo = MakeUnique(); - } - - mInfo->mRate = mSamplesPerSecond; - mInfo->mChannels = mChannels; - mInfo->mBitDepth = 16; - mInfo->mMimeType = "audio/mpeg"; - mInfo->mDuration = Duration().ToMicroseconds(); - - MP3LOG("Init mInfo={mRate=%d mChannels=%d mBitDepth=%d mDuration=%" PRId64 "}", - mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth, - mInfo->mDuration); - - return mSamplesPerSecond && mChannels; -} - -media::TimeUnit -MP3TrackDemuxer::SeekPosition() const { - TimeUnit pos = Duration(mFrameIndex); - if (Duration() > TimeUnit()) { - pos = std::min(Duration(), pos); - } - return pos; -} - -const FrameParser::Frame& -MP3TrackDemuxer::LastFrame() const { - return mParser.PrevFrame(); -} - -RefPtr -MP3TrackDemuxer::DemuxSample() { - return GetNextFrame(FindNextFrame()); -} - -const ID3Parser::ID3Header& -MP3TrackDemuxer::ID3Header() const { - return mParser.ID3Header(); -} - -const FrameParser::VBRHeader& -MP3TrackDemuxer::VBRInfo() const { - return mParser.VBRInfo(); -} - -UniquePtr -MP3TrackDemuxer::GetInfo() const { - return mInfo->Clone(); -} - -RefPtr -MP3TrackDemuxer::Seek(TimeUnit aTime) { - // Efficiently seek to the position. - FastSeek(aTime); - // Correct seek position by scanning the next frames. - const TimeUnit seekTime = ScanUntil(aTime); - - return SeekPromise::CreateAndResolve(seekTime, __func__); -} - -TimeUnit -MP3TrackDemuxer::FastSeek(const TimeUnit& aTime) { - MP3LOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64 - " mFrameIndex=%" PRId64 " mOffset=%" PRIu64, - aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames, - mFrameIndex, mOffset); - - const auto& vbr = mParser.VBRInfo(); - if (!aTime.ToMicroseconds()) { - // Quick seek to the beginning of the stream. - mFrameIndex = 0; - } else if (vbr.IsTOCPresent() && Duration().ToMicroseconds() > 0) { - // Use TOC for more precise seeking. - const float durationFrac = static_cast(aTime.ToMicroseconds()) / - Duration().ToMicroseconds(); - mFrameIndex = FrameIndexFromOffset(vbr.Offset(durationFrac)); - } else if (AverageFrameLength() > 0) { - mFrameIndex = FrameIndexFromTime(aTime); - } - - mOffset = OffsetFromFrameIndex(mFrameIndex); - - if (mOffset > mFirstFrameOffset && StreamLength() > 0) { - mOffset = std::min(StreamLength() - 1, mOffset); - } - - mParser.EndFrameSession(); - - MP3LOG("FastSeek End TOC=%d avgFrameLen=%f mNumParsedFrames=%" PRIu64 - " mFrameIndex=%" PRId64 " mFirstFrameOffset=%llu mOffset=%" PRIu64 - " SL=%llu NumBytes=%u", - vbr.IsTOCPresent(), AverageFrameLength(), mNumParsedFrames, mFrameIndex, - mFirstFrameOffset, mOffset, StreamLength(), vbr.NumBytes().valueOr(0)); - - return Duration(mFrameIndex); -} - -TimeUnit -MP3TrackDemuxer::ScanUntil(const TimeUnit& aTime) { - MP3LOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64 - " mFrameIndex=%" PRId64 " mOffset=%" PRIu64, - aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames, - mFrameIndex, mOffset); - - if (!aTime.ToMicroseconds()) { - return FastSeek(aTime); - } - - if (Duration(mFrameIndex) > aTime) { - FastSeek(aTime); - } - - if (Duration(mFrameIndex + 1) > aTime) { - return SeekPosition(); - } - - MediaByteRange nextRange = FindNextFrame(); - while (SkipNextFrame(nextRange) && Duration(mFrameIndex + 1) < aTime) { - nextRange = FindNextFrame(); - MP3LOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64 - " mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64, - AverageFrameLength(), mNumParsedFrames, - mFrameIndex, mOffset, Duration(mFrameIndex + 1).ToMicroseconds()); - } - - MP3LOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64 - " mFrameIndex=%" PRId64 " mOffset=%" PRIu64, - AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset); - - return SeekPosition(); -} - -RefPtr -MP3TrackDemuxer::GetSamples(int32_t aNumSamples) { - MP3LOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 - " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d " - "mSamplesPerSecond=%d mChannels=%d", - aNumSamples, mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, - mSamplesPerFrame, mSamplesPerSecond, mChannels); - - if (!aNumSamples) { - return SamplesPromise::CreateAndReject( - NS_ERROR_DOM_MEDIA_DEMUXER_ERR, __func__); - } - - RefPtr frames = new SamplesHolder(); - - while (aNumSamples--) { - RefPtr frame(GetNextFrame(FindNextFrame())); - if (!frame) { - break; - } - - frames->mSamples.AppendElement(frame); - } - - MP3LOGV("GetSamples() End mSamples.Size()=%d aNumSamples=%d mOffset=%" PRIu64 - " mNumParsedFrames=%" PRIu64 " mFrameIndex=%" PRId64 - " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d " - "mChannels=%d", - frames->mSamples.Length(), aNumSamples, mOffset, mNumParsedFrames, - mFrameIndex, mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, - mChannels); - - if (frames->mSamples.IsEmpty()) { - return SamplesPromise::CreateAndReject( - NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__); - } - return SamplesPromise::CreateAndResolve(frames, __func__); -} - -void -MP3TrackDemuxer::Reset() { - MP3LOG("Reset()"); - - FastSeek(TimeUnit()); - mParser.Reset(); -} - -RefPtr -MP3TrackDemuxer::SkipToNextRandomAccessPoint(TimeUnit aTimeThreshold) { - // Will not be called for audio-only resources. - return SkipAccessPointPromise::CreateAndReject( - SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__); -} - -int64_t -MP3TrackDemuxer::GetResourceOffset() const { - return mOffset; -} - -TimeIntervals -MP3TrackDemuxer::GetBuffered() { - AutoPinned stream(mSource.GetResource()); - TimeIntervals buffered; - - if (Duration() > TimeUnit() && stream->IsDataCachedToEndOfResource(0)) { - // Special case completely cached files. This also handles local files. - buffered += TimeInterval(TimeUnit(), Duration()); - MP3LOGV("buffered = [[%" PRId64 ", %" PRId64 "]]", - TimeUnit().ToMicroseconds(), Duration().ToMicroseconds()); - return buffered; - } - - MediaByteRangeSet ranges; - nsresult rv = stream->GetCachedRanges(ranges); - NS_ENSURE_SUCCESS(rv, buffered); - - for (const auto& range: ranges) { - if (range.IsEmpty()) { - continue; - } - TimeUnit start = Duration(FrameIndexFromOffset(range.mStart)); - TimeUnit end = Duration(FrameIndexFromOffset(range.mEnd)); - MP3LOGV("buffered += [%" PRId64 ", %" PRId64 "]", - start.ToMicroseconds(), end.ToMicroseconds()); - buffered += TimeInterval(start, end); - } - - return buffered; -} - -int64_t -MP3TrackDemuxer::StreamLength() const { - return mSource.GetLength(); -} - -TimeUnit -MP3TrackDemuxer::Duration() const { - if (!mNumParsedFrames) { - return TimeUnit::FromMicroseconds(-1); - } - - int64_t numFrames = 0; - const auto numAudioFrames = mParser.VBRInfo().NumAudioFrames(); - if (mParser.VBRInfo().IsValid() && numAudioFrames.valueOr(0) + 1 > 1) { - // VBR headers don't include the VBR header frame. - numFrames = numAudioFrames.value() + 1; - } else { - const int64_t streamLen = StreamLength(); - if (streamLen < 0) { - // Unknown length, we can't estimate duration. - return TimeUnit::FromMicroseconds(-1); - } - if (AverageFrameLength() > 0) { - numFrames = (streamLen - mFirstFrameOffset) / AverageFrameLength(); - } - } - return Duration(numFrames); -} - -TimeUnit -MP3TrackDemuxer::Duration(int64_t aNumFrames) const { - if (!mSamplesPerSecond) { - return TimeUnit::FromMicroseconds(-1); - } - - const double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond; - return TimeUnit::FromMicroseconds(aNumFrames * usPerFrame); -} - -MediaByteRange -MP3TrackDemuxer::FindFirstFrame() { - static const int MIN_SUCCESSIVE_FRAMES = 4; - - MediaByteRange candidateFrame = FindNextFrame(); - int numSuccFrames = candidateFrame.Length() > 0; - MediaByteRange currentFrame = candidateFrame; - MP3LOGV("FindFirst() first candidate frame: mOffset=%" PRIu64 " Length()=%" PRIu64, - candidateFrame.mStart, candidateFrame.Length()); - - while (candidateFrame.Length() && numSuccFrames < MIN_SUCCESSIVE_FRAMES) { - mParser.EndFrameSession(); - mOffset = currentFrame.mEnd; - const MediaByteRange prevFrame = currentFrame; - - // FindNextFrame() here will only return frames consistent with our candidate frame. - currentFrame = FindNextFrame(); - numSuccFrames += currentFrame.Length() > 0; - // Multiple successive false positives, which wouldn't be caught by the consistency - // checks alone, can be detected by wrong alignment (non-zero gap between frames). - const int64_t frameSeparation = currentFrame.mStart - prevFrame.mEnd; - - if (!currentFrame.Length() || frameSeparation != 0) { - MP3LOGV("FindFirst() not enough successive frames detected, " - "rejecting candidate frame: successiveFrames=%d, last Length()=%" PRIu64 - ", last frameSeparation=%" PRId64, numSuccFrames, currentFrame.Length(), - frameSeparation); - - mParser.ResetFrameData(); - mOffset = candidateFrame.mStart + 1; - candidateFrame = FindNextFrame(); - numSuccFrames = candidateFrame.Length() > 0; - currentFrame = candidateFrame; - MP3LOGV("FindFirst() new candidate frame: mOffset=%" PRIu64 " Length()=%" PRIu64, - candidateFrame.mStart, candidateFrame.Length()); - } - } - - if (numSuccFrames >= MIN_SUCCESSIVE_FRAMES) { - MP3LOG("FindFirst() accepting candidate frame: " - "successiveFrames=%d", numSuccFrames); - } else { - MP3LOG("FindFirst() no suitable first frame found"); - } - return candidateFrame; -} - -static bool -VerifyFrameConsistency( - const FrameParser::Frame& aFrame1, const FrameParser::Frame& aFrame2) { - const auto& h1 = aFrame1.Header(); - const auto& h2 = aFrame2.Header(); - - return h1.IsValid() && h2.IsValid() && - h1.Layer() == h2.Layer() && - h1.SlotSize() == h2.SlotSize() && - h1.SamplesPerFrame() == h2.SamplesPerFrame() && - h1.Channels() == h2.Channels() && - h1.SampleRate() == h2.SampleRate() && - h1.RawVersion() == h2.RawVersion() && - h1.RawProtection() == h2.RawProtection(); -} - -MediaByteRange -MP3TrackDemuxer::FindNextFrame() { - static const int BUFFER_SIZE = 64; - static const int MAX_SKIPPED_BYTES = 1024 * BUFFER_SIZE; - - MP3LOGV("FindNext() Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 - " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 - " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d", - mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, - mSamplesPerFrame, mSamplesPerSecond, mChannels); - - uint8_t buffer[BUFFER_SIZE]; - int32_t read = 0; - - bool foundFrame = false; - int64_t frameHeaderOffset = 0; - - // Check whether we've found a valid MPEG frame. - while (!foundFrame) { - if ((!mParser.FirstFrame().Length() && - mOffset - mParser.ID3Header().Size() > MAX_SKIPPED_BYTES) || - (read = Read(buffer, mOffset, BUFFER_SIZE)) == 0) { - MP3LOG("FindNext() EOS or exceeded MAX_SKIPPED_BYTES without a frame"); - // This is not a valid MPEG audio stream or we've reached EOS, give up. - break; - } - - ByteReader reader(buffer, read); - uint32_t bytesToSkip = 0; - foundFrame = mParser.Parse(&reader, &bytesToSkip); - frameHeaderOffset = mOffset + reader.Offset() - FrameParser::FrameHeader::SIZE; - - // If we've found neither an MPEG frame header nor an ID3v2 tag, - // the reader shouldn't have any bytes remaining. - MOZ_ASSERT(foundFrame || bytesToSkip || !reader.Remaining()); - - if (foundFrame && mParser.FirstFrame().Length() && - !VerifyFrameConsistency(mParser.FirstFrame(), mParser.CurrentFrame())) { - // We've likely hit a false-positive, ignore it and proceed with the - // search for the next valid frame. - foundFrame = false; - mOffset = frameHeaderOffset + 1; - mParser.EndFrameSession(); - } else { - // Advance mOffset by the amount of bytes read and if necessary, - // skip an ID3v2 tag which stretches beyond the current buffer. - NS_ENSURE_TRUE(mOffset + read + bytesToSkip > mOffset, - MediaByteRange(0, 0)); - mOffset += read + bytesToSkip; - } - } - - if (!foundFrame || !mParser.CurrentFrame().Length()) { - MP3LOG("FindNext() Exit foundFrame=%d mParser.CurrentFrame().Length()=%d ", - foundFrame, mParser.CurrentFrame().Length()); - return { 0, 0 }; - } - - MP3LOGV("FindNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 - " mFrameIndex=%" PRId64 " frameHeaderOffset=%d" - " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d" - " mChannels=%d", - mOffset, mNumParsedFrames, mFrameIndex, frameHeaderOffset, - mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, mChannels); - - return { frameHeaderOffset, frameHeaderOffset + mParser.CurrentFrame().Length() }; -} - -bool -MP3TrackDemuxer::SkipNextFrame(const MediaByteRange& aRange) { - if (!mNumParsedFrames || !aRange.Length()) { - // We can't skip the first frame, since it could contain VBR headers. - RefPtr frame(GetNextFrame(aRange)); - return frame; - } - - UpdateState(aRange); - - MP3LOGV("SkipNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 - " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 - " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d", - mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, - mSamplesPerFrame, mSamplesPerSecond, mChannels); - - return true; -} - -already_AddRefed -MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange) { - MP3LOG("GetNext() Begin({mStart=%" PRId64 " Length()=%" PRId64 "})", - aRange.mStart, aRange.Length()); - if (!aRange.Length()) { - return nullptr; - } - - RefPtr frame = new MediaRawData(); - frame->mOffset = aRange.mStart; - - nsAutoPtr frameWriter(frame->CreateWriter()); - if (!frameWriter->SetSize(aRange.Length())) { - MP3LOG("GetNext() Exit failed to allocated media buffer"); - return nullptr; - } - - const uint32_t read = Read(frameWriter->Data(), frame->mOffset, frame->Size()); - - if (read != aRange.Length()) { - MP3LOG("GetNext() Exit read=%u frame->Size()=%u", read, frame->Size()); - return nullptr; - } - - UpdateState(aRange); - - frame->mTime = Duration(mFrameIndex - 1).ToMicroseconds(); - frame->mDuration = Duration(1).ToMicroseconds(); - frame->mTimecode = frame->mTime; - frame->mKeyframe = true; - - MOZ_ASSERT(frame->mTime >= 0); - MOZ_ASSERT(frame->mDuration > 0); - - if (mNumParsedFrames == 1) { - // First frame parsed, let's read VBR info if available. - ByteReader reader(frame->Data(), frame->Size()); - mParser.ParseVBRHeader(&reader); - mFirstFrameOffset = frame->mOffset; - } - - MP3LOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 - " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 - " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d", - mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, - mSamplesPerFrame, mSamplesPerSecond, mChannels); - - return frame.forget(); -} - -int64_t -MP3TrackDemuxer::OffsetFromFrameIndex(int64_t aFrameIndex) const { - int64_t offset = 0; - const auto& vbr = mParser.VBRInfo(); - - if (vbr.IsComplete()) { - offset = mFirstFrameOffset + aFrameIndex * vbr.NumBytes().value() / - vbr.NumAudioFrames().value(); - } else if (AverageFrameLength() > 0) { - offset = mFirstFrameOffset + aFrameIndex * AverageFrameLength(); - } - - MP3LOGV("OffsetFromFrameIndex(%" PRId64 ") -> %" PRId64, aFrameIndex, offset); - return std::max(mFirstFrameOffset, offset); -} - -int64_t -MP3TrackDemuxer::FrameIndexFromOffset(int64_t aOffset) const { - int64_t frameIndex = 0; - const auto& vbr = mParser.VBRInfo(); - - if (vbr.IsComplete()) { - frameIndex = static_cast(aOffset - mFirstFrameOffset) / - vbr.NumBytes().value() * vbr.NumAudioFrames().value(); - frameIndex = std::min(vbr.NumAudioFrames().value(), frameIndex); - } else if (AverageFrameLength() > 0) { - frameIndex = (aOffset - mFirstFrameOffset) / AverageFrameLength(); - } - - MP3LOGV("FrameIndexFromOffset(%" PRId64 ") -> %" PRId64, aOffset, frameIndex); - return std::max(0, frameIndex); -} - -int64_t -MP3TrackDemuxer::FrameIndexFromTime(const media::TimeUnit& aTime) const { - int64_t frameIndex = 0; - if (mSamplesPerSecond > 0 && mSamplesPerFrame > 0) { - frameIndex = aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerFrame - 1; - } - - MP3LOGV("FrameIndexFromOffset(%fs) -> %" PRId64, aTime.ToSeconds(), frameIndex); - return std::max(0, frameIndex); -} - -void -MP3TrackDemuxer::UpdateState(const MediaByteRange& aRange) { - // Prevent overflow. - if (mTotalFrameLen + aRange.Length() < mTotalFrameLen) { - // These variables have a linear dependency and are only used to derive the - // average frame length. - mTotalFrameLen /= 2; - mNumParsedFrames /= 2; - } - - // Full frame parsed, move offset to its end. - mOffset = aRange.mEnd; - - mTotalFrameLen += aRange.Length(); - - if (!mSamplesPerFrame) { - mSamplesPerFrame = mParser.CurrentFrame().Header().SamplesPerFrame(); - mSamplesPerSecond = mParser.CurrentFrame().Header().SampleRate(); - mChannels = mParser.CurrentFrame().Header().Channels(); - } - - ++mNumParsedFrames; - ++mFrameIndex; - MOZ_ASSERT(mFrameIndex > 0); - - // Prepare the parser for the next frame parsing session. - mParser.EndFrameSession(); -} - -int32_t -MP3TrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize) { - MP3LOGV("MP3TrackDemuxer::Read(%p %" PRId64 " %d)", aBuffer, aOffset, aSize); - - const int64_t streamLen = StreamLength(); - if (mInfo && streamLen > 0) { - // Prevent blocking reads after successful initialization. - aSize = std::min(aSize, streamLen - aOffset); - } - - uint32_t read = 0; - MP3LOGV("MP3TrackDemuxer::Read -> ReadAt(%d)", aSize); - const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast(aBuffer), - static_cast(aSize), &read); - NS_ENSURE_SUCCESS(rv, 0); - return static_cast(read); -} - -double -MP3TrackDemuxer::AverageFrameLength() const { - if (mNumParsedFrames) { - return static_cast(mTotalFrameLen) / mNumParsedFrames; - } - const auto& vbr = mParser.VBRInfo(); - if (vbr.IsComplete() && vbr.NumAudioFrames().value() + 1) { - return static_cast(vbr.NumBytes().value()) / - (vbr.NumAudioFrames().value() + 1); - } - return 0.0; -} - -// FrameParser - -namespace frame_header { -// FrameHeader mRaw byte offsets. -static const int SYNC1 = 0; -static const int SYNC2_VERSION_LAYER_PROTECTION = 1; -static const int BITRATE_SAMPLERATE_PADDING_PRIVATE = 2; -static const int CHANNELMODE_MODEEXT_COPY_ORIG_EMPH = 3; -} // namespace frame_header - -FrameParser::FrameParser() -{ -} - -void -FrameParser::Reset() { - mID3Parser.Reset(); - mFrame.Reset(); -} - -void -FrameParser::ResetFrameData() { - mFrame.Reset(); - mFirstFrame.Reset(); - mPrevFrame.Reset(); -} - -void -FrameParser::EndFrameSession() { - if (!mID3Parser.Header().IsValid()) { - // Reset ID3 tags only if we have not parsed a valid ID3 header yet. - mID3Parser.Reset(); - } - mPrevFrame = mFrame; - mFrame.Reset(); -} - -const FrameParser::Frame& -FrameParser::CurrentFrame() const { - return mFrame; -} - -const FrameParser::Frame& -FrameParser::PrevFrame() const { - return mPrevFrame; -} - -const FrameParser::Frame& -FrameParser::FirstFrame() const { - return mFirstFrame; -} - -const ID3Parser::ID3Header& -FrameParser::ID3Header() const { - return mID3Parser.Header(); -} - -const FrameParser::VBRHeader& -FrameParser::VBRInfo() const { - return mVBRHeader; -} - -bool -FrameParser::Parse(ByteReader* aReader, uint32_t* aBytesToSkip) { - MOZ_ASSERT(aReader && aBytesToSkip); - *aBytesToSkip = 0; - - if (!mID3Parser.Header().Size() && !mFirstFrame.Length()) { - // No MP3 frames have been parsed yet, look for ID3v2 headers at file begin. - // ID3v1 tags may only be at file end. - // TODO: should we try to read ID3 tags at end of file/mid-stream, too? - const size_t prevReaderOffset = aReader->Offset(); - const uint32_t tagSize = mID3Parser.Parse(aReader); - if (tagSize) { - // ID3 tag found, skip past it. - const uint32_t skipSize = tagSize - ID3Parser::ID3Header::SIZE; - - if (skipSize > aReader->Remaining()) { - // Skipping across the ID3v2 tag would take us past the end of the buffer, therefore we - // return immediately and let the calling function handle skipping the rest of the tag. - MP3LOGV("ID3v2 tag detected, size=%d," - " needing to skip %d bytes past the current buffer", - tagSize, skipSize - aReader->Remaining()); - *aBytesToSkip = skipSize - aReader->Remaining(); - return false; - } - MP3LOGV("ID3v2 tag detected, size=%d", tagSize); - aReader->Read(skipSize); - } else { - // No ID3v2 tag found, rewinding reader in order to search for a MPEG frame header. - aReader->Seek(prevReaderOffset); - } - } - - while (aReader->CanRead8() && !mFrame.ParseNext(aReader->ReadU8())) { } - - if (mFrame.Length()) { - // MP3 frame found. - if (!mFirstFrame.Length()) { - mFirstFrame = mFrame; - } - // Indicate success. - return true; - } - return false; -} - -// FrameParser::Header - -FrameParser::FrameHeader::FrameHeader() -{ - Reset(); -} - -uint8_t -FrameParser::FrameHeader::Sync1() const { - return mRaw[frame_header::SYNC1]; -} - -uint8_t -FrameParser::FrameHeader::Sync2() const { - return 0x7 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 5; -} - -uint8_t -FrameParser::FrameHeader::RawVersion() const { - return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 3; -} - -uint8_t -FrameParser::FrameHeader::RawLayer() const { - return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 1; -} - -uint8_t -FrameParser::FrameHeader::RawProtection() const { - return 0x1 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 6; -} - -uint8_t -FrameParser::FrameHeader::RawBitrate() const { - return 0xF & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 4; -} - -uint8_t -FrameParser::FrameHeader::RawSampleRate() const { - return 0x3 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 2; -} - -uint8_t -FrameParser::FrameHeader::Padding() const { - return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 1; -} - -uint8_t -FrameParser::FrameHeader::Private() const { - return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE]; -} - -uint8_t -FrameParser::FrameHeader::RawChannelMode() const { - return 0x3 & mRaw[frame_header::CHANNELMODE_MODEEXT_COPY_ORIG_EMPH] >> 6; -} - -int32_t -FrameParser::FrameHeader::Layer() const { - static const uint8_t LAYERS[4] = { 0, 3, 2, 1 }; - - return LAYERS[RawLayer()]; -} - -int32_t -FrameParser::FrameHeader::SampleRate() const { - // Sample rates - use [version][srate] - static const uint16_t SAMPLE_RATE[4][4] = { - { 11025, 12000, 8000, 0 }, // MPEG 2.5 - { 0, 0, 0, 0 }, // Reserved - { 22050, 24000, 16000, 0 }, // MPEG 2 - { 44100, 48000, 32000, 0 } // MPEG 1 - }; - - return SAMPLE_RATE[RawVersion()][RawSampleRate()]; -} - -int32_t -FrameParser::FrameHeader::Channels() const { - // 3 is single channel (mono), any other value is some variant of dual - // channel. - return RawChannelMode() == 3 ? 1 : 2; -} - -int32_t -FrameParser::FrameHeader::SamplesPerFrame() const { - // Samples per frame - use [version][layer] - static const uint16_t FRAME_SAMPLE[4][4] = { - // Layer 3 2 1 Version - { 0, 576, 1152, 384 }, // 2.5 - { 0, 0, 0, 0 }, // Reserved - { 0, 576, 1152, 384 }, // 2 - { 0, 1152, 1152, 384 } // 1 - }; - - return FRAME_SAMPLE[RawVersion()][RawLayer()]; -} - -int32_t -FrameParser::FrameHeader::Bitrate() const { - // Bitrates - use [version][layer][bitrate] - static const uint16_t BITRATE[4][4][16] = { - { // Version 2.5 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3 - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2 - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1 - }, - { // Reserved - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // Invalid - }, - { // Version 2 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3 - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2 - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1 - }, - { // Version 1 - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved - { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer 3 - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer 2 - { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // Layer 1 - } - }; - - return 1000 * BITRATE[RawVersion()][RawLayer()][RawBitrate()]; -} - -int32_t -FrameParser::FrameHeader::SlotSize() const { - // Slot size (MPEG unit of measurement) - use [layer] - static const uint8_t SLOT_SIZE[4] = { 0, 1, 1, 4 }; // Rsvd, 3, 2, 1 - - return SLOT_SIZE[RawLayer()]; -} - -bool -FrameParser::FrameHeader::ParseNext(uint8_t c) { - if (!Update(c)) { - Reset(); - if (!Update(c)) { - Reset(); - } - } - return IsValid(); -} - -bool -FrameParser::FrameHeader::IsValid(int aPos) const { - if (aPos >= SIZE) { - return true; - } - if (aPos == frame_header::SYNC1) { - return Sync1() == 0xFF; - } - if (aPos == frame_header::SYNC2_VERSION_LAYER_PROTECTION) { - return Sync2() == 7 && - RawVersion() != 1 && - Layer() == 3; - } - if (aPos == frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE) { - return RawBitrate() != 0xF && RawBitrate() != 0 && - RawSampleRate() != 3; - } - return true; -} - -bool -FrameParser::FrameHeader::IsValid() const { - return mPos >= SIZE; -} - -void -FrameParser::FrameHeader::Reset() { - mPos = 0; -} - -bool -FrameParser::FrameHeader::Update(uint8_t c) { - if (mPos < SIZE) { - mRaw[mPos] = c; - } - return IsValid(mPos++); -} - -// FrameParser::VBRHeader - -namespace vbr_header { -static const char* TYPE_STR[3] = {"NONE", "XING", "VBRI"}; -static const uint32_t TOC_SIZE = 100; -} // namespace vbr_header - -FrameParser::VBRHeader::VBRHeader() - : mType(NONE) -{ -} - -FrameParser::VBRHeader::VBRHeaderType -FrameParser::VBRHeader::Type() const { - return mType; -} - -const Maybe& -FrameParser::VBRHeader::NumAudioFrames() const { - return mNumAudioFrames; -} - -const Maybe& -FrameParser::VBRHeader::NumBytes() const { - return mNumBytes; -} - -const Maybe& -FrameParser::VBRHeader::Scale() const { - return mScale; -} - -bool -FrameParser::VBRHeader::IsTOCPresent() const { - return mTOC.size() == vbr_header::TOC_SIZE; -} - -bool -FrameParser::VBRHeader::IsValid() const { - return mType != NONE; -} - -bool -FrameParser::VBRHeader::IsComplete() const { - return IsValid() && - mNumAudioFrames.valueOr(0) > 0 && - mNumBytes.valueOr(0) > 0 && - // We don't care about the scale for any computations here. - // mScale < 101 && - true; -} - -int64_t -FrameParser::VBRHeader::Offset(float aDurationFac) const { - if (!IsTOCPresent()) { - return -1; - } - - // Constrain the duration percentage to [0, 99]. - const float durationPer = 100.0f * std::min(0.99f, std::max(0.0f, aDurationFac)); - const size_t fullPer = durationPer; - const float rest = durationPer - fullPer; - - MOZ_ASSERT(fullPer < mTOC.size()); - int64_t offset = mTOC.at(fullPer); - - if (rest > 0.0 && fullPer + 1 < mTOC.size()) { - offset += rest * (mTOC.at(fullPer + 1) - offset); - } - - return offset; -} - -bool -FrameParser::VBRHeader::ParseXing(ByteReader* aReader) { - static const uint32_t XING_TAG = BigEndian::readUint32("Xing"); - static const uint32_t INFO_TAG = BigEndian::readUint32("Info"); - - enum Flags { - NUM_FRAMES = 0x01, - NUM_BYTES = 0x02, - TOC = 0x04, - VBR_SCALE = 0x08 - }; - - MOZ_ASSERT(aReader); - const size_t prevReaderOffset = aReader->Offset(); - - // We have to search for the Xing header as its position can change. - while (aReader->CanRead32() && - aReader->PeekU32() != XING_TAG && aReader->PeekU32() != INFO_TAG) { - aReader->Read(1); - } - - if (aReader->CanRead32()) { - // Skip across the VBR header ID tag. - aReader->ReadU32(); - mType = XING; - } - uint32_t flags = 0; - if (aReader->CanRead32()) { - flags = aReader->ReadU32(); - } - if (flags & NUM_FRAMES && aReader->CanRead32()) { - mNumAudioFrames = Some(aReader->ReadU32()); - } - if (flags & NUM_BYTES && aReader->CanRead32()) { - mNumBytes = Some(aReader->ReadU32()); - } - if (flags & TOC && aReader->Remaining() >= vbr_header::TOC_SIZE) { - if (!mNumBytes) { - // We don't have the stream size to calculate offsets, skip the TOC. - aReader->Read(vbr_header::TOC_SIZE); - } else { - mTOC.clear(); - mTOC.reserve(vbr_header::TOC_SIZE); - for (size_t i = 0; i < vbr_header::TOC_SIZE; ++i) { - mTOC.push_back(1.0f / 256.0f * aReader->ReadU8() * mNumBytes.value()); - } - } - } - if (flags & VBR_SCALE && aReader->CanRead32()) { - mScale = Some(aReader->ReadU32()); - } - - aReader->Seek(prevReaderOffset); - return mType == XING; -} - -bool -FrameParser::VBRHeader::ParseVBRI(ByteReader* aReader) { - static const uint32_t TAG = BigEndian::readUint32("VBRI"); - static const uint32_t OFFSET = 32 + FrameParser::FrameHeader::SIZE; - static const uint32_t FRAME_COUNT_OFFSET = OFFSET + 14; - static const uint32_t MIN_FRAME_SIZE = OFFSET + 26; - - MOZ_ASSERT(aReader); - // ParseVBRI assumes that the ByteReader offset points to the beginning of a frame, - // therefore as a simple check, we look for the presence of a frame sync at that position. - MOZ_ASSERT((aReader->PeekU16() & 0xFFE0) == 0xFFE0); - const size_t prevReaderOffset = aReader->Offset(); - - // VBRI have a fixed relative position, so let's check for it there. - if (aReader->Remaining() > MIN_FRAME_SIZE) { - aReader->Seek(prevReaderOffset + OFFSET); - if (aReader->ReadU32() == TAG) { - aReader->Seek(prevReaderOffset + FRAME_COUNT_OFFSET); - mNumAudioFrames = Some(aReader->ReadU32()); - mType = VBRI; - aReader->Seek(prevReaderOffset); - return true; - } - } - aReader->Seek(prevReaderOffset); - return false; -} - -bool -FrameParser::VBRHeader::Parse(ByteReader* aReader) { - const bool rv = ParseVBRI(aReader) || ParseXing(aReader); - if (rv) { - MP3LOG("VBRHeader::Parse found valid VBR/CBR header: type=%s" - " NumAudioFrames=%u NumBytes=%u Scale=%u TOC-size=%u", - vbr_header::TYPE_STR[Type()], NumAudioFrames().valueOr(0), - NumBytes().valueOr(0), Scale().valueOr(0), mTOC.size()); - } - return rv; -} - -// FrameParser::Frame - -void -FrameParser::Frame::Reset() { - mHeader.Reset(); -} - -int32_t -FrameParser::Frame::Length() const { - if (!mHeader.IsValid() || !mHeader.SampleRate()) { - return 0; - } - - const float bitsPerSample = mHeader.SamplesPerFrame() / 8.0f; - const int32_t frameLen = bitsPerSample * mHeader.Bitrate() / - mHeader.SampleRate() + - mHeader.Padding() * mHeader.SlotSize(); - return frameLen; -} - -bool -FrameParser::Frame::ParseNext(uint8_t c) { - return mHeader.ParseNext(c); -} - -const FrameParser::FrameHeader& -FrameParser::Frame::Header() const { - return mHeader; -} - -bool -FrameParser::ParseVBRHeader(ByteReader* aReader) { - return mVBRHeader.Parse(aReader); -} - -// ID3Parser - -// Constants -namespace id3_header { -static const int ID_LEN = 3; -static const int VERSION_LEN = 2; -static const int FLAGS_LEN = 1; -static const int SIZE_LEN = 4; - -static const int ID_END = ID_LEN; -static const int VERSION_END = ID_END + VERSION_LEN; -static const int FLAGS_END = VERSION_END + FLAGS_LEN; -static const int SIZE_END = FLAGS_END + SIZE_LEN; - -static const uint8_t ID[ID_LEN] = {'I', 'D', '3'}; - -static const uint8_t MIN_MAJOR_VER = 2; -static const uint8_t MAX_MAJOR_VER = 4; -} // namespace id3_header - -uint32_t -ID3Parser::Parse(ByteReader* aReader) { - MOZ_ASSERT(aReader); - - while (aReader->CanRead8() && !mHeader.ParseNext(aReader->ReadU8())) { } - - if (mHeader.IsValid()) { - // Header found, return total tag size. - return ID3Header::SIZE + Header().Size() + Header().FooterSize(); - } - return 0; -} - -void -ID3Parser::Reset() { - mHeader.Reset(); -} - -const ID3Parser::ID3Header& -ID3Parser::Header() const { - return mHeader; -} - -// ID3Parser::Header - -ID3Parser::ID3Header::ID3Header() -{ - Reset(); -} - -void -ID3Parser::ID3Header::Reset() { - mSize = 0; - mPos = 0; -} - -uint8_t -ID3Parser::ID3Header::MajorVersion() const { - return mRaw[id3_header::ID_END]; -} - -uint8_t -ID3Parser::ID3Header::MinorVersion() const { - return mRaw[id3_header::ID_END + 1]; -} - -uint8_t -ID3Parser::ID3Header::Flags() const { - return mRaw[id3_header::FLAGS_END - id3_header::FLAGS_LEN]; -} - -uint32_t -ID3Parser::ID3Header::Size() const { - if (!IsValid()) { - return 0; - } - return mSize; -} - -uint8_t -ID3Parser::ID3Header::FooterSize() const { - if (Flags() & (1 << 4)) { - return SIZE; - } - return 0; -} - -bool -ID3Parser::ID3Header::ParseNext(uint8_t c) { - if (!Update(c)) { - Reset(); - if (!Update(c)) { - Reset(); - } - } - return IsValid(); -} - -bool -ID3Parser::ID3Header::IsValid(int aPos) const { - if (aPos >= SIZE) { - return true; - } - const uint8_t c = mRaw[aPos]; - switch (aPos) { - case 0: case 1: case 2: - // Expecting "ID3". - return id3_header::ID[aPos] == c; - case 3: - return MajorVersion() >= id3_header::MIN_MAJOR_VER && - MajorVersion() <= id3_header::MAX_MAJOR_VER; - case 4: - return MinorVersion() < 0xFF; - case 5: - // Validate flags for supported versions, see bug 949036. - return ((0xFF >> MajorVersion()) & c) == 0; - case 6: case 7: case 8: case 9: - return c < 0x80; - } - return true; -} - -bool -ID3Parser::ID3Header::IsValid() const { - return mPos >= SIZE; -} - -bool -ID3Parser::ID3Header::Update(uint8_t c) { - if (mPos >= id3_header::SIZE_END - id3_header::SIZE_LEN && - mPos < id3_header::SIZE_END) { - mSize <<= 7; - mSize |= c; - } - if (mPos < SIZE) { - mRaw[mPos] = c; - } - return IsValid(mPos++); -} - -} // namespace mozilla diff --git a/dom/media/MP3Demuxer.h b/dom/media/MP3Demuxer.h deleted file mode 100644 index 5331c4d54..000000000 --- a/dom/media/MP3Demuxer.h +++ /dev/null @@ -1,472 +0,0 @@ -/* 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/. */ - -#ifndef MP3_DEMUXER_H_ -#define MP3_DEMUXER_H_ - -#include "mozilla/Attributes.h" -#include "mozilla/Maybe.h" -#include "MediaDataDemuxer.h" -#include "MediaResource.h" -#include "mp4_demuxer/ByteReader.h" -#include - -namespace mozilla { - -class MP3TrackDemuxer; - -class MP3Demuxer : public MediaDataDemuxer { -public: - // MediaDataDemuxer interface. - explicit MP3Demuxer(MediaResource* aSource); - RefPtr Init() override; - bool HasTrackType(TrackInfo::TrackType aType) const override; - uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override; - already_AddRefed GetTrackDemuxer( - TrackInfo::TrackType aType, uint32_t aTrackNumber) override; - bool IsSeekable() const override; - void NotifyDataArrived() override; - void NotifyDataRemoved() override; - -private: - // Synchronous initialization. - bool InitInternal(); - - RefPtr mSource; - RefPtr mTrackDemuxer; -}; - -// ID3 header parser state machine used by FrameParser. -// The header contains the following format (one byte per term): -// 'I' 'D' '3' MajorVersion MinorVersion Flags Size1 Size2 Size3 Size4 -// For more details see http://id3.org/id3v2.3.0. -class ID3Parser { -public: - // Holds the ID3 header and its parsing state. - class ID3Header { - public: - // The header size is static, see class comment. - static const int SIZE = 10; - - // Constructor. - ID3Header(); - - // Resets the state to allow for a new parsing session. - void Reset(); - - // The ID3 tags are versioned like this: ID3vMajorVersion.MinorVersion. - uint8_t MajorVersion() const; - uint8_t MinorVersion() const; - - // The ID3 flags field. - uint8_t Flags() const; - - // The derived size based on the provided size fields. - uint32_t Size() const; - - // Returns the size of an ID3v2.4 footer if present and zero otherwise. - uint8_t FooterSize() const; - - // Returns whether the parsed data is a valid ID3 header up to the given - // byte position. - bool IsValid(int aPos) const; - - // Returns whether the parsed data is a complete and valid ID3 header. - bool IsValid() const; - - // Parses the next provided byte. - // Returns whether the byte creates a valid sequence up to this point. - bool ParseNext(uint8_t c); - - private: - // Updates the parser state machine with the provided next byte. - // Returns whether the provided byte is a valid next byte in the sequence. - bool Update(uint8_t c); - - // The currently parsed byte sequence. - uint8_t mRaw[SIZE]; - - // The derived size as provided by the size fields. - // The header size fields holds a 4 byte sequence with each MSB set to 0, - // this bits need to be ignored when deriving the actual size. - uint32_t mSize; - - // The current byte position in the parsed sequence. Reset via Reset and - // incremented via Update. - int mPos; - }; - - // Returns the parsed ID3 header. Note: check for validity. - const ID3Header& Header() const; - - // Parses contents of given ByteReader for a valid ID3v2 header. - // Returns the total ID3v2 tag size if successful and zero otherwise. - uint32_t Parse(mp4_demuxer::ByteReader* aReader); - - // Resets the state to allow for a new parsing session. - void Reset(); - -private: - // The currently parsed ID3 header. Reset via Reset, updated via Parse. - ID3Header mHeader; -}; - -// MPEG audio frame parser. -// The MPEG frame header has the following format (one bit per character): -// 11111111 111VVLLC BBBBSSPR MMEETOHH -// { sync } - 11 sync bits -// VV - MPEG audio version ID (0->2.5, 1->reserved, 2->2, 3->1) -// LL - Layer description (0->reserved, 1->III, 2->II, 3->I) -// C - CRC protection bit (0->protected, 1->not protected) -// BBBB - Bitrate index (see table in implementation) -// SS - Sampling rate index (see table in implementation) -// P - Padding bit (0->not padded, 1->padded by 1 slot size) -// R - Private bit (ignored) -// MM - Channel mode (0->stereo, 1->joint stereo, 2->dual channel, -// 3->single channel) -// EE - Mode extension for joint stereo (ignored) -// T - Copyright (0->disabled, 1->enabled) -// O - Original (0->copy, 1->original) -// HH - Emphasis (0->none, 1->50/15 ms, 2->reserved, 3->CCIT J.17) -class FrameParser { -public: - // Holds the frame header and its parsing state. - class FrameHeader { - public: - // The header size is static, see class comments. - static const int SIZE = 4; - - // Constructor. - FrameHeader(); - - // Raw field access, see class comments for details. - uint8_t Sync1() const; - uint8_t Sync2() const; - uint8_t RawVersion() const; - uint8_t RawLayer() const; - uint8_t RawProtection() const; - uint8_t RawBitrate() const; - uint8_t RawSampleRate() const; - uint8_t Padding() const; - uint8_t Private() const; - uint8_t RawChannelMode() const; - - // Sampling rate frequency in Hz. - int32_t SampleRate() const; - - // Number of audio channels. - int32_t Channels() const; - - // Samples per frames, static depending on MPEG version and layer. - int32_t SamplesPerFrame() const; - - // Slot size used for padding, static depending on MPEG layer. - int32_t SlotSize() const; - - // Bitrate in kbps, can vary between frames. - int32_t Bitrate() const; - - // MPEG layer (0->invalid, 1->I, 2->II, 3->III). - int32_t Layer() const; - - // Returns whether the parsed data is a valid frame header up to the given - // byte position. - bool IsValid(const int aPos) const; - - // Returns whether the parsed data is a complete and valid frame header. - bool IsValid() const; - - // Resets the state to allow for a new parsing session. - void Reset(); - - // Parses the next provided byte. - // Returns whether the byte creates a valid sequence up to this point. - bool ParseNext(const uint8_t c); - - private: - // Updates the parser state machine with the provided next byte. - // Returns whether the provided byte is a valid next byte in the sequence. - bool Update(const uint8_t c); - - // The currently parsed byte sequence. - uint8_t mRaw[SIZE]; - - // The current byte position in the parsed sequence. Reset via Reset and - // incremented via Update. - int mPos; - }; - - // VBR frames may contain Xing or VBRI headers for additional info, we use - // this class to parse them and access this info. - class VBRHeader { - public: - // Synchronize with vbr_header TYPE_STR on change. - enum VBRHeaderType { - NONE = 0, - XING, - VBRI - }; - - // Constructor. - VBRHeader(); - - // Returns the parsed VBR header type, or NONE if no valid header found. - VBRHeaderType Type() const; - - // Returns the total number of audio frames (excluding the VBR header frame) - // expected in the stream/file. - const Maybe& NumAudioFrames() const; - - // Returns the expected size of the stream. - const Maybe& NumBytes() const; - - // Returns the VBR scale factor (0: best quality, 100: lowest quality). - const Maybe& Scale() const; - - // Returns true iff Xing/Info TOC (table of contents) is present. - bool IsTOCPresent() const; - - // Returns whether the header is valid (type XING or VBRI). - bool IsValid() const; - - // Returns whether the header is valid and contains reasonable non-zero field values. - bool IsComplete() const; - - // Returns the byte offset for the given duration percentage as a factor - // (0: begin, 1.0: end). - int64_t Offset(float aDurationFac) const; - - // Parses contents of given ByteReader for a valid VBR header. - // The offset of the passed ByteReader needs to point to an MPEG frame begin, - // as a VBRI-style header is searched at a fixed offset relative to frame begin. - // Returns whether a valid VBR header was found in the range. - bool Parse(mp4_demuxer::ByteReader* aReader); - - private: - // Parses contents of given ByteReader for a valid Xing header. - // The initial ByteReader offset will be preserved. - // Returns whether a valid Xing header was found in the range. - bool ParseXing(mp4_demuxer::ByteReader* aReader); - - // Parses contents of given ByteReader for a valid VBRI header. - // The initial ByteReader offset will be preserved. It also needs to point - // to the beginning of a valid MPEG frame, as VBRI headers are searched - // at a fixed offset relative to frame begin. - // Returns whether a valid VBRI header was found in the range. - bool ParseVBRI(mp4_demuxer::ByteReader* aReader); - - // The total number of frames expected as parsed from a VBR header. - Maybe mNumAudioFrames; - - // The total number of bytes expected in the stream. - Maybe mNumBytes; - - // The VBR scale factor. - Maybe mScale; - - // The TOC table mapping duration percentage to byte offset. - std::vector mTOC; - - // The detected VBR header type. - VBRHeaderType mType; - }; - - // Frame meta container used to parse and hold a frame header and side info. - class Frame { - public: - // Returns the length of the frame excluding the header in bytes. - int32_t Length() const; - - // Returns the parsed frame header. - const FrameHeader& Header() const; - - // Resets the frame header and data. - void Reset(); - - // Parses the next provided byte. - // Returns whether the byte creates a valid sequence up to this point. - bool ParseNext(uint8_t c); - - private: - // The currently parsed frame header. - FrameHeader mHeader; - }; - - // Constructor. - FrameParser(); - - // Returns the currently parsed frame. Reset via Reset or EndFrameSession. - const Frame& CurrentFrame() const; - - // Returns the previously parsed frame. Reset via Reset. - const Frame& PrevFrame() const; - - // Returns the first parsed frame. Reset via Reset. - const Frame& FirstFrame() const; - - // Returns the parsed ID3 header. Note: check for validity. - const ID3Parser::ID3Header& ID3Header() const; - - // Returns the parsed VBR header info. Note: check for validity by type. - const VBRHeader& VBRInfo() const; - - // Resets the parser. - void Reset(); - - // Resets all frame data, but not the ID3Header. - // Don't use between frames as first frame data is reset. - void ResetFrameData(); - - // Clear the last parsed frame to allow for next frame parsing, i.e.: - // - sets PrevFrame to CurrentFrame - // - resets the CurrentFrame - // - resets ID3Header if no valid header was parsed yet - void EndFrameSession(); - - // Parses contents of given ByteReader for a valid frame header and returns true - // if one was found. After returning, the variable passed to 'aBytesToSkip' holds - // the amount of bytes to be skipped (if any) in order to jump across a large - // ID3v2 tag spanning multiple buffers. - bool Parse(mp4_demuxer::ByteReader* aReader, uint32_t* aBytesToSkip); - - // Parses contents of given ByteReader for a valid VBR header. - // The offset of the passed ByteReader needs to point to an MPEG frame begin, - // as a VBRI-style header is searched at a fixed offset relative to frame begin. - // Returns whether a valid VBR header was found. - bool ParseVBRHeader(mp4_demuxer::ByteReader* aReader); - -private: - // ID3 header parser. - ID3Parser mID3Parser; - - // VBR header parser. - VBRHeader mVBRHeader; - - // We keep the first parsed frame around for static info access, the - // previously parsed frame for debugging and the currently parsed frame. - Frame mFirstFrame; - Frame mFrame; - Frame mPrevFrame; -}; - -// The MP3 demuxer used to extract MPEG frames and side information out of -// MPEG streams. -class MP3TrackDemuxer : public MediaTrackDemuxer { -public: - // Constructor, expecting a valid media resource. - explicit MP3TrackDemuxer(MediaResource* aSource); - - // Initializes the track demuxer by reading the first frame for meta data. - // Returns initialization success state. - bool Init(); - - // Returns the total stream length if known, -1 otherwise. - int64_t StreamLength() const; - - // Returns the estimated stream duration, or a 0-duration if unknown. - media::TimeUnit Duration() const; - - // Returns the estimated duration up to the given frame number, - // or a 0-duration if unknown. - media::TimeUnit Duration(int64_t aNumFrames) const; - - // Returns the estimated current seek position time. - media::TimeUnit SeekPosition() const; - - const FrameParser::Frame& LastFrame() const; - RefPtr DemuxSample(); - - const ID3Parser::ID3Header& ID3Header() const; - const FrameParser::VBRHeader& VBRInfo() const; - - // MediaTrackDemuxer interface. - UniquePtr GetInfo() const override; - RefPtr Seek(media::TimeUnit aTime) override; - RefPtr GetSamples(int32_t aNumSamples = 1) override; - void Reset() override; - RefPtr SkipToNextRandomAccessPoint( - media::TimeUnit aTimeThreshold) override; - int64_t GetResourceOffset() const override; - media::TimeIntervals GetBuffered() override; - -private: - // Destructor. - ~MP3TrackDemuxer() {} - - // Fast approximate seeking to given time. - media::TimeUnit FastSeek(const media::TimeUnit& aTime); - - // Seeks by scanning the stream up to the given time for more accurate results. - media::TimeUnit ScanUntil(const media::TimeUnit& aTime); - - // Finds the first valid frame and returns its byte range if found - // or a null-byte range otherwise. - MediaByteRange FindFirstFrame(); - - // Finds the next valid frame and returns its byte range if found - // or a null-byte range otherwise. - MediaByteRange FindNextFrame(); - - // Skips the next frame given the provided byte range. - bool SkipNextFrame(const MediaByteRange& aRange); - - // Returns the next MPEG frame, if available. - already_AddRefed GetNextFrame(const MediaByteRange& aRange); - - // Updates post-read meta data. - void UpdateState(const MediaByteRange& aRange); - - // Returns the estimated offset for the given frame index. - int64_t OffsetFromFrameIndex(int64_t aFrameIndex) const; - - // Returns the estimated frame index for the given offset. - int64_t FrameIndexFromOffset(int64_t aOffset) const; - - // Returns the estimated frame index for the given time. - int64_t FrameIndexFromTime(const media::TimeUnit& aTime) const; - - // Reads aSize bytes into aBuffer from the source starting at aOffset. - // Returns the actual size read. - int32_t Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize); - - // Returns the average frame length derived from the previously parsed frames. - double AverageFrameLength() const; - - // The (hopefully) MPEG resource. - MediaResourceIndex mSource; - - // MPEG frame parser used to detect frames and extract side info. - FrameParser mParser; - - // Current byte offset in the source stream. - int64_t mOffset; - - // Byte offset of the begin of the first frame, or 0 if none parsed yet. - int64_t mFirstFrameOffset; - - // Total parsed frames. - uint64_t mNumParsedFrames; - - // Current frame index. - int64_t mFrameIndex; - - // Sum of parsed frames' lengths in bytes. - uint64_t mTotalFrameLen; - - // Samples per frame metric derived from frame headers or 0 if none available. - int32_t mSamplesPerFrame; - - // Samples per second metric derived from frame headers or 0 if none available. - int32_t mSamplesPerSecond; - - // Channel count derived from frame headers or 0 if none available. - int32_t mChannels; - - // Audio track config info. - UniquePtr mInfo; -}; - -} // namespace mozilla - -#endif diff --git a/dom/media/moz.build b/dom/media/moz.build index 772f27b35..6d2c71f2d 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -30,6 +30,7 @@ DIRS += [ 'ipc', 'mediasink', 'mediasource', + 'mp3', 'ogg', 'platforms', 'systemservices', @@ -125,8 +126,6 @@ EXPORTS += [ 'MediaTimer.h', 'MediaTrack.h', 'MediaTrackList.h', - 'MP3Decoder.h', - 'MP3Demuxer.h', 'NextFrameSeekTask.h', 'nsIDocumentActivity.h', 'PrincipalChangeObserver.h', @@ -233,8 +232,6 @@ UNIFIED_SOURCES += [ 'MediaTimer.cpp', 'MediaTrack.cpp', 'MediaTrackList.cpp', - 'MP3Decoder.cpp', - 'MP3Demuxer.cpp', 'NextFrameSeekTask.cpp', 'QueueObject.cpp', 'SeekJob.cpp', diff --git a/dom/media/mp3/MP3Decoder.cpp b/dom/media/mp3/MP3Decoder.cpp new file mode 100644 index 000000000..074a0866d --- /dev/null +++ b/dom/media/mp3/MP3Decoder.cpp @@ -0,0 +1,50 @@ + +/* -*- 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 "MP3Decoder.h" +#include "MediaDecoderStateMachine.h" +#include "MediaFormatReader.h" +#include "MP3Demuxer.h" +#include "PDMFactory.h" + +namespace mozilla { + +MediaDecoder* +MP3Decoder::Clone(MediaDecoderOwner* aOwner) { + if (!IsEnabled()) { + return nullptr; + } + return new MP3Decoder(aOwner); +} + +MediaDecoderStateMachine* +MP3Decoder::CreateStateMachine() { + RefPtr reader = + new MediaFormatReader(this, new MP3Demuxer(GetResource())); + return new MediaDecoderStateMachine(this, reader); +} + +/* static */ +bool +MP3Decoder::IsEnabled() { + RefPtr platform = new PDMFactory(); + return platform->SupportsMimeType(NS_LITERAL_CSTRING("audio/mpeg"), + /* DecoderDoctorDiagnostics* */ nullptr); +} + +/* static */ +bool MP3Decoder::CanHandleMediaType(const nsACString& aType, + const nsAString& aCodecs) +{ + if (aType.EqualsASCII("audio/mp3") || aType.EqualsASCII("audio/mpeg")) { + return IsEnabled() && + (aCodecs.IsEmpty() || aCodecs.EqualsASCII("mp3")); + } + return false; +} + +} // namespace mozilla diff --git a/dom/media/mp3/MP3Decoder.h b/dom/media/mp3/MP3Decoder.h new file mode 100644 index 000000000..887251065 --- /dev/null +++ b/dom/media/mp3/MP3Decoder.h @@ -0,0 +1,29 @@ +/* -*- 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/. */ +#ifndef MP3Decoder_h_ +#define MP3Decoder_h_ + +#include "MediaDecoder.h" + +namespace mozilla { + +class MP3Decoder : public MediaDecoder { +public: + // MediaDecoder interface. + explicit MP3Decoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {} + MediaDecoder* Clone(MediaDecoderOwner* aOwner) override; + MediaDecoderStateMachine* CreateStateMachine() override; + + // Returns true if the MP3 backend is preffed 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 + +#endif diff --git a/dom/media/mp3/MP3Demuxer.cpp b/dom/media/mp3/MP3Demuxer.cpp new file mode 100644 index 000000000..5a98cabfe --- /dev/null +++ b/dom/media/mp3/MP3Demuxer.cpp @@ -0,0 +1,1340 @@ +/* -*- 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 "MP3Demuxer.h" + +#include +#include + +#include "mozilla/Assertions.h" +#include "mozilla/EndianUtils.h" +#include "nsAutoPtr.h" +#include "VideoUtils.h" +#include "TimeUnits.h" +#include "prenv.h" + +#ifdef PR_LOGGING +extern mozilla::LazyLogModule gMediaDemuxerLog; +#define MP3LOG(msg, ...) \ + MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__)) +#define MP3LOGV(msg, ...) \ + MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("MP3Demuxer " msg, ##__VA_ARGS__)) +#else +#define MP3LOG(msg, ...) +#define MP3LOGV(msg, ...) +#endif + +using mozilla::media::TimeUnit; +using mozilla::media::TimeInterval; +using mozilla::media::TimeIntervals; +using mp4_demuxer::ByteReader; + +namespace mozilla { + +// MP3Demuxer + +MP3Demuxer::MP3Demuxer(MediaResource* aSource) + : mSource(aSource) +{} + +bool +MP3Demuxer::InitInternal() { + if (!mTrackDemuxer) { + mTrackDemuxer = new MP3TrackDemuxer(mSource); + } + return mTrackDemuxer->Init(); +} + +RefPtr +MP3Demuxer::Init() { + if (!InitInternal()) { + MP3LOG("MP3Demuxer::Init() failure: waiting for data"); + + return InitPromise::CreateAndReject( + NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__); + } + + MP3LOG("MP3Demuxer::Init() successful"); + return InitPromise::CreateAndResolve(NS_OK, __func__); +} + +bool +MP3Demuxer::HasTrackType(TrackInfo::TrackType aType) const { + return aType == TrackInfo::kAudioTrack; +} + +uint32_t +MP3Demuxer::GetNumberTracks(TrackInfo::TrackType aType) const { + return aType == TrackInfo::kAudioTrack ? 1u : 0u; +} + +already_AddRefed +MP3Demuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber) { + if (!mTrackDemuxer) { + return nullptr; + } + return RefPtr(mTrackDemuxer).forget(); +} + +bool +MP3Demuxer::IsSeekable() const { + return true; +} + +void +MP3Demuxer::NotifyDataArrived() { + // TODO: bug 1169485. + NS_WARNING("Unimplemented function NotifyDataArrived"); + MP3LOGV("NotifyDataArrived()"); +} + +void +MP3Demuxer::NotifyDataRemoved() { + // TODO: bug 1169485. + NS_WARNING("Unimplemented function NotifyDataRemoved"); + MP3LOGV("NotifyDataRemoved()"); +} + + +// MP3TrackDemuxer + +MP3TrackDemuxer::MP3TrackDemuxer(MediaResource* aSource) + : mSource(aSource) + , mOffset(0) + , mFirstFrameOffset(0) + , mNumParsedFrames(0) + , mFrameIndex(0) + , mTotalFrameLen(0) + , mSamplesPerFrame(0) + , mSamplesPerSecond(0) + , mChannels(0) +{ + Reset(); +} + +bool +MP3TrackDemuxer::Init() { + Reset(); + FastSeek(TimeUnit()); + // Read the first frame to fetch sample rate and other meta data. + RefPtr frame(GetNextFrame(FindFirstFrame())); + + MP3LOG("Init StreamLength()=%" PRId64 " first-frame-found=%d", + StreamLength(), !!frame); + + if (!frame) { + return false; + } + + // Rewind back to the stream begin to avoid dropping the first frame. + FastSeek(TimeUnit()); + + if (!mInfo) { + mInfo = MakeUnique(); + } + + mInfo->mRate = mSamplesPerSecond; + mInfo->mChannels = mChannels; + mInfo->mBitDepth = 16; + mInfo->mMimeType = "audio/mpeg"; + mInfo->mDuration = Duration().ToMicroseconds(); + + MP3LOG("Init mInfo={mRate=%d mChannels=%d mBitDepth=%d mDuration=%" PRId64 "}", + mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth, + mInfo->mDuration); + + return mSamplesPerSecond && mChannels; +} + +media::TimeUnit +MP3TrackDemuxer::SeekPosition() const { + TimeUnit pos = Duration(mFrameIndex); + if (Duration() > TimeUnit()) { + pos = std::min(Duration(), pos); + } + return pos; +} + +const FrameParser::Frame& +MP3TrackDemuxer::LastFrame() const { + return mParser.PrevFrame(); +} + +RefPtr +MP3TrackDemuxer::DemuxSample() { + return GetNextFrame(FindNextFrame()); +} + +const ID3Parser::ID3Header& +MP3TrackDemuxer::ID3Header() const { + return mParser.ID3Header(); +} + +const FrameParser::VBRHeader& +MP3TrackDemuxer::VBRInfo() const { + return mParser.VBRInfo(); +} + +UniquePtr +MP3TrackDemuxer::GetInfo() const { + return mInfo->Clone(); +} + +RefPtr +MP3TrackDemuxer::Seek(TimeUnit aTime) { + // Efficiently seek to the position. + FastSeek(aTime); + // Correct seek position by scanning the next frames. + const TimeUnit seekTime = ScanUntil(aTime); + + return SeekPromise::CreateAndResolve(seekTime, __func__); +} + +TimeUnit +MP3TrackDemuxer::FastSeek(const TimeUnit& aTime) { + MP3LOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mOffset=%" PRIu64, + aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames, + mFrameIndex, mOffset); + + const auto& vbr = mParser.VBRInfo(); + if (!aTime.ToMicroseconds()) { + // Quick seek to the beginning of the stream. + mFrameIndex = 0; + } else if (vbr.IsTOCPresent() && Duration().ToMicroseconds() > 0) { + // Use TOC for more precise seeking. + const float durationFrac = static_cast(aTime.ToMicroseconds()) / + Duration().ToMicroseconds(); + mFrameIndex = FrameIndexFromOffset(vbr.Offset(durationFrac)); + } else if (AverageFrameLength() > 0) { + mFrameIndex = FrameIndexFromTime(aTime); + } + + mOffset = OffsetFromFrameIndex(mFrameIndex); + + if (mOffset > mFirstFrameOffset && StreamLength() > 0) { + mOffset = std::min(StreamLength() - 1, mOffset); + } + + mParser.EndFrameSession(); + + MP3LOG("FastSeek End TOC=%d avgFrameLen=%f mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mFirstFrameOffset=%llu mOffset=%" PRIu64 + " SL=%llu NumBytes=%u", + vbr.IsTOCPresent(), AverageFrameLength(), mNumParsedFrames, mFrameIndex, + mFirstFrameOffset, mOffset, StreamLength(), vbr.NumBytes().valueOr(0)); + + return Duration(mFrameIndex); +} + +TimeUnit +MP3TrackDemuxer::ScanUntil(const TimeUnit& aTime) { + MP3LOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mOffset=%" PRIu64, + aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames, + mFrameIndex, mOffset); + + if (!aTime.ToMicroseconds()) { + return FastSeek(aTime); + } + + if (Duration(mFrameIndex) > aTime) { + FastSeek(aTime); + } + + if (Duration(mFrameIndex + 1) > aTime) { + return SeekPosition(); + } + + MediaByteRange nextRange = FindNextFrame(); + while (SkipNextFrame(nextRange) && Duration(mFrameIndex + 1) < aTime) { + nextRange = FindNextFrame(); + MP3LOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64, + AverageFrameLength(), mNumParsedFrames, + mFrameIndex, mOffset, Duration(mFrameIndex + 1).ToMicroseconds()); + } + + MP3LOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mOffset=%" PRIu64, + AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset); + + return SeekPosition(); +} + +RefPtr +MP3TrackDemuxer::GetSamples(int32_t aNumSamples) { + MP3LOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d " + "mSamplesPerSecond=%d mChannels=%d", + aNumSamples, mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, + mSamplesPerFrame, mSamplesPerSecond, mChannels); + + if (!aNumSamples) { + return SamplesPromise::CreateAndReject( + NS_ERROR_DOM_MEDIA_DEMUXER_ERR, __func__); + } + + RefPtr frames = new SamplesHolder(); + + while (aNumSamples--) { + RefPtr frame(GetNextFrame(FindNextFrame())); + if (!frame) { + break; + } + + frames->mSamples.AppendElement(frame); + } + + MP3LOGV("GetSamples() End mSamples.Size()=%d aNumSamples=%d mOffset=%" PRIu64 + " mNumParsedFrames=%" PRIu64 " mFrameIndex=%" PRId64 + " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d " + "mChannels=%d", + frames->mSamples.Length(), aNumSamples, mOffset, mNumParsedFrames, + mFrameIndex, mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, + mChannels); + + if (frames->mSamples.IsEmpty()) { + return SamplesPromise::CreateAndReject( + NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__); + } + return SamplesPromise::CreateAndResolve(frames, __func__); +} + +void +MP3TrackDemuxer::Reset() { + MP3LOG("Reset()"); + + FastSeek(TimeUnit()); + mParser.Reset(); +} + +RefPtr +MP3TrackDemuxer::SkipToNextRandomAccessPoint(TimeUnit aTimeThreshold) { + // Will not be called for audio-only resources. + return SkipAccessPointPromise::CreateAndReject( + SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__); +} + +int64_t +MP3TrackDemuxer::GetResourceOffset() const { + return mOffset; +} + +TimeIntervals +MP3TrackDemuxer::GetBuffered() { + AutoPinned stream(mSource.GetResource()); + TimeIntervals buffered; + + if (Duration() > TimeUnit() && stream->IsDataCachedToEndOfResource(0)) { + // Special case completely cached files. This also handles local files. + buffered += TimeInterval(TimeUnit(), Duration()); + MP3LOGV("buffered = [[%" PRId64 ", %" PRId64 "]]", + TimeUnit().ToMicroseconds(), Duration().ToMicroseconds()); + return buffered; + } + + MediaByteRangeSet ranges; + nsresult rv = stream->GetCachedRanges(ranges); + NS_ENSURE_SUCCESS(rv, buffered); + + for (const auto& range: ranges) { + if (range.IsEmpty()) { + continue; + } + TimeUnit start = Duration(FrameIndexFromOffset(range.mStart)); + TimeUnit end = Duration(FrameIndexFromOffset(range.mEnd)); + MP3LOGV("buffered += [%" PRId64 ", %" PRId64 "]", + start.ToMicroseconds(), end.ToMicroseconds()); + buffered += TimeInterval(start, end); + } + + return buffered; +} + +int64_t +MP3TrackDemuxer::StreamLength() const { + return mSource.GetLength(); +} + +TimeUnit +MP3TrackDemuxer::Duration() const { + if (!mNumParsedFrames) { + return TimeUnit::FromMicroseconds(-1); + } + + int64_t numFrames = 0; + const auto numAudioFrames = mParser.VBRInfo().NumAudioFrames(); + if (mParser.VBRInfo().IsValid() && numAudioFrames.valueOr(0) + 1 > 1) { + // VBR headers don't include the VBR header frame. + numFrames = numAudioFrames.value() + 1; + } else { + const int64_t streamLen = StreamLength(); + if (streamLen < 0) { + // Unknown length, we can't estimate duration. + return TimeUnit::FromMicroseconds(-1); + } + if (AverageFrameLength() > 0) { + numFrames = (streamLen - mFirstFrameOffset) / AverageFrameLength(); + } + } + return Duration(numFrames); +} + +TimeUnit +MP3TrackDemuxer::Duration(int64_t aNumFrames) const { + if (!mSamplesPerSecond) { + return TimeUnit::FromMicroseconds(-1); + } + + const double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond; + return TimeUnit::FromMicroseconds(aNumFrames * usPerFrame); +} + +MediaByteRange +MP3TrackDemuxer::FindFirstFrame() { + static const int MIN_SUCCESSIVE_FRAMES = 4; + + MediaByteRange candidateFrame = FindNextFrame(); + int numSuccFrames = candidateFrame.Length() > 0; + MediaByteRange currentFrame = candidateFrame; + MP3LOGV("FindFirst() first candidate frame: mOffset=%" PRIu64 " Length()=%" PRIu64, + candidateFrame.mStart, candidateFrame.Length()); + + while (candidateFrame.Length() && numSuccFrames < MIN_SUCCESSIVE_FRAMES) { + mParser.EndFrameSession(); + mOffset = currentFrame.mEnd; + const MediaByteRange prevFrame = currentFrame; + + // FindNextFrame() here will only return frames consistent with our candidate frame. + currentFrame = FindNextFrame(); + numSuccFrames += currentFrame.Length() > 0; + // Multiple successive false positives, which wouldn't be caught by the consistency + // checks alone, can be detected by wrong alignment (non-zero gap between frames). + const int64_t frameSeparation = currentFrame.mStart - prevFrame.mEnd; + + if (!currentFrame.Length() || frameSeparation != 0) { + MP3LOGV("FindFirst() not enough successive frames detected, " + "rejecting candidate frame: successiveFrames=%d, last Length()=%" PRIu64 + ", last frameSeparation=%" PRId64, numSuccFrames, currentFrame.Length(), + frameSeparation); + + mParser.ResetFrameData(); + mOffset = candidateFrame.mStart + 1; + candidateFrame = FindNextFrame(); + numSuccFrames = candidateFrame.Length() > 0; + currentFrame = candidateFrame; + MP3LOGV("FindFirst() new candidate frame: mOffset=%" PRIu64 " Length()=%" PRIu64, + candidateFrame.mStart, candidateFrame.Length()); + } + } + + if (numSuccFrames >= MIN_SUCCESSIVE_FRAMES) { + MP3LOG("FindFirst() accepting candidate frame: " + "successiveFrames=%d", numSuccFrames); + } else { + MP3LOG("FindFirst() no suitable first frame found"); + } + return candidateFrame; +} + +static bool +VerifyFrameConsistency( + const FrameParser::Frame& aFrame1, const FrameParser::Frame& aFrame2) { + const auto& h1 = aFrame1.Header(); + const auto& h2 = aFrame2.Header(); + + return h1.IsValid() && h2.IsValid() && + h1.Layer() == h2.Layer() && + h1.SlotSize() == h2.SlotSize() && + h1.SamplesPerFrame() == h2.SamplesPerFrame() && + h1.Channels() == h2.Channels() && + h1.SampleRate() == h2.SampleRate() && + h1.RawVersion() == h2.RawVersion() && + h1.RawProtection() == h2.RawProtection(); +} + +MediaByteRange +MP3TrackDemuxer::FindNextFrame() { + static const int BUFFER_SIZE = 64; + static const int MAX_SKIPPED_BYTES = 1024 * BUFFER_SIZE; + + MP3LOGV("FindNext() Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 + " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d", + mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, + mSamplesPerFrame, mSamplesPerSecond, mChannels); + + uint8_t buffer[BUFFER_SIZE]; + int32_t read = 0; + + bool foundFrame = false; + int64_t frameHeaderOffset = 0; + + // Check whether we've found a valid MPEG frame. + while (!foundFrame) { + if ((!mParser.FirstFrame().Length() && + mOffset - mParser.ID3Header().Size() > MAX_SKIPPED_BYTES) || + (read = Read(buffer, mOffset, BUFFER_SIZE)) == 0) { + MP3LOG("FindNext() EOS or exceeded MAX_SKIPPED_BYTES without a frame"); + // This is not a valid MPEG audio stream or we've reached EOS, give up. + break; + } + + ByteReader reader(buffer, read); + uint32_t bytesToSkip = 0; + foundFrame = mParser.Parse(&reader, &bytesToSkip); + frameHeaderOffset = mOffset + reader.Offset() - FrameParser::FrameHeader::SIZE; + + // If we've found neither an MPEG frame header nor an ID3v2 tag, + // the reader shouldn't have any bytes remaining. + MOZ_ASSERT(foundFrame || bytesToSkip || !reader.Remaining()); + + if (foundFrame && mParser.FirstFrame().Length() && + !VerifyFrameConsistency(mParser.FirstFrame(), mParser.CurrentFrame())) { + // We've likely hit a false-positive, ignore it and proceed with the + // search for the next valid frame. + foundFrame = false; + mOffset = frameHeaderOffset + 1; + mParser.EndFrameSession(); + } else { + // Advance mOffset by the amount of bytes read and if necessary, + // skip an ID3v2 tag which stretches beyond the current buffer. + NS_ENSURE_TRUE(mOffset + read + bytesToSkip > mOffset, + MediaByteRange(0, 0)); + mOffset += read + bytesToSkip; + } + } + + if (!foundFrame || !mParser.CurrentFrame().Length()) { + MP3LOG("FindNext() Exit foundFrame=%d mParser.CurrentFrame().Length()=%d ", + foundFrame, mParser.CurrentFrame().Length()); + return { 0, 0 }; + } + + MP3LOGV("FindNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " frameHeaderOffset=%d" + " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d" + " mChannels=%d", + mOffset, mNumParsedFrames, mFrameIndex, frameHeaderOffset, + mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, mChannels); + + return { frameHeaderOffset, frameHeaderOffset + mParser.CurrentFrame().Length() }; +} + +bool +MP3TrackDemuxer::SkipNextFrame(const MediaByteRange& aRange) { + if (!mNumParsedFrames || !aRange.Length()) { + // We can't skip the first frame, since it could contain VBR headers. + RefPtr frame(GetNextFrame(aRange)); + return frame; + } + + UpdateState(aRange); + + MP3LOGV("SkipNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 + " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d", + mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, + mSamplesPerFrame, mSamplesPerSecond, mChannels); + + return true; +} + +already_AddRefed +MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange) { + MP3LOG("GetNext() Begin({mStart=%" PRId64 " Length()=%" PRId64 "})", + aRange.mStart, aRange.Length()); + if (!aRange.Length()) { + return nullptr; + } + + RefPtr frame = new MediaRawData(); + frame->mOffset = aRange.mStart; + + nsAutoPtr frameWriter(frame->CreateWriter()); + if (!frameWriter->SetSize(aRange.Length())) { + MP3LOG("GetNext() Exit failed to allocated media buffer"); + return nullptr; + } + + const uint32_t read = Read(frameWriter->Data(), frame->mOffset, frame->Size()); + + if (read != aRange.Length()) { + MP3LOG("GetNext() Exit read=%u frame->Size()=%u", read, frame->Size()); + return nullptr; + } + + UpdateState(aRange); + + frame->mTime = Duration(mFrameIndex - 1).ToMicroseconds(); + frame->mDuration = Duration(1).ToMicroseconds(); + frame->mTimecode = frame->mTime; + frame->mKeyframe = true; + + MOZ_ASSERT(frame->mTime >= 0); + MOZ_ASSERT(frame->mDuration > 0); + + if (mNumParsedFrames == 1) { + // First frame parsed, let's read VBR info if available. + ByteReader reader(frame->Data(), frame->Size()); + mParser.ParseVBRHeader(&reader); + mFirstFrameOffset = frame->mOffset; + } + + MP3LOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 + " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 + " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d", + mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, + mSamplesPerFrame, mSamplesPerSecond, mChannels); + + return frame.forget(); +} + +int64_t +MP3TrackDemuxer::OffsetFromFrameIndex(int64_t aFrameIndex) const { + int64_t offset = 0; + const auto& vbr = mParser.VBRInfo(); + + if (vbr.IsComplete()) { + offset = mFirstFrameOffset + aFrameIndex * vbr.NumBytes().value() / + vbr.NumAudioFrames().value(); + } else if (AverageFrameLength() > 0) { + offset = mFirstFrameOffset + aFrameIndex * AverageFrameLength(); + } + + MP3LOGV("OffsetFromFrameIndex(%" PRId64 ") -> %" PRId64, aFrameIndex, offset); + return std::max(mFirstFrameOffset, offset); +} + +int64_t +MP3TrackDemuxer::FrameIndexFromOffset(int64_t aOffset) const { + int64_t frameIndex = 0; + const auto& vbr = mParser.VBRInfo(); + + if (vbr.IsComplete()) { + frameIndex = static_cast(aOffset - mFirstFrameOffset) / + vbr.NumBytes().value() * vbr.NumAudioFrames().value(); + frameIndex = std::min(vbr.NumAudioFrames().value(), frameIndex); + } else if (AverageFrameLength() > 0) { + frameIndex = (aOffset - mFirstFrameOffset) / AverageFrameLength(); + } + + MP3LOGV("FrameIndexFromOffset(%" PRId64 ") -> %" PRId64, aOffset, frameIndex); + return std::max(0, frameIndex); +} + +int64_t +MP3TrackDemuxer::FrameIndexFromTime(const media::TimeUnit& aTime) const { + int64_t frameIndex = 0; + if (mSamplesPerSecond > 0 && mSamplesPerFrame > 0) { + frameIndex = aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerFrame - 1; + } + + MP3LOGV("FrameIndexFromOffset(%fs) -> %" PRId64, aTime.ToSeconds(), frameIndex); + return std::max(0, frameIndex); +} + +void +MP3TrackDemuxer::UpdateState(const MediaByteRange& aRange) { + // Prevent overflow. + if (mTotalFrameLen + aRange.Length() < mTotalFrameLen) { + // These variables have a linear dependency and are only used to derive the + // average frame length. + mTotalFrameLen /= 2; + mNumParsedFrames /= 2; + } + + // Full frame parsed, move offset to its end. + mOffset = aRange.mEnd; + + mTotalFrameLen += aRange.Length(); + + if (!mSamplesPerFrame) { + mSamplesPerFrame = mParser.CurrentFrame().Header().SamplesPerFrame(); + mSamplesPerSecond = mParser.CurrentFrame().Header().SampleRate(); + mChannels = mParser.CurrentFrame().Header().Channels(); + } + + ++mNumParsedFrames; + ++mFrameIndex; + MOZ_ASSERT(mFrameIndex > 0); + + // Prepare the parser for the next frame parsing session. + mParser.EndFrameSession(); +} + +int32_t +MP3TrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize) { + MP3LOGV("MP3TrackDemuxer::Read(%p %" PRId64 " %d)", aBuffer, aOffset, aSize); + + const int64_t streamLen = StreamLength(); + if (mInfo && streamLen > 0) { + // Prevent blocking reads after successful initialization. + aSize = std::min(aSize, streamLen - aOffset); + } + + uint32_t read = 0; + MP3LOGV("MP3TrackDemuxer::Read -> ReadAt(%d)", aSize); + const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast(aBuffer), + static_cast(aSize), &read); + NS_ENSURE_SUCCESS(rv, 0); + return static_cast(read); +} + +double +MP3TrackDemuxer::AverageFrameLength() const { + if (mNumParsedFrames) { + return static_cast(mTotalFrameLen) / mNumParsedFrames; + } + const auto& vbr = mParser.VBRInfo(); + if (vbr.IsComplete() && vbr.NumAudioFrames().value() + 1) { + return static_cast(vbr.NumBytes().value()) / + (vbr.NumAudioFrames().value() + 1); + } + return 0.0; +} + +// FrameParser + +namespace frame_header { +// FrameHeader mRaw byte offsets. +static const int SYNC1 = 0; +static const int SYNC2_VERSION_LAYER_PROTECTION = 1; +static const int BITRATE_SAMPLERATE_PADDING_PRIVATE = 2; +static const int CHANNELMODE_MODEEXT_COPY_ORIG_EMPH = 3; +} // namespace frame_header + +FrameParser::FrameParser() +{ +} + +void +FrameParser::Reset() { + mID3Parser.Reset(); + mFrame.Reset(); +} + +void +FrameParser::ResetFrameData() { + mFrame.Reset(); + mFirstFrame.Reset(); + mPrevFrame.Reset(); +} + +void +FrameParser::EndFrameSession() { + if (!mID3Parser.Header().IsValid()) { + // Reset ID3 tags only if we have not parsed a valid ID3 header yet. + mID3Parser.Reset(); + } + mPrevFrame = mFrame; + mFrame.Reset(); +} + +const FrameParser::Frame& +FrameParser::CurrentFrame() const { + return mFrame; +} + +const FrameParser::Frame& +FrameParser::PrevFrame() const { + return mPrevFrame; +} + +const FrameParser::Frame& +FrameParser::FirstFrame() const { + return mFirstFrame; +} + +const ID3Parser::ID3Header& +FrameParser::ID3Header() const { + return mID3Parser.Header(); +} + +const FrameParser::VBRHeader& +FrameParser::VBRInfo() const { + return mVBRHeader; +} + +bool +FrameParser::Parse(ByteReader* aReader, uint32_t* aBytesToSkip) { + MOZ_ASSERT(aReader && aBytesToSkip); + *aBytesToSkip = 0; + + if (!mID3Parser.Header().Size() && !mFirstFrame.Length()) { + // No MP3 frames have been parsed yet, look for ID3v2 headers at file begin. + // ID3v1 tags may only be at file end. + // TODO: should we try to read ID3 tags at end of file/mid-stream, too? + const size_t prevReaderOffset = aReader->Offset(); + const uint32_t tagSize = mID3Parser.Parse(aReader); + if (tagSize) { + // ID3 tag found, skip past it. + const uint32_t skipSize = tagSize - ID3Parser::ID3Header::SIZE; + + if (skipSize > aReader->Remaining()) { + // Skipping across the ID3v2 tag would take us past the end of the buffer, therefore we + // return immediately and let the calling function handle skipping the rest of the tag. + MP3LOGV("ID3v2 tag detected, size=%d," + " needing to skip %d bytes past the current buffer", + tagSize, skipSize - aReader->Remaining()); + *aBytesToSkip = skipSize - aReader->Remaining(); + return false; + } + MP3LOGV("ID3v2 tag detected, size=%d", tagSize); + aReader->Read(skipSize); + } else { + // No ID3v2 tag found, rewinding reader in order to search for a MPEG frame header. + aReader->Seek(prevReaderOffset); + } + } + + while (aReader->CanRead8() && !mFrame.ParseNext(aReader->ReadU8())) { } + + if (mFrame.Length()) { + // MP3 frame found. + if (!mFirstFrame.Length()) { + mFirstFrame = mFrame; + } + // Indicate success. + return true; + } + return false; +} + +// FrameParser::Header + +FrameParser::FrameHeader::FrameHeader() +{ + Reset(); +} + +uint8_t +FrameParser::FrameHeader::Sync1() const { + return mRaw[frame_header::SYNC1]; +} + +uint8_t +FrameParser::FrameHeader::Sync2() const { + return 0x7 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 5; +} + +uint8_t +FrameParser::FrameHeader::RawVersion() const { + return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 3; +} + +uint8_t +FrameParser::FrameHeader::RawLayer() const { + return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 1; +} + +uint8_t +FrameParser::FrameHeader::RawProtection() const { + return 0x1 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 6; +} + +uint8_t +FrameParser::FrameHeader::RawBitrate() const { + return 0xF & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 4; +} + +uint8_t +FrameParser::FrameHeader::RawSampleRate() const { + return 0x3 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 2; +} + +uint8_t +FrameParser::FrameHeader::Padding() const { + return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 1; +} + +uint8_t +FrameParser::FrameHeader::Private() const { + return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE]; +} + +uint8_t +FrameParser::FrameHeader::RawChannelMode() const { + return 0x3 & mRaw[frame_header::CHANNELMODE_MODEEXT_COPY_ORIG_EMPH] >> 6; +} + +int32_t +FrameParser::FrameHeader::Layer() const { + static const uint8_t LAYERS[4] = { 0, 3, 2, 1 }; + + return LAYERS[RawLayer()]; +} + +int32_t +FrameParser::FrameHeader::SampleRate() const { + // Sample rates - use [version][srate] + static const uint16_t SAMPLE_RATE[4][4] = { + { 11025, 12000, 8000, 0 }, // MPEG 2.5 + { 0, 0, 0, 0 }, // Reserved + { 22050, 24000, 16000, 0 }, // MPEG 2 + { 44100, 48000, 32000, 0 } // MPEG 1 + }; + + return SAMPLE_RATE[RawVersion()][RawSampleRate()]; +} + +int32_t +FrameParser::FrameHeader::Channels() const { + // 3 is single channel (mono), any other value is some variant of dual + // channel. + return RawChannelMode() == 3 ? 1 : 2; +} + +int32_t +FrameParser::FrameHeader::SamplesPerFrame() const { + // Samples per frame - use [version][layer] + static const uint16_t FRAME_SAMPLE[4][4] = { + // Layer 3 2 1 Version + { 0, 576, 1152, 384 }, // 2.5 + { 0, 0, 0, 0 }, // Reserved + { 0, 576, 1152, 384 }, // 2 + { 0, 1152, 1152, 384 } // 1 + }; + + return FRAME_SAMPLE[RawVersion()][RawLayer()]; +} + +int32_t +FrameParser::FrameHeader::Bitrate() const { + // Bitrates - use [version][layer][bitrate] + static const uint16_t BITRATE[4][4][16] = { + { // Version 2.5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2 + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1 + }, + { // Reserved + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // Invalid + }, + { // Version 2 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2 + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1 + }, + { // Version 1 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved + { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer 3 + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer 2 + { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // Layer 1 + } + }; + + return 1000 * BITRATE[RawVersion()][RawLayer()][RawBitrate()]; +} + +int32_t +FrameParser::FrameHeader::SlotSize() const { + // Slot size (MPEG unit of measurement) - use [layer] + static const uint8_t SLOT_SIZE[4] = { 0, 1, 1, 4 }; // Rsvd, 3, 2, 1 + + return SLOT_SIZE[RawLayer()]; +} + +bool +FrameParser::FrameHeader::ParseNext(uint8_t c) { + if (!Update(c)) { + Reset(); + if (!Update(c)) { + Reset(); + } + } + return IsValid(); +} + +bool +FrameParser::FrameHeader::IsValid(int aPos) const { + if (aPos >= SIZE) { + return true; + } + if (aPos == frame_header::SYNC1) { + return Sync1() == 0xFF; + } + if (aPos == frame_header::SYNC2_VERSION_LAYER_PROTECTION) { + return Sync2() == 7 && + RawVersion() != 1 && + Layer() == 3; + } + if (aPos == frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE) { + return RawBitrate() != 0xF && RawBitrate() != 0 && + RawSampleRate() != 3; + } + return true; +} + +bool +FrameParser::FrameHeader::IsValid() const { + return mPos >= SIZE; +} + +void +FrameParser::FrameHeader::Reset() { + mPos = 0; +} + +bool +FrameParser::FrameHeader::Update(uint8_t c) { + if (mPos < SIZE) { + mRaw[mPos] = c; + } + return IsValid(mPos++); +} + +// FrameParser::VBRHeader + +namespace vbr_header { +static const char* TYPE_STR[3] = {"NONE", "XING", "VBRI"}; +static const uint32_t TOC_SIZE = 100; +} // namespace vbr_header + +FrameParser::VBRHeader::VBRHeader() + : mType(NONE) +{ +} + +FrameParser::VBRHeader::VBRHeaderType +FrameParser::VBRHeader::Type() const { + return mType; +} + +const Maybe& +FrameParser::VBRHeader::NumAudioFrames() const { + return mNumAudioFrames; +} + +const Maybe& +FrameParser::VBRHeader::NumBytes() const { + return mNumBytes; +} + +const Maybe& +FrameParser::VBRHeader::Scale() const { + return mScale; +} + +bool +FrameParser::VBRHeader::IsTOCPresent() const { + return mTOC.size() == vbr_header::TOC_SIZE; +} + +bool +FrameParser::VBRHeader::IsValid() const { + return mType != NONE; +} + +bool +FrameParser::VBRHeader::IsComplete() const { + return IsValid() && + mNumAudioFrames.valueOr(0) > 0 && + mNumBytes.valueOr(0) > 0 && + // We don't care about the scale for any computations here. + // mScale < 101 && + true; +} + +int64_t +FrameParser::VBRHeader::Offset(float aDurationFac) const { + if (!IsTOCPresent()) { + return -1; + } + + // Constrain the duration percentage to [0, 99]. + const float durationPer = 100.0f * std::min(0.99f, std::max(0.0f, aDurationFac)); + const size_t fullPer = durationPer; + const float rest = durationPer - fullPer; + + MOZ_ASSERT(fullPer < mTOC.size()); + int64_t offset = mTOC.at(fullPer); + + if (rest > 0.0 && fullPer + 1 < mTOC.size()) { + offset += rest * (mTOC.at(fullPer + 1) - offset); + } + + return offset; +} + +bool +FrameParser::VBRHeader::ParseXing(ByteReader* aReader) { + static const uint32_t XING_TAG = BigEndian::readUint32("Xing"); + static const uint32_t INFO_TAG = BigEndian::readUint32("Info"); + + enum Flags { + NUM_FRAMES = 0x01, + NUM_BYTES = 0x02, + TOC = 0x04, + VBR_SCALE = 0x08 + }; + + MOZ_ASSERT(aReader); + const size_t prevReaderOffset = aReader->Offset(); + + // We have to search for the Xing header as its position can change. + while (aReader->CanRead32() && + aReader->PeekU32() != XING_TAG && aReader->PeekU32() != INFO_TAG) { + aReader->Read(1); + } + + if (aReader->CanRead32()) { + // Skip across the VBR header ID tag. + aReader->ReadU32(); + mType = XING; + } + uint32_t flags = 0; + if (aReader->CanRead32()) { + flags = aReader->ReadU32(); + } + if (flags & NUM_FRAMES && aReader->CanRead32()) { + mNumAudioFrames = Some(aReader->ReadU32()); + } + if (flags & NUM_BYTES && aReader->CanRead32()) { + mNumBytes = Some(aReader->ReadU32()); + } + if (flags & TOC && aReader->Remaining() >= vbr_header::TOC_SIZE) { + if (!mNumBytes) { + // We don't have the stream size to calculate offsets, skip the TOC. + aReader->Read(vbr_header::TOC_SIZE); + } else { + mTOC.clear(); + mTOC.reserve(vbr_header::TOC_SIZE); + for (size_t i = 0; i < vbr_header::TOC_SIZE; ++i) { + mTOC.push_back(1.0f / 256.0f * aReader->ReadU8() * mNumBytes.value()); + } + } + } + if (flags & VBR_SCALE && aReader->CanRead32()) { + mScale = Some(aReader->ReadU32()); + } + + aReader->Seek(prevReaderOffset); + return mType == XING; +} + +bool +FrameParser::VBRHeader::ParseVBRI(ByteReader* aReader) { + static const uint32_t TAG = BigEndian::readUint32("VBRI"); + static const uint32_t OFFSET = 32 + FrameParser::FrameHeader::SIZE; + static const uint32_t FRAME_COUNT_OFFSET = OFFSET + 14; + static const uint32_t MIN_FRAME_SIZE = OFFSET + 26; + + MOZ_ASSERT(aReader); + // ParseVBRI assumes that the ByteReader offset points to the beginning of a frame, + // therefore as a simple check, we look for the presence of a frame sync at that position. + MOZ_ASSERT((aReader->PeekU16() & 0xFFE0) == 0xFFE0); + const size_t prevReaderOffset = aReader->Offset(); + + // VBRI have a fixed relative position, so let's check for it there. + if (aReader->Remaining() > MIN_FRAME_SIZE) { + aReader->Seek(prevReaderOffset + OFFSET); + if (aReader->ReadU32() == TAG) { + aReader->Seek(prevReaderOffset + FRAME_COUNT_OFFSET); + mNumAudioFrames = Some(aReader->ReadU32()); + mType = VBRI; + aReader->Seek(prevReaderOffset); + return true; + } + } + aReader->Seek(prevReaderOffset); + return false; +} + +bool +FrameParser::VBRHeader::Parse(ByteReader* aReader) { + const bool rv = ParseVBRI(aReader) || ParseXing(aReader); + if (rv) { + MP3LOG("VBRHeader::Parse found valid VBR/CBR header: type=%s" + " NumAudioFrames=%u NumBytes=%u Scale=%u TOC-size=%u", + vbr_header::TYPE_STR[Type()], NumAudioFrames().valueOr(0), + NumBytes().valueOr(0), Scale().valueOr(0), mTOC.size()); + } + return rv; +} + +// FrameParser::Frame + +void +FrameParser::Frame::Reset() { + mHeader.Reset(); +} + +int32_t +FrameParser::Frame::Length() const { + if (!mHeader.IsValid() || !mHeader.SampleRate()) { + return 0; + } + + const float bitsPerSample = mHeader.SamplesPerFrame() / 8.0f; + const int32_t frameLen = bitsPerSample * mHeader.Bitrate() / + mHeader.SampleRate() + + mHeader.Padding() * mHeader.SlotSize(); + return frameLen; +} + +bool +FrameParser::Frame::ParseNext(uint8_t c) { + return mHeader.ParseNext(c); +} + +const FrameParser::FrameHeader& +FrameParser::Frame::Header() const { + return mHeader; +} + +bool +FrameParser::ParseVBRHeader(ByteReader* aReader) { + return mVBRHeader.Parse(aReader); +} + +// ID3Parser + +// Constants +namespace id3_header { +static const int ID_LEN = 3; +static const int VERSION_LEN = 2; +static const int FLAGS_LEN = 1; +static const int SIZE_LEN = 4; + +static const int ID_END = ID_LEN; +static const int VERSION_END = ID_END + VERSION_LEN; +static const int FLAGS_END = VERSION_END + FLAGS_LEN; +static const int SIZE_END = FLAGS_END + SIZE_LEN; + +static const uint8_t ID[ID_LEN] = {'I', 'D', '3'}; + +static const uint8_t MIN_MAJOR_VER = 2; +static const uint8_t MAX_MAJOR_VER = 4; +} // namespace id3_header + +uint32_t +ID3Parser::Parse(ByteReader* aReader) { + MOZ_ASSERT(aReader); + + while (aReader->CanRead8() && !mHeader.ParseNext(aReader->ReadU8())) { } + + if (mHeader.IsValid()) { + // Header found, return total tag size. + return ID3Header::SIZE + Header().Size() + Header().FooterSize(); + } + return 0; +} + +void +ID3Parser::Reset() { + mHeader.Reset(); +} + +const ID3Parser::ID3Header& +ID3Parser::Header() const { + return mHeader; +} + +// ID3Parser::Header + +ID3Parser::ID3Header::ID3Header() +{ + Reset(); +} + +void +ID3Parser::ID3Header::Reset() { + mSize = 0; + mPos = 0; +} + +uint8_t +ID3Parser::ID3Header::MajorVersion() const { + return mRaw[id3_header::ID_END]; +} + +uint8_t +ID3Parser::ID3Header::MinorVersion() const { + return mRaw[id3_header::ID_END + 1]; +} + +uint8_t +ID3Parser::ID3Header::Flags() const { + return mRaw[id3_header::FLAGS_END - id3_header::FLAGS_LEN]; +} + +uint32_t +ID3Parser::ID3Header::Size() const { + if (!IsValid()) { + return 0; + } + return mSize; +} + +uint8_t +ID3Parser::ID3Header::FooterSize() const { + if (Flags() & (1 << 4)) { + return SIZE; + } + return 0; +} + +bool +ID3Parser::ID3Header::ParseNext(uint8_t c) { + if (!Update(c)) { + Reset(); + if (!Update(c)) { + Reset(); + } + } + return IsValid(); +} + +bool +ID3Parser::ID3Header::IsValid(int aPos) const { + if (aPos >= SIZE) { + return true; + } + const uint8_t c = mRaw[aPos]; + switch (aPos) { + case 0: case 1: case 2: + // Expecting "ID3". + return id3_header::ID[aPos] == c; + case 3: + return MajorVersion() >= id3_header::MIN_MAJOR_VER && + MajorVersion() <= id3_header::MAX_MAJOR_VER; + case 4: + return MinorVersion() < 0xFF; + case 5: + // Validate flags for supported versions, see bug 949036. + return ((0xFF >> MajorVersion()) & c) == 0; + case 6: case 7: case 8: case 9: + return c < 0x80; + } + return true; +} + +bool +ID3Parser::ID3Header::IsValid() const { + return mPos >= SIZE; +} + +bool +ID3Parser::ID3Header::Update(uint8_t c) { + if (mPos >= id3_header::SIZE_END - id3_header::SIZE_LEN && + mPos < id3_header::SIZE_END) { + mSize <<= 7; + mSize |= c; + } + if (mPos < SIZE) { + mRaw[mPos] = c; + } + return IsValid(mPos++); +} + +} // namespace mozilla diff --git a/dom/media/mp3/MP3Demuxer.h b/dom/media/mp3/MP3Demuxer.h new file mode 100644 index 000000000..5331c4d54 --- /dev/null +++ b/dom/media/mp3/MP3Demuxer.h @@ -0,0 +1,472 @@ +/* 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/. */ + +#ifndef MP3_DEMUXER_H_ +#define MP3_DEMUXER_H_ + +#include "mozilla/Attributes.h" +#include "mozilla/Maybe.h" +#include "MediaDataDemuxer.h" +#include "MediaResource.h" +#include "mp4_demuxer/ByteReader.h" +#include + +namespace mozilla { + +class MP3TrackDemuxer; + +class MP3Demuxer : public MediaDataDemuxer { +public: + // MediaDataDemuxer interface. + explicit MP3Demuxer(MediaResource* aSource); + RefPtr Init() override; + bool HasTrackType(TrackInfo::TrackType aType) const override; + uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override; + already_AddRefed GetTrackDemuxer( + TrackInfo::TrackType aType, uint32_t aTrackNumber) override; + bool IsSeekable() const override; + void NotifyDataArrived() override; + void NotifyDataRemoved() override; + +private: + // Synchronous initialization. + bool InitInternal(); + + RefPtr mSource; + RefPtr mTrackDemuxer; +}; + +// ID3 header parser state machine used by FrameParser. +// The header contains the following format (one byte per term): +// 'I' 'D' '3' MajorVersion MinorVersion Flags Size1 Size2 Size3 Size4 +// For more details see http://id3.org/id3v2.3.0. +class ID3Parser { +public: + // Holds the ID3 header and its parsing state. + class ID3Header { + public: + // The header size is static, see class comment. + static const int SIZE = 10; + + // Constructor. + ID3Header(); + + // Resets the state to allow for a new parsing session. + void Reset(); + + // The ID3 tags are versioned like this: ID3vMajorVersion.MinorVersion. + uint8_t MajorVersion() const; + uint8_t MinorVersion() const; + + // The ID3 flags field. + uint8_t Flags() const; + + // The derived size based on the provided size fields. + uint32_t Size() const; + + // Returns the size of an ID3v2.4 footer if present and zero otherwise. + uint8_t FooterSize() const; + + // Returns whether the parsed data is a valid ID3 header up to the given + // byte position. + bool IsValid(int aPos) const; + + // Returns whether the parsed data is a complete and valid ID3 header. + bool IsValid() const; + + // Parses the next provided byte. + // Returns whether the byte creates a valid sequence up to this point. + bool ParseNext(uint8_t c); + + private: + // Updates the parser state machine with the provided next byte. + // Returns whether the provided byte is a valid next byte in the sequence. + bool Update(uint8_t c); + + // The currently parsed byte sequence. + uint8_t mRaw[SIZE]; + + // The derived size as provided by the size fields. + // The header size fields holds a 4 byte sequence with each MSB set to 0, + // this bits need to be ignored when deriving the actual size. + uint32_t mSize; + + // The current byte position in the parsed sequence. Reset via Reset and + // incremented via Update. + int mPos; + }; + + // Returns the parsed ID3 header. Note: check for validity. + const ID3Header& Header() const; + + // Parses contents of given ByteReader for a valid ID3v2 header. + // Returns the total ID3v2 tag size if successful and zero otherwise. + uint32_t Parse(mp4_demuxer::ByteReader* aReader); + + // Resets the state to allow for a new parsing session. + void Reset(); + +private: + // The currently parsed ID3 header. Reset via Reset, updated via Parse. + ID3Header mHeader; +}; + +// MPEG audio frame parser. +// The MPEG frame header has the following format (one bit per character): +// 11111111 111VVLLC BBBBSSPR MMEETOHH +// { sync } - 11 sync bits +// VV - MPEG audio version ID (0->2.5, 1->reserved, 2->2, 3->1) +// LL - Layer description (0->reserved, 1->III, 2->II, 3->I) +// C - CRC protection bit (0->protected, 1->not protected) +// BBBB - Bitrate index (see table in implementation) +// SS - Sampling rate index (see table in implementation) +// P - Padding bit (0->not padded, 1->padded by 1 slot size) +// R - Private bit (ignored) +// MM - Channel mode (0->stereo, 1->joint stereo, 2->dual channel, +// 3->single channel) +// EE - Mode extension for joint stereo (ignored) +// T - Copyright (0->disabled, 1->enabled) +// O - Original (0->copy, 1->original) +// HH - Emphasis (0->none, 1->50/15 ms, 2->reserved, 3->CCIT J.17) +class FrameParser { +public: + // Holds the frame header and its parsing state. + class FrameHeader { + public: + // The header size is static, see class comments. + static const int SIZE = 4; + + // Constructor. + FrameHeader(); + + // Raw field access, see class comments for details. + uint8_t Sync1() const; + uint8_t Sync2() const; + uint8_t RawVersion() const; + uint8_t RawLayer() const; + uint8_t RawProtection() const; + uint8_t RawBitrate() const; + uint8_t RawSampleRate() const; + uint8_t Padding() const; + uint8_t Private() const; + uint8_t RawChannelMode() const; + + // Sampling rate frequency in Hz. + int32_t SampleRate() const; + + // Number of audio channels. + int32_t Channels() const; + + // Samples per frames, static depending on MPEG version and layer. + int32_t SamplesPerFrame() const; + + // Slot size used for padding, static depending on MPEG layer. + int32_t SlotSize() const; + + // Bitrate in kbps, can vary between frames. + int32_t Bitrate() const; + + // MPEG layer (0->invalid, 1->I, 2->II, 3->III). + int32_t Layer() const; + + // Returns whether the parsed data is a valid frame header up to the given + // byte position. + bool IsValid(const int aPos) const; + + // Returns whether the parsed data is a complete and valid frame header. + bool IsValid() const; + + // Resets the state to allow for a new parsing session. + void Reset(); + + // Parses the next provided byte. + // Returns whether the byte creates a valid sequence up to this point. + bool ParseNext(const uint8_t c); + + private: + // Updates the parser state machine with the provided next byte. + // Returns whether the provided byte is a valid next byte in the sequence. + bool Update(const uint8_t c); + + // The currently parsed byte sequence. + uint8_t mRaw[SIZE]; + + // The current byte position in the parsed sequence. Reset via Reset and + // incremented via Update. + int mPos; + }; + + // VBR frames may contain Xing or VBRI headers for additional info, we use + // this class to parse them and access this info. + class VBRHeader { + public: + // Synchronize with vbr_header TYPE_STR on change. + enum VBRHeaderType { + NONE = 0, + XING, + VBRI + }; + + // Constructor. + VBRHeader(); + + // Returns the parsed VBR header type, or NONE if no valid header found. + VBRHeaderType Type() const; + + // Returns the total number of audio frames (excluding the VBR header frame) + // expected in the stream/file. + const Maybe& NumAudioFrames() const; + + // Returns the expected size of the stream. + const Maybe& NumBytes() const; + + // Returns the VBR scale factor (0: best quality, 100: lowest quality). + const Maybe& Scale() const; + + // Returns true iff Xing/Info TOC (table of contents) is present. + bool IsTOCPresent() const; + + // Returns whether the header is valid (type XING or VBRI). + bool IsValid() const; + + // Returns whether the header is valid and contains reasonable non-zero field values. + bool IsComplete() const; + + // Returns the byte offset for the given duration percentage as a factor + // (0: begin, 1.0: end). + int64_t Offset(float aDurationFac) const; + + // Parses contents of given ByteReader for a valid VBR header. + // The offset of the passed ByteReader needs to point to an MPEG frame begin, + // as a VBRI-style header is searched at a fixed offset relative to frame begin. + // Returns whether a valid VBR header was found in the range. + bool Parse(mp4_demuxer::ByteReader* aReader); + + private: + // Parses contents of given ByteReader for a valid Xing header. + // The initial ByteReader offset will be preserved. + // Returns whether a valid Xing header was found in the range. + bool ParseXing(mp4_demuxer::ByteReader* aReader); + + // Parses contents of given ByteReader for a valid VBRI header. + // The initial ByteReader offset will be preserved. It also needs to point + // to the beginning of a valid MPEG frame, as VBRI headers are searched + // at a fixed offset relative to frame begin. + // Returns whether a valid VBRI header was found in the range. + bool ParseVBRI(mp4_demuxer::ByteReader* aReader); + + // The total number of frames expected as parsed from a VBR header. + Maybe mNumAudioFrames; + + // The total number of bytes expected in the stream. + Maybe mNumBytes; + + // The VBR scale factor. + Maybe mScale; + + // The TOC table mapping duration percentage to byte offset. + std::vector mTOC; + + // The detected VBR header type. + VBRHeaderType mType; + }; + + // Frame meta container used to parse and hold a frame header and side info. + class Frame { + public: + // Returns the length of the frame excluding the header in bytes. + int32_t Length() const; + + // Returns the parsed frame header. + const FrameHeader& Header() const; + + // Resets the frame header and data. + void Reset(); + + // Parses the next provided byte. + // Returns whether the byte creates a valid sequence up to this point. + bool ParseNext(uint8_t c); + + private: + // The currently parsed frame header. + FrameHeader mHeader; + }; + + // Constructor. + FrameParser(); + + // Returns the currently parsed frame. Reset via Reset or EndFrameSession. + const Frame& CurrentFrame() const; + + // Returns the previously parsed frame. Reset via Reset. + const Frame& PrevFrame() const; + + // Returns the first parsed frame. Reset via Reset. + const Frame& FirstFrame() const; + + // Returns the parsed ID3 header. Note: check for validity. + const ID3Parser::ID3Header& ID3Header() const; + + // Returns the parsed VBR header info. Note: check for validity by type. + const VBRHeader& VBRInfo() const; + + // Resets the parser. + void Reset(); + + // Resets all frame data, but not the ID3Header. + // Don't use between frames as first frame data is reset. + void ResetFrameData(); + + // Clear the last parsed frame to allow for next frame parsing, i.e.: + // - sets PrevFrame to CurrentFrame + // - resets the CurrentFrame + // - resets ID3Header if no valid header was parsed yet + void EndFrameSession(); + + // Parses contents of given ByteReader for a valid frame header and returns true + // if one was found. After returning, the variable passed to 'aBytesToSkip' holds + // the amount of bytes to be skipped (if any) in order to jump across a large + // ID3v2 tag spanning multiple buffers. + bool Parse(mp4_demuxer::ByteReader* aReader, uint32_t* aBytesToSkip); + + // Parses contents of given ByteReader for a valid VBR header. + // The offset of the passed ByteReader needs to point to an MPEG frame begin, + // as a VBRI-style header is searched at a fixed offset relative to frame begin. + // Returns whether a valid VBR header was found. + bool ParseVBRHeader(mp4_demuxer::ByteReader* aReader); + +private: + // ID3 header parser. + ID3Parser mID3Parser; + + // VBR header parser. + VBRHeader mVBRHeader; + + // We keep the first parsed frame around for static info access, the + // previously parsed frame for debugging and the currently parsed frame. + Frame mFirstFrame; + Frame mFrame; + Frame mPrevFrame; +}; + +// The MP3 demuxer used to extract MPEG frames and side information out of +// MPEG streams. +class MP3TrackDemuxer : public MediaTrackDemuxer { +public: + // Constructor, expecting a valid media resource. + explicit MP3TrackDemuxer(MediaResource* aSource); + + // Initializes the track demuxer by reading the first frame for meta data. + // Returns initialization success state. + bool Init(); + + // Returns the total stream length if known, -1 otherwise. + int64_t StreamLength() const; + + // Returns the estimated stream duration, or a 0-duration if unknown. + media::TimeUnit Duration() const; + + // Returns the estimated duration up to the given frame number, + // or a 0-duration if unknown. + media::TimeUnit Duration(int64_t aNumFrames) const; + + // Returns the estimated current seek position time. + media::TimeUnit SeekPosition() const; + + const FrameParser::Frame& LastFrame() const; + RefPtr DemuxSample(); + + const ID3Parser::ID3Header& ID3Header() const; + const FrameParser::VBRHeader& VBRInfo() const; + + // MediaTrackDemuxer interface. + UniquePtr GetInfo() const override; + RefPtr Seek(media::TimeUnit aTime) override; + RefPtr GetSamples(int32_t aNumSamples = 1) override; + void Reset() override; + RefPtr SkipToNextRandomAccessPoint( + media::TimeUnit aTimeThreshold) override; + int64_t GetResourceOffset() const override; + media::TimeIntervals GetBuffered() override; + +private: + // Destructor. + ~MP3TrackDemuxer() {} + + // Fast approximate seeking to given time. + media::TimeUnit FastSeek(const media::TimeUnit& aTime); + + // Seeks by scanning the stream up to the given time for more accurate results. + media::TimeUnit ScanUntil(const media::TimeUnit& aTime); + + // Finds the first valid frame and returns its byte range if found + // or a null-byte range otherwise. + MediaByteRange FindFirstFrame(); + + // Finds the next valid frame and returns its byte range if found + // or a null-byte range otherwise. + MediaByteRange FindNextFrame(); + + // Skips the next frame given the provided byte range. + bool SkipNextFrame(const MediaByteRange& aRange); + + // Returns the next MPEG frame, if available. + already_AddRefed GetNextFrame(const MediaByteRange& aRange); + + // Updates post-read meta data. + void UpdateState(const MediaByteRange& aRange); + + // Returns the estimated offset for the given frame index. + int64_t OffsetFromFrameIndex(int64_t aFrameIndex) const; + + // Returns the estimated frame index for the given offset. + int64_t FrameIndexFromOffset(int64_t aOffset) const; + + // Returns the estimated frame index for the given time. + int64_t FrameIndexFromTime(const media::TimeUnit& aTime) const; + + // Reads aSize bytes into aBuffer from the source starting at aOffset. + // Returns the actual size read. + int32_t Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize); + + // Returns the average frame length derived from the previously parsed frames. + double AverageFrameLength() const; + + // The (hopefully) MPEG resource. + MediaResourceIndex mSource; + + // MPEG frame parser used to detect frames and extract side info. + FrameParser mParser; + + // Current byte offset in the source stream. + int64_t mOffset; + + // Byte offset of the begin of the first frame, or 0 if none parsed yet. + int64_t mFirstFrameOffset; + + // Total parsed frames. + uint64_t mNumParsedFrames; + + // Current frame index. + int64_t mFrameIndex; + + // Sum of parsed frames' lengths in bytes. + uint64_t mTotalFrameLen; + + // Samples per frame metric derived from frame headers or 0 if none available. + int32_t mSamplesPerFrame; + + // Samples per second metric derived from frame headers or 0 if none available. + int32_t mSamplesPerSecond; + + // Channel count derived from frame headers or 0 if none available. + int32_t mChannels; + + // Audio track config info. + UniquePtr mInfo; +}; + +} // namespace mozilla + +#endif diff --git a/dom/media/mp3/moz.build b/dom/media/mp3/moz.build new file mode 100644 index 000000000..596d061f8 --- /dev/null +++ b/dom/media/mp3/moz.build @@ -0,0 +1,17 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXPORTS += [ + 'MP3Decoder.h', + 'MP3Demuxer.h', +] + +UNIFIED_SOURCES += [ + 'MP3Decoder.cpp', + 'MP3Demuxer.cpp', +] + +FINAL_LIBRARY = 'xul' -- cgit v1.2.3 From 45f442a2a04bfc299c994e2712c3553278d46e6c Mon Sep 17 00:00:00 2001 From: trav90 Date: Sat, 8 Dec 2018 23:13:55 -0600 Subject: Add missing #include --- dom/media/WebVTTListener.h | 1 + 1 file changed, 1 insertion(+) (limited to 'dom/media') diff --git a/dom/media/WebVTTListener.h b/dom/media/WebVTTListener.h index 67271664a..461d7f00d 100644 --- a/dom/media/WebVTTListener.h +++ b/dom/media/WebVTTListener.h @@ -10,6 +10,7 @@ #include "nsIStreamListener.h" #include "nsIChannelEventSink.h" #include "nsIInterfaceRequestor.h" +#include "nsCOMPtr.h" #include "nsCycleCollectionParticipant.h" class nsIWebVTTParserWrapper; -- cgit v1.2.3 From ec2346967015044f5a6ebef03f51ccb5665206b8 Mon Sep 17 00:00:00 2001 From: trav90 Date: Sun, 16 Dec 2018 08:02:30 -0600 Subject: Remove AndroidMediaDecoder and friends They are no longer supported and don't work with newer Android versions anyway. --- dom/media/DecoderTraits.cpp | 47 --- dom/media/MediaDecoder.cpp | 14 - dom/media/MediaDecoder.h | 4 - dom/media/android/AndroidMediaDecoder.cpp | 25 -- dom/media/android/AndroidMediaDecoder.h | 28 -- dom/media/android/AndroidMediaPluginHost.cpp | 305 -------------- dom/media/android/AndroidMediaPluginHost.h | 41 -- dom/media/android/AndroidMediaReader.cpp | 449 -------------------- dom/media/android/AndroidMediaReader.h | 75 ---- dom/media/android/AndroidMediaResourceServer.cpp | 503 ----------------------- dom/media/android/AndroidMediaResourceServer.h | 96 ----- dom/media/android/MPAPI.h | 165 -------- dom/media/android/moz.build | 27 -- dom/media/moz.build | 3 - 14 files changed, 1782 deletions(-) delete mode 100644 dom/media/android/AndroidMediaDecoder.cpp delete mode 100644 dom/media/android/AndroidMediaDecoder.h delete mode 100644 dom/media/android/AndroidMediaPluginHost.cpp delete mode 100644 dom/media/android/AndroidMediaPluginHost.h delete mode 100644 dom/media/android/AndroidMediaReader.cpp delete mode 100644 dom/media/android/AndroidMediaReader.h delete mode 100644 dom/media/android/AndroidMediaResourceServer.cpp delete mode 100644 dom/media/android/AndroidMediaResourceServer.h delete mode 100644 dom/media/android/MPAPI.h delete mode 100644 dom/media/android/moz.build (limited to 'dom/media') diff --git a/dom/media/DecoderTraits.cpp b/dom/media/DecoderTraits.cpp index 477ecf358..f8cb4fd0e 100644 --- a/dom/media/DecoderTraits.cpp +++ b/dom/media/DecoderTraits.cpp @@ -18,11 +18,6 @@ #include "WebMDecoder.h" #include "WebMDemuxer.h" -#ifdef MOZ_ANDROID_OMX -#include "AndroidMediaDecoder.h" -#include "AndroidMediaReader.h" -#include "AndroidMediaPluginHost.h" -#endif #ifdef MOZ_FMP4 #include "MP4Decoder.h" #include "MP4Demuxer.h" @@ -106,21 +101,6 @@ IsHttpLiveStreamingType(const nsACString& aType) return CodecListContains(gHttpLiveStreamingTypes, aType); } -#ifdef MOZ_ANDROID_OMX -static bool -IsAndroidMediaType(const nsACString& aType) -{ - if (!MediaDecoder::IsAndroidMediaPluginEnabled()) { - return false; - } - - static const char* supportedTypes[] = { - "audio/mpeg", "audio/mp4", "video/mp4", "video/x-m4v", nullptr - }; - return CodecListContains(supportedTypes, aType); -} -#endif - #ifdef MOZ_FMP4 static bool IsMP4SupportedType(const MediaContentType& aParsedType, @@ -235,11 +215,6 @@ CanHandleCodecsType(const MediaContentType& aType, if (IsFlacSupportedType(aType.GetMIMEType(), aType.GetCodecs())) { return CANPLAY_YES; } -#ifdef MOZ_ANDROID_OMX - if (MediaDecoder::IsAndroidMediaPluginEnabled()) { - EnsureAndroidMediaPluginHost()->FindDecoder(aType.GetMIMEType(), &codecList); - } -#endif if (!codecList) { return CANPLAY_MAYBE; } @@ -305,12 +280,6 @@ CanHandleMediaType(const MediaContentType& aType, if (IsFlacSupportedType(aType.GetMIMEType())) { return CANPLAY_MAYBE; } -#ifdef MOZ_ANDROID_OMX - if (MediaDecoder::IsAndroidMediaPluginEnabled() && - EnsureAndroidMediaPluginHost()->FindDecoder(aType.GetMIMEType(), nullptr)) { - return CANPLAY_MAYBE; - } -#endif return CANPLAY_NO; } @@ -391,13 +360,6 @@ InstantiateDecoder(const nsACString& aType, decoder = new FlacDecoder(aOwner); return decoder.forget(); } -#ifdef MOZ_ANDROID_OMX - if (MediaDecoder::IsAndroidMediaPluginEnabled() && - EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) { - decoder = new AndroidMediaDecoder(aOwner, aType); - return decoder.forget(); - } -#endif if (IsWebMSupportedType(aType)) { decoder = new WebMDecoder(aOwner); @@ -446,12 +408,6 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac if (IsOggSupportedType(aType)) { decoderReader = new MediaFormatReader(aDecoder, new OggDemuxer(aDecoder->GetResource())); } else -#ifdef MOZ_ANDROID_OMX - if (MediaDecoder::IsAndroidMediaPluginEnabled() && - EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) { - decoderReader = new AndroidMediaReader(aDecoder, aType); - } else -#endif if (IsWebMSupportedType(aType)) { decoderReader = new MediaFormatReader(aDecoder, new WebMDemuxer(aDecoder->GetResource())); @@ -474,9 +430,6 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType) return IsOggSupportedType(aType) || IsWebMSupportedType(aType) || -#ifdef MOZ_ANDROID_OMX - (MediaDecoder::IsAndroidMediaPluginEnabled() && IsAndroidMediaType(aType)) || -#endif #ifdef MOZ_FMP4 IsMP4SupportedType(aType, /* DecoderDoctorDiagnostics* */ nullptr) || #endif diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index d027818de..cf2266bf6 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -35,10 +35,6 @@ #include "Layers.h" #include "mozilla/layers/ShadowLayers.h" -#ifdef MOZ_ANDROID_OMX -#include "AndroidBridge.h" -#endif - using namespace mozilla::dom; using namespace mozilla::layers; using namespace mozilla::media; @@ -1617,16 +1613,6 @@ MediaDecoder::IsWebMEnabled() return Preferences::GetBool("media.webm.enabled"); } -#ifdef MOZ_ANDROID_OMX -bool -MediaDecoder::IsAndroidMediaPluginEnabled() -{ - return AndroidBridge::Bridge() && - AndroidBridge::Bridge()->GetAPIVersion() < 16 && - Preferences::GetBool("media.plugins.enabled"); -} -#endif - NS_IMETHODIMP MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize) diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index a4edcbe72..7e93de044 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -447,10 +447,6 @@ private: static bool IsWaveEnabled(); static bool IsWebMEnabled(); -#ifdef MOZ_ANDROID_OMX - static bool IsAndroidMediaPluginEnabled(); -#endif - #ifdef MOZ_WMF static bool IsWMFEnabled(); #endif diff --git a/dom/media/android/AndroidMediaDecoder.cpp b/dom/media/android/AndroidMediaDecoder.cpp deleted file mode 100644 index 41ef3fcb0..000000000 --- a/dom/media/android/AndroidMediaDecoder.cpp +++ /dev/null @@ -1,25 +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 "MediaDecoderStateMachine.h" -#include "AndroidMediaDecoder.h" -#include "AndroidMediaReader.h" - -namespace mozilla { - -AndroidMediaDecoder::AndroidMediaDecoder(MediaDecoderOwner* aOwner, - const nsACString& aType) - : MediaDecoder(aOwner), mType(aType) -{ -} - -MediaDecoderStateMachine* AndroidMediaDecoder::CreateStateMachine() -{ - return new MediaDecoderStateMachine(this, new AndroidMediaReader(this, mType)); -} - -} // namespace mozilla - diff --git a/dom/media/android/AndroidMediaDecoder.h b/dom/media/android/AndroidMediaDecoder.h deleted file mode 100644 index 88b5a243f..000000000 --- a/dom/media/android/AndroidMediaDecoder.h +++ /dev/null @@ -1,28 +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(AndroidMediaDecoder_h_) -#define AndroidMediaDecoder_h_ - -#include "MediaDecoder.h" -#include "AndroidMediaDecoder.h" - -namespace mozilla { - -class AndroidMediaDecoder : public MediaDecoder -{ - nsCString mType; -public: - AndroidMediaDecoder(MediaDecoderOwner* aOwner, const nsACString& aType); - - MediaDecoder* Clone(MediaDecoderOwner* aOwner) override { - return new AndroidMediaDecoder(aOwner, mType); - } - MediaDecoderStateMachine* CreateStateMachine() override; -}; - -} // namespace mozilla - -#endif diff --git a/dom/media/android/AndroidMediaPluginHost.cpp b/dom/media/android/AndroidMediaPluginHost.cpp deleted file mode 100644 index d4c4fc59e..000000000 --- a/dom/media/android/AndroidMediaPluginHost.cpp +++ /dev/null @@ -1,305 +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 "mozilla/Preferences.h" -#include "MediaResource.h" -#include "mozilla/dom/HTMLMediaElement.h" -#include "mozilla/Services.h" -#include "AndroidMediaPluginHost.h" -#include "nsAutoPtr.h" -#include "nsXPCOMStrings.h" -#include "nsISeekableStream.h" -#include "nsIGfxInfo.h" -#include "prmem.h" -#include "prlink.h" -#include "AndroidMediaResourceServer.h" -#include "nsServiceManagerUtils.h" - -#include "MPAPI.h" - -#include "nsIPropertyBag2.h" - -#if defined(ANDROID) -#include "android/log.h" -#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "AndroidMediaPluginHost" , ## args) -#else -#define ALOG(args...) /* do nothing */ -#endif - -using namespace MPAPI; - -Decoder::Decoder() : - mResource(nullptr), mPrivate(nullptr) -{ -} - -namespace mozilla { - -static char* GetResource(Decoder *aDecoder) -{ - return static_cast(aDecoder->mResource); -} - -class GetIntPrefEvent : public Runnable { -public: - GetIntPrefEvent(const char* aPref, int32_t* aResult) - : mPref(aPref), mResult(aResult) {} - NS_IMETHOD Run() override { - return Preferences::GetInt(mPref, mResult); - } -private: - const char* mPref; - int32_t* mResult; -}; - -static bool GetIntPref(const char* aPref, int32_t* aResult) -{ - // GetIntPref() is called on the decoder thread, but the Preferences API - // can only be called on the main thread. Post a runnable and wait. - NS_ENSURE_TRUE(aPref, false); - NS_ENSURE_TRUE(aResult, false); - nsCOMPtr event = new GetIntPrefEvent(aPref, aResult); - return NS_SUCCEEDED(NS_DispatchToMainThread(event, NS_DISPATCH_SYNC)); -} - -static bool -GetSystemInfoString(const char *aKey, char *aResult, size_t aResultLength) -{ - NS_ENSURE_TRUE(aKey, false); - NS_ENSURE_TRUE(aResult, false); - - nsCOMPtr infoService = do_GetService("@mozilla.org/system-info;1"); - NS_ASSERTION(infoService, "Could not find a system info service"); - - nsAutoCString key(aKey); - nsAutoCString info; - nsresult rv = infoService->GetPropertyAsACString(NS_ConvertUTF8toUTF16(key), - info); - - NS_ENSURE_SUCCESS(rv, false); - - strncpy(aResult, info.get(), aResultLength); - - return true; -} - -static PluginHost sPluginHost = { - nullptr, - nullptr, - nullptr, - nullptr, - GetIntPref, - GetSystemInfoString -}; - -// Return true if Omx decoding is supported on the device. This checks the -// built in whitelist/blacklist and preferences to see if that is overridden. -static bool IsOmxSupported() -{ - bool forceEnabled = - Preferences::GetBool("stagefright.force-enabled", false); - bool disabled = - Preferences::GetBool("stagefright.disabled", false); - - if (disabled) { - NS_WARNING("XXX stagefright disabled\n"); - return false; - } - - if (!forceEnabled) { - nsCOMPtr gfxInfo = services::GetGfxInfo(); - if (gfxInfo) { - int32_t status; - nsCString discardFailure; - if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_STAGEFRIGHT, discardFailure, &status))) { - if (status != nsIGfxInfo::FEATURE_STATUS_OK) { - NS_WARNING("XXX stagefright blacklisted\n"); - return false; - } - } - } - } - - return true; -} - -// Return the name of the shared library that implements Omx based decoding. This varies -// depending on libstagefright version installed on the device and whether it is B2G vs Android. -// nullptr is returned if Omx decoding is not supported on the device, -static const char* GetOmxLibraryName() -{ -#if defined(ANDROID) - nsCOMPtr infoService = do_GetService("@mozilla.org/system-info;1"); - NS_ASSERTION(infoService, "Could not find a system info service"); - - int32_t version; - nsresult rv = infoService->GetPropertyAsInt32(NS_LITERAL_STRING("version"), &version); - if (NS_SUCCEEDED(rv)) { - ALOG("Android Version is: %d", version); - } - - nsAutoString release_version; - rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("release_version"), release_version); - if (NS_SUCCEEDED(rv)) { - ALOG("Android Release Version is: %s", NS_LossyConvertUTF16toASCII(release_version).get()); - } - - nsAutoString device; - rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("device"), device); - if (NS_SUCCEEDED(rv)) { - ALOG("Android Device is: %s", NS_LossyConvertUTF16toASCII(device).get()); - } - - nsAutoString manufacturer; - rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("manufacturer"), manufacturer); - if (NS_SUCCEEDED(rv)) { - ALOG("Android Manufacturer is: %s", NS_LossyConvertUTF16toASCII(manufacturer).get()); - } - - nsAutoString hardware; - rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("hardware"), hardware); - if (NS_SUCCEEDED(rv)) { - ALOG("Android Hardware is: %s", NS_LossyConvertUTF16toASCII(hardware).get()); - } -#endif - - if (!IsOmxSupported()) - return nullptr; - -#if defined(ANDROID) - if (version >= 17) { - return "libomxpluginkk.so"; - } - - // Ice Cream Sandwich and Jellybean - return "libomxplugin.so"; - -#else - return nullptr; -#endif -} - -AndroidMediaPluginHost::AndroidMediaPluginHost() { - MOZ_COUNT_CTOR(AndroidMediaPluginHost); - MOZ_ASSERT(NS_IsMainThread()); - - mResourceServer = AndroidMediaResourceServer::Start(); - - const char* name = GetOmxLibraryName(); - ALOG("Loading OMX Plugin: %s", name ? name : "nullptr"); - if (name) { - char *path = PR_GetLibraryFilePathname("libxul.so", (PRFuncPtr) GetOmxLibraryName); - PRLibrary *lib = nullptr; - if (path) { - nsAutoCString libpath(path); - PR_Free(path); - int32_t slash = libpath.RFindChar('/'); - if (slash != kNotFound) { - libpath.Truncate(slash + 1); - libpath.Append(name); - lib = PR_LoadLibrary(libpath.get()); - } - } - if (!lib) - lib = PR_LoadLibrary(name); - - if (lib) { - Manifest *manifest = static_cast(PR_FindSymbol(lib, "MPAPI_MANIFEST")); - if (manifest) { - mPlugins.AppendElement(manifest); - ALOG("OMX plugin successfully loaded"); - } - } - } -} - -AndroidMediaPluginHost::~AndroidMediaPluginHost() { - mResourceServer->Stop(); - MOZ_COUNT_DTOR(AndroidMediaPluginHost); -} - -bool AndroidMediaPluginHost::FindDecoder(const nsACString& aMimeType, const char* const** aCodecs) -{ - const char *chars; - size_t len = NS_CStringGetData(aMimeType, &chars, nullptr); - for (size_t n = 0; n < mPlugins.Length(); ++n) { - Manifest *plugin = mPlugins[n]; - const char* const *codecs; - if (plugin->CanDecode(chars, len, &codecs)) { - if (aCodecs) - *aCodecs = codecs; - return true; - } - } - return false; -} - -MPAPI::Decoder *AndroidMediaPluginHost::CreateDecoder(MediaResource *aResource, const nsACString& aMimeType) -{ - NS_ENSURE_TRUE(aResource, nullptr); - - nsAutoPtr decoder(new Decoder()); - if (!decoder) { - return nullptr; - } - - const char *chars; - size_t len = NS_CStringGetData(aMimeType, &chars, nullptr); - for (size_t n = 0; n < mPlugins.Length(); ++n) { - Manifest *plugin = mPlugins[n]; - const char* const *codecs; - if (!plugin->CanDecode(chars, len, &codecs)) { - continue; - } - - nsCString url; - nsresult rv = mResourceServer->AddResource(aResource, url); - if (NS_FAILED (rv)) continue; - - decoder->mResource = strdup(url.get()); - if (plugin->CreateDecoder(&sPluginHost, decoder, chars, len)) { - return decoder.forget(); - } - } - - return nullptr; -} - -void AndroidMediaPluginHost::DestroyDecoder(Decoder *aDecoder) -{ - aDecoder->DestroyDecoder(aDecoder); - char* resource = GetResource(aDecoder); - if (resource) { - // resource *shouldn't* be null, but check anyway just in case the plugin - // decoder does something stupid. - mResourceServer->RemoveResource(nsCString(resource)); - free(resource); - } - delete aDecoder; -} - -AndroidMediaPluginHost *sAndroidMediaPluginHost = nullptr; -AndroidMediaPluginHost *EnsureAndroidMediaPluginHost() -{ - MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); - if (!sAndroidMediaPluginHost) { - sAndroidMediaPluginHost = new AndroidMediaPluginHost(); - } - return sAndroidMediaPluginHost; -} - -AndroidMediaPluginHost *GetAndroidMediaPluginHost() -{ - MOZ_ASSERT(sAndroidMediaPluginHost); - return sAndroidMediaPluginHost; -} - -void AndroidMediaPluginHost::Shutdown() -{ - delete sAndroidMediaPluginHost; - sAndroidMediaPluginHost = nullptr; -} - -} // namespace mozilla diff --git a/dom/media/android/AndroidMediaPluginHost.h b/dom/media/android/AndroidMediaPluginHost.h deleted file mode 100644 index 854b7f21e..000000000 --- a/dom/media/android/AndroidMediaPluginHost.h +++ /dev/null @@ -1,41 +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(AndroidMediaPluginHost_h_) -#define AndroidMediaPluginHost_h_ - -#include "nsTArray.h" -#include "MediaResource.h" -#include "MPAPI.h" -#include "AndroidMediaResourceServer.h" - -namespace mozilla { - -class AndroidMediaPluginHost { - RefPtr mResourceServer; - nsTArray mPlugins; - - MPAPI::Manifest *FindPlugin(const nsACString& aMimeType); -public: - AndroidMediaPluginHost(); - ~AndroidMediaPluginHost(); - - static void Shutdown(); - - bool FindDecoder(const nsACString& aMimeType, const char* const** aCodecs); - MPAPI::Decoder *CreateDecoder(mozilla::MediaResource *aResource, const nsACString& aMimeType); - void DestroyDecoder(MPAPI::Decoder *aDecoder); -}; - -// Must be called on the main thread. Creates the plugin host if it doesn't -// already exist. -AndroidMediaPluginHost *EnsureAndroidMediaPluginHost(); - -// May be called on any thread after EnsureAndroidMediaPluginHost has been called. -AndroidMediaPluginHost *GetAndroidMediaPluginHost(); - -} // namespace mozilla - -#endif diff --git a/dom/media/android/AndroidMediaReader.cpp b/dom/media/android/AndroidMediaReader.cpp deleted file mode 100644 index 12afacbc9..000000000 --- a/dom/media/android/AndroidMediaReader.cpp +++ /dev/null @@ -1,449 +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 "AndroidMediaReader.h" -#include "mozilla/TimeStamp.h" -#include "mozilla/gfx/Point.h" -#include "MediaResource.h" -#include "VideoUtils.h" -#include "AndroidMediaDecoder.h" -#include "AndroidMediaPluginHost.h" -#include "MediaDecoderStateMachine.h" -#include "ImageContainer.h" -#include "AbstractMediaDecoder.h" -#include "gfx2DGlue.h" -#include "VideoFrameContainer.h" -#include "mozilla/CheckedInt.h" - -namespace mozilla { - -using namespace mozilla::gfx; -using namespace mozilla::media; - -typedef mozilla::layers::Image Image; -typedef mozilla::layers::PlanarYCbCrImage PlanarYCbCrImage; - -AndroidMediaReader::AndroidMediaReader(AbstractMediaDecoder *aDecoder, - const nsACString& aContentType) : - MediaDecoderReader(aDecoder), - mType(aContentType), - mPlugin(nullptr), - mHasAudio(false), - mHasVideo(false), - mVideoSeekTimeUs(-1), - mAudioSeekTimeUs(-1) -{ -} - -nsresult AndroidMediaReader::ReadMetadata(MediaInfo* aInfo, - MetadataTags** aTags) -{ - MOZ_ASSERT(OnTaskQueue()); - - if (!mPlugin) { - mPlugin = GetAndroidMediaPluginHost()->CreateDecoder(mDecoder->GetResource(), mType); - if (!mPlugin) { - return NS_ERROR_FAILURE; - } - } - - // Set the total duration (the max of the audio and video track). - int64_t durationUs; - mPlugin->GetDuration(mPlugin, &durationUs); - if (durationUs) { - mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(durationUs)); - } - - if (mPlugin->HasVideo(mPlugin)) { - int32_t width, height; - mPlugin->GetVideoParameters(mPlugin, &width, &height); - nsIntRect pictureRect(0, 0, width, height); - - // Validate the container-reported frame and pictureRect sizes. This ensures - // that our video frame creation code doesn't overflow. - nsIntSize displaySize(width, height); - nsIntSize frameSize(width, height); - if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) { - return NS_ERROR_FAILURE; - } - - // Video track's frame sizes will not overflow. Activate the video track. - mHasVideo = true; - mInfo.mVideo.mDisplay = displaySize; - mPicture = pictureRect; - mInitialFrame = frameSize; - VideoFrameContainer* container = mDecoder->GetVideoFrameContainer(); - if (container) { - container->ClearCurrentFrame(IntSize(displaySize.width, displaySize.height)); - } - } - - if (mPlugin->HasAudio(mPlugin)) { - int32_t numChannels, sampleRate; - mPlugin->GetAudioParameters(mPlugin, &numChannels, &sampleRate); - mHasAudio = true; - mInfo.mAudio.mChannels = numChannels; - mInfo.mAudio.mRate = sampleRate; - } - - *aInfo = mInfo; - *aTags = nullptr; - return NS_OK; -} - -RefPtr -AndroidMediaReader::Shutdown() -{ - ResetDecode(); - if (mPlugin) { - GetAndroidMediaPluginHost()->DestroyDecoder(mPlugin); - mPlugin = nullptr; - } - - return MediaDecoderReader::Shutdown(); -} - -// Resets all state related to decoding, emptying all buffers etc. -nsresult AndroidMediaReader::ResetDecode(TrackSet aTracks) -{ - if (mLastVideoFrame) { - mLastVideoFrame = nullptr; - } - mSeekRequest.DisconnectIfExists(); - mSeekPromise.RejectIfExists(NS_OK, __func__); - return MediaDecoderReader::ResetDecode(aTracks); -} - -bool AndroidMediaReader::DecodeVideoFrame(bool &aKeyframeSkip, - int64_t aTimeThreshold) -{ - // Record number of frames decoded and parsed. Automatically update the - // stats counters using the AutoNotifyDecoded stack-based class. - AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder); - - // Throw away the currently buffered frame if we are seeking. - if (mLastVideoFrame && mVideoSeekTimeUs != -1) { - mLastVideoFrame = nullptr; - } - - ImageBufferCallback bufferCallback(mDecoder->GetImageContainer()); - RefPtr currentImage; - - // Read next frame - while (true) { - MPAPI::VideoFrame frame; - if (!mPlugin->ReadVideo(mPlugin, &frame, mVideoSeekTimeUs, &bufferCallback)) { - // We reached the end of the video stream. If we have a buffered - // video frame, push it the video queue using the total duration - // of the video as the end time. - if (mLastVideoFrame) { - int64_t durationUs; - mPlugin->GetDuration(mPlugin, &durationUs); - durationUs = std::max(durationUs - mLastVideoFrame->mTime, 0); - RefPtr data = VideoData::ShallowCopyUpdateDuration(mLastVideoFrame, - durationUs); - mVideoQueue.Push(data); - mLastVideoFrame = nullptr; - } - return false; - } - mVideoSeekTimeUs = -1; - - if (aKeyframeSkip) { - // Disable keyframe skipping for now as - // stagefright doesn't seem to be telling us - // when a frame is a keyframe. -#if 0 - if (!frame.mKeyFrame) { - ++a.mStats.mParsedFrames; - ++a.mStats.mDroppedFrames; - continue; - } -#endif - aKeyframeSkip = false; - } - - if (frame.mSize == 0) - return true; - - currentImage = bufferCallback.GetImage(); - int64_t pos = mDecoder->GetResource()->Tell(); - IntRect picture = mPicture; - - RefPtr v; - if (currentImage) { - gfx::IntSize frameSize = currentImage->GetSize(); - if (frameSize.width != mInitialFrame.width || - frameSize.height != mInitialFrame.height) { - // Frame size is different from what the container reports. This is legal, - // and we will preserve the ratio of the crop rectangle as it - // was reported relative to the picture size reported by the container. - picture.x = (mPicture.x * frameSize.width) / mInitialFrame.width; - picture.y = (mPicture.y * frameSize.height) / mInitialFrame.height; - picture.width = (frameSize.width * mPicture.width) / mInitialFrame.width; - picture.height = (frameSize.height * mPicture.height) / mInitialFrame.height; - } - - v = VideoData::CreateFromImage(mInfo.mVideo, - pos, - frame.mTimeUs, - 1, // We don't know the duration yet. - currentImage, - frame.mKeyFrame, - -1, - picture); - } else { - // Assume YUV - VideoData::YCbCrBuffer b; - b.mPlanes[0].mData = static_cast(frame.Y.mData); - b.mPlanes[0].mStride = frame.Y.mStride; - b.mPlanes[0].mHeight = frame.Y.mHeight; - b.mPlanes[0].mWidth = frame.Y.mWidth; - b.mPlanes[0].mOffset = frame.Y.mOffset; - b.mPlanes[0].mSkip = frame.Y.mSkip; - - b.mPlanes[1].mData = static_cast(frame.Cb.mData); - b.mPlanes[1].mStride = frame.Cb.mStride; - b.mPlanes[1].mHeight = frame.Cb.mHeight; - b.mPlanes[1].mWidth = frame.Cb.mWidth; - b.mPlanes[1].mOffset = frame.Cb.mOffset; - b.mPlanes[1].mSkip = frame.Cb.mSkip; - - b.mPlanes[2].mData = static_cast(frame.Cr.mData); - b.mPlanes[2].mStride = frame.Cr.mStride; - b.mPlanes[2].mHeight = frame.Cr.mHeight; - b.mPlanes[2].mWidth = frame.Cr.mWidth; - b.mPlanes[2].mOffset = frame.Cr.mOffset; - b.mPlanes[2].mSkip = frame.Cr.mSkip; - - if (frame.Y.mWidth != mInitialFrame.width || - frame.Y.mHeight != mInitialFrame.height) { - - // Frame size is different from what the container reports. This is legal, - // and we will preserve the ratio of the crop rectangle as it - // was reported relative to the picture size reported by the container. - picture.x = (mPicture.x * frame.Y.mWidth) / mInitialFrame.width; - picture.y = (mPicture.y * frame.Y.mHeight) / mInitialFrame.height; - picture.width = (frame.Y.mWidth * mPicture.width) / mInitialFrame.width; - picture.height = (frame.Y.mHeight * mPicture.height) / mInitialFrame.height; - } - - // This is the approximate byte position in the stream. - v = VideoData::CreateAndCopyData(mInfo.mVideo, - mDecoder->GetImageContainer(), - pos, - frame.mTimeUs, - 1, // We don't know the duration yet. - b, - frame.mKeyFrame, - -1, - picture); - } - - if (!v) { - return false; - } - a.mStats.mParsedFrames++; - a.mStats.mDecodedFrames++; - NS_ASSERTION(a.mStats.mDecodedFrames <= a.mStats.mParsedFrames, "Expect to decode fewer frames than parsed in AndroidMedia..."); - - // Since MPAPI doesn't give us the end time of frames, we keep one frame - // buffered in AndroidMediaReader and push it into the queue as soon - // we read the following frame so we can use that frame's start time as - // the end time of the buffered frame. - if (!mLastVideoFrame) { - mLastVideoFrame = v; - continue; - } - - // Calculate the duration as the timestamp of the current frame minus the - // timestamp of the previous frame. We can then return the previously - // decoded frame, and it will have a valid timestamp. - int64_t duration = v->mTime - mLastVideoFrame->mTime; - mLastVideoFrame = VideoData::ShallowCopyUpdateDuration(mLastVideoFrame, duration); - - // We have the start time of the next frame, so we can push the previous - // frame into the queue, except if the end time is below the threshold, - // in which case it wouldn't be displayed anyway. - if (mLastVideoFrame->GetEndTime() < aTimeThreshold) { - mLastVideoFrame = nullptr; - continue; - } - - // Buffer the current frame we just decoded. - mVideoQueue.Push(mLastVideoFrame); - mLastVideoFrame = v; - - break; - } - - return true; -} - -bool AndroidMediaReader::DecodeAudioData() -{ - MOZ_ASSERT(OnTaskQueue()); - - // This is the approximate byte position in the stream. - int64_t pos = mDecoder->GetResource()->Tell(); - - // Read next frame - MPAPI::AudioFrame source; - if (!mPlugin->ReadAudio(mPlugin, &source, mAudioSeekTimeUs)) { - return false; - } - mAudioSeekTimeUs = -1; - - // Ignore empty buffers which stagefright media read will sporadically return - if (source.mSize == 0) - return true; - - uint32_t frames = source.mSize / (source.mAudioChannels * - sizeof(AudioDataValue)); - - typedef AudioCompactor::NativeCopy MPCopy; - return mAudioCompactor.Push(pos, - source.mTimeUs, - source.mAudioSampleRate, - frames, - source.mAudioChannels, - MPCopy(static_cast(source.mData), - source.mSize, - source.mAudioChannels)); -} - -RefPtr -AndroidMediaReader::Seek(SeekTarget aTarget, int64_t aEndTime) -{ - MOZ_ASSERT(OnTaskQueue()); - - RefPtr p = mSeekPromise.Ensure(__func__); - if (mHasAudio && mHasVideo) { - // The decoder seeks/demuxes audio and video streams separately. So if - // we seek both audio and video to aTarget, the audio stream can typically - // seek closer to the seek target, since typically every audio block is - // a sync point, whereas for video there are only keyframes once every few - // seconds. So if we have both audio and video, we must seek the video - // stream to the preceeding keyframe first, get the stream time, and then - // seek the audio stream to match the video stream's time. Otherwise, the - // audio and video streams won't be in sync after the seek. - mVideoSeekTimeUs = aTarget.GetTime().ToMicroseconds(); - - RefPtr self = this; - mSeekRequest.Begin(DecodeToFirstVideoData()->Then(OwnerThread(), __func__, [self] (MediaData* v) { - self->mSeekRequest.Complete(); - self->mAudioSeekTimeUs = v->mTime; - self->mSeekPromise.Resolve(media::TimeUnit::FromMicroseconds(self->mAudioSeekTimeUs), __func__); - }, [self, aTarget] () { - self->mSeekRequest.Complete(); - self->mAudioSeekTimeUs = aTarget.GetTime().ToMicroseconds(); - self->mSeekPromise.Resolve(aTarget.GetTime(), __func__); - })); - } else { - mAudioSeekTimeUs = mVideoSeekTimeUs = aTarget.GetTime().ToMicroseconds(); - mSeekPromise.Resolve(aTarget.GetTime(), __func__); - } - - return p; -} - -AndroidMediaReader::ImageBufferCallback::ImageBufferCallback(mozilla::layers::ImageContainer *aImageContainer) : - mImageContainer(aImageContainer) -{ -} - -void * -AndroidMediaReader::ImageBufferCallback::operator()(size_t aWidth, size_t aHeight, - MPAPI::ColorFormat aColorFormat) -{ - if (!mImageContainer) { - NS_WARNING("No image container to construct an image"); - return nullptr; - } - - RefPtr image; - switch(aColorFormat) { - case MPAPI::RGB565: - image = mozilla::layers::CreateSharedRGBImage(mImageContainer, - nsIntSize(aWidth, aHeight), - SurfaceFormat::R5G6B5_UINT16); - if (!image) { - NS_WARNING("Could not create rgb image"); - return nullptr; - } - - mImage = image; - return image->GetBuffer(); - case MPAPI::I420: - return CreateI420Image(aWidth, aHeight); - default: - NS_NOTREACHED("Color format not supported"); - return nullptr; - } -} - -uint8_t * -AndroidMediaReader::ImageBufferCallback::CreateI420Image(size_t aWidth, - size_t aHeight) -{ - RefPtr yuvImage = mImageContainer->CreatePlanarYCbCrImage(); - mImage = yuvImage; - - if (!yuvImage) { - NS_WARNING("Could not create I420 image"); - return nullptr; - } - - // Use uint32_t throughout to match AllocateAndGetNewBuffer's param - const auto checkedFrameSize = - CheckedInt(aWidth) * aHeight; - - // Allocate enough for one full resolution Y plane - // and two quarter resolution Cb/Cr planes. - const auto checkedBufferSize = - checkedFrameSize + checkedFrameSize / 2; - - if (!checkedBufferSize.isValid()) { // checks checkedFrameSize too - NS_WARNING("Could not create I420 image"); - return nullptr; - } - - const auto frameSize = checkedFrameSize.value(); - - uint8_t *buffer = - yuvImage->AllocateAndGetNewBuffer(checkedBufferSize.value()); - - mozilla::layers::PlanarYCbCrData frameDesc; - - frameDesc.mYChannel = buffer; - frameDesc.mCbChannel = buffer + frameSize; - frameDesc.mCrChannel = frameDesc.mCbChannel + frameSize / 4; - - frameDesc.mYSize = IntSize(aWidth, aHeight); - frameDesc.mCbCrSize = IntSize(aWidth / 2, aHeight / 2); - - frameDesc.mYStride = aWidth; - frameDesc.mCbCrStride = aWidth / 2; - - frameDesc.mYSkip = 0; - frameDesc.mCbSkip = 0; - frameDesc.mCrSkip = 0; - - frameDesc.mPicX = 0; - frameDesc.mPicY = 0; - frameDesc.mPicSize = IntSize(aWidth, aHeight); - - yuvImage->AdoptData(frameDesc); - - return buffer; -} - -already_AddRefed -AndroidMediaReader::ImageBufferCallback::GetImage() -{ - return mImage.forget(); -} - -} // namespace mozilla diff --git a/dom/media/android/AndroidMediaReader.h b/dom/media/android/AndroidMediaReader.h deleted file mode 100644 index def85a343..000000000 --- a/dom/media/android/AndroidMediaReader.h +++ /dev/null @@ -1,75 +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(AndroidMediaReader_h_) -#define AndroidMediaReader_h_ - -#include "mozilla/Attributes.h" -#include "MediaResource.h" -#include "MediaDecoderReader.h" -#include "ImageContainer.h" -#include "mozilla/layers/SharedRGBImage.h" - -#include "MPAPI.h" - -class nsACString; - -namespace mozilla { - -class AbstractMediaDecoder; - -namespace layers { -class ImageContainer; -} - -class AndroidMediaReader : public MediaDecoderReader -{ - nsCString mType; - MPAPI::Decoder *mPlugin; - bool mHasAudio; - bool mHasVideo; - nsIntRect mPicture; - nsIntSize mInitialFrame; - int64_t mVideoSeekTimeUs; - int64_t mAudioSeekTimeUs; - RefPtr mLastVideoFrame; - MozPromiseHolder mSeekPromise; - MozPromiseRequestHolder mSeekRequest; -public: - AndroidMediaReader(AbstractMediaDecoder* aDecoder, - const nsACString& aContentType); - - nsresult ResetDecode(TrackSet aTracks = TrackSet(TrackInfo::kAudioTrack, - TrackInfo::kVideoTrack)) override; - - bool DecodeAudioData() override; - bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) override; - - nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) override; - RefPtr Seek(SeekTarget aTarget, int64_t aEndTime) override; - - RefPtr Shutdown() override; - - class ImageBufferCallback : public MPAPI::BufferCallback { - typedef mozilla::layers::Image Image; - - public: - ImageBufferCallback(mozilla::layers::ImageContainer *aImageContainer); - void *operator()(size_t aWidth, size_t aHeight, - MPAPI::ColorFormat aColorFormat) override; - already_AddRefed GetImage(); - - private: - uint8_t *CreateI420Image(size_t aWidth, size_t aHeight); - - mozilla::layers::ImageContainer *mImageContainer; - RefPtr mImage; - }; - -}; - -} // namespace mozilla - -#endif diff --git a/dom/media/android/AndroidMediaResourceServer.cpp b/dom/media/android/AndroidMediaResourceServer.cpp deleted file mode 100644 index bd76a8c68..000000000 --- a/dom/media/android/AndroidMediaResourceServer.cpp +++ /dev/null @@ -1,503 +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 "mozilla/Assertions.h" -#include "mozilla/Base64.h" -#include "mozilla/IntegerPrintfMacros.h" -#include "mozilla/UniquePtr.h" -#include "nsThreadUtils.h" -#include "nsIServiceManager.h" -#include "nsISocketTransport.h" -#include "nsIOutputStream.h" -#include "nsIInputStream.h" -#include "nsIRandomGenerator.h" -#include "nsReadLine.h" -#include "nsNetCID.h" -#include "VideoUtils.h" -#include "MediaResource.h" -#include "AndroidMediaResourceServer.h" - -#if defined(_MSC_VER) -#define strtoll _strtoi64 -#endif - -using namespace mozilla; - -/* - ReadCRLF is a variant of NS_ReadLine from nsReadLine.h that deals - with the carriage return/line feed requirements of HTTP requests. -*/ -template -nsresult -ReadCRLF (StreamType* aStream, nsLineBuffer * aBuffer, - StringType & aLine, bool *aMore) -{ - // eollast is true if the last character in the buffer is a '\r', - // signaling a potential '\r\n' sequence split between reads. - bool eollast = false; - - aLine.Truncate(); - - while (1) { // will be returning out of this loop on eol or eof - if (aBuffer->start == aBuffer->end) { // buffer is empty. Read into it. - uint32_t bytesRead; - nsresult rv = aStream->Read(aBuffer->buf, kLineBufferSize, &bytesRead); - if (NS_FAILED(rv) || bytesRead == 0) { - *aMore = false; - return rv; - } - aBuffer->start = aBuffer->buf; - aBuffer->end = aBuffer->buf + bytesRead; - *(aBuffer->end) = '\0'; - } - - /* - * Walk the buffer looking for an end-of-line. - * There are 4 cases to consider: - * 1. the CR char is the last char in the buffer - * 2. the CRLF sequence are the last characters in the buffer - * 3. the CRLF sequence + one or more chars at the end of the buffer - * we need at least one char after the first CRLF sequence to - * set |aMore| correctly. - * 4. The LF character is the first char in the buffer when eollast is - * true. - */ - CharT* current = aBuffer->start; - if (eollast) { // Case 4 - if (*current == '\n') { - aBuffer->start = ++current; - *aMore = true; - return NS_OK; - } - else { - eollast = false; - aLine.Append('\r'); - } - } - // Cases 2 and 3 - for ( ; current < aBuffer->end-1; ++current) { - if (*current == '\r' && *(current+1) == '\n') { - *current++ = '\0'; - *current++ = '\0'; - aLine.Append(aBuffer->start); - aBuffer->start = current; - *aMore = true; - return NS_OK; - } - } - // Case 1 - if (*current == '\r') { - eollast = true; - *current++ = '\0'; - } - - aLine.Append(aBuffer->start); - aBuffer->start = aBuffer->end; // mark the buffer empty - } -} - -// Each client HTTP request results in a thread being spawned to process it. -// That thread has a single event dispatched to it which handles the HTTP -// protocol. It parses the headers and forwards data from the MediaResource -// associated with the URL back to client. When the request is complete it will -// shutdown the thread. -class ServeResourceEvent : public Runnable { -private: - // Reading from this reads the data sent from the client. - nsCOMPtr mInput; - - // Writing to this sends data to the client. - nsCOMPtr mOutput; - - // The AndroidMediaResourceServer that owns the MediaResource instances - // served. This is used to lookup the MediaResource from the URL. - RefPtr mServer; - - // Write 'aBufferLength' bytes from 'aBuffer' to 'mOutput'. This - // method ensures all the data is written by checking the number - // of bytes returned from the output streams 'Write' method and - // looping until done. - nsresult WriteAll(char const* aBuffer, int32_t aBufferLength); - -public: - ServeResourceEvent(nsIInputStream* aInput, nsIOutputStream* aOutput, - AndroidMediaResourceServer* aServer) - : mInput(aInput), mOutput(aOutput), mServer(aServer) {} - - // This method runs on the thread and exits when it has completed the - // HTTP request. - NS_IMETHOD Run(); - - // Given the first line of an HTTP request, parse the URL requested and - // return the MediaResource for that URL. - already_AddRefed GetMediaResource(nsCString const& aHTTPRequest); - - // Gracefully shutdown the thread and cleanup resources - void Shutdown(); -}; - -nsresult -ServeResourceEvent::WriteAll(char const* aBuffer, int32_t aBufferLength) -{ - while (aBufferLength > 0) { - uint32_t written = 0; - nsresult rv = mOutput->Write(aBuffer, aBufferLength, &written); - if (NS_FAILED (rv)) return rv; - - aBufferLength -= written; - aBuffer += written; - } - - return NS_OK; -} - -already_AddRefed -ServeResourceEvent::GetMediaResource(nsCString const& aHTTPRequest) -{ - // Check that the HTTP method is GET - const char* HTTP_METHOD = "GET "; - if (strncmp(aHTTPRequest.get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) { - return nullptr; - } - - const char* url_start = strchr(aHTTPRequest.get(), ' '); - if (!url_start) { - return nullptr; - } - - const char* url_end = strrchr(++url_start, ' '); - if (!url_end) { - return nullptr; - } - - // The path extracted from the HTTP request is used as a key in hash - // table. It is not related to retrieving data from the filesystem so - // we don't need to do any sanity checking on ".." paths and similar - // exploits. - nsCString relative(url_start, url_end - url_start); - RefPtr resource = - mServer->GetResource(mServer->GetURLPrefix() + relative); - return resource.forget(); -} - -NS_IMETHODIMP -ServeResourceEvent::Run() { - bool more = false; // Are there HTTP headers to read after the first line - nsCString line; // Contains the current line read from input stream - nsLineBuffer* buffer = new nsLineBuffer(); - nsresult rv = ReadCRLF(mInput.get(), buffer, line, &more); - if (NS_FAILED(rv)) { Shutdown(); return rv; } - - // First line contains the HTTP GET request. Extract the URL and obtain - // the MediaResource for it. - RefPtr resource = GetMediaResource(line); - if (!resource) { - const char* response_404 = "HTTP/1.1 404 Not Found\r\n" - "Content-Length: 0\r\n\r\n"; - rv = WriteAll(response_404, strlen(response_404)); - Shutdown(); - return rv; - } - - // Offset in bytes to start reading from resource. - // This is zero by default but can be set to another starting value if - // this HTTP request includes a byte range request header. - int64_t start = 0; - - // Keep reading lines until we get a zero length line, which is the HTTP - // protocol's way of signifying the end of headers and start of body, or - // until we have no more data to read. - while (more && line.Length() > 0) { - rv = ReadCRLF(mInput.get(), buffer, line, &more); - if (NS_FAILED(rv)) { Shutdown(); return rv; } - - // Look for a byte range request header. If there is one, set the - // media resource offset to start from to that requested. Here we - // only check for the range request format used by Android rather - // than implementing all possibilities in the HTTP specification. - // That is, the range request is of the form: - // Range: bytes=nnnn- - // Were 'nnnn' is an integer number. - // The end of the range is not checked, instead we return up to - // the end of the resource and the client is informed of this via - // the content-range header. - NS_NAMED_LITERAL_CSTRING(byteRange, "Range: bytes="); - const char* s = strstr(line.get(), byteRange.get()); - if (s) { - start = strtoll(s+byteRange.Length(), nullptr, 10); - - // Clamp 'start' to be between 0 and the resource length. - start = std::max(int64_t(0), std::min(resource->GetLength(), start)); - } - } - - // HTTP response to use if this is a non byte range request - const char* response_normal = "HTTP/1.1 200 OK\r\n"; - - // HTTP response to use if this is a byte range request - const char* response_range = "HTTP/1.1 206 Partial Content\r\n"; - - // End of HTTP reponse headers is indicated by an empty line. - const char* response_end = "\r\n"; - - // If the request was a byte range request, we need to read from the - // requested offset. If the resource is non-seekable, or the seek - // fails, then the start offset is set back to zero. This results in all - // HTTP response data being as if the byte range request was not made. - if (start > 0 && !resource->IsTransportSeekable()) { - start = 0; - } - - const char* response_line = start > 0 ? - response_range : - response_normal; - rv = WriteAll(response_line, strlen(response_line)); - if (NS_FAILED(rv)) { Shutdown(); return NS_OK; } - - // Buffer used for reading from the input stream and writing to - // the output stream. The buffer size should be big enough for the - // HTTP response headers sent below. A static_assert ensures - // this where the buffer is used. - const int buffer_size = 32768; - auto b = MakeUnique(buffer_size); - - // If we know the length of the resource, send a Content-Length header. - int64_t contentlength = resource->GetLength() - start; - if (contentlength > 0) { - static_assert (buffer_size > 1024, - "buffer_size must be large enough " - "to hold response headers"); - snprintf(b.get(), buffer_size, "Content-Length: %" PRId64 "\r\n", contentlength); - rv = WriteAll(b.get(), strlen(b.get())); - if (NS_FAILED(rv)) { Shutdown(); return NS_OK; } - } - - // If the request was a byte range request, respond with a Content-Range - // header which details the extent of the data returned. - if (start > 0) { - static_assert (buffer_size > 1024, - "buffer_size must be large enough " - "to hold response headers"); - snprintf(b.get(), buffer_size, "Content-Range: " - "bytes %" PRId64 "-%" PRId64 "/%" PRId64 "\r\n", - start, resource->GetLength() - 1, resource->GetLength()); - rv = WriteAll(b.get(), strlen(b.get())); - if (NS_FAILED(rv)) { Shutdown(); return NS_OK; } - } - - rv = WriteAll(response_end, strlen(response_end)); - if (NS_FAILED(rv)) { Shutdown(); return NS_OK; } - - rv = mOutput->Flush(); - if (NS_FAILED(rv)) { Shutdown(); return NS_OK; } - - // Read data from media resource - uint32_t bytesRead = 0; // Number of bytes read/written to streams - rv = resource->ReadAt(start, b.get(), buffer_size, &bytesRead); - while (NS_SUCCEEDED(rv) && bytesRead != 0) { - // Keep track of what we think the starting position for the next read - // is. This is used in subsequent ReadAt calls to ensure we are reading - // from the correct offset in the case where another thread is reading - // from th same MediaResource. - start += bytesRead; - - // Write data obtained from media resource to output stream - rv = WriteAll(b.get(), bytesRead); - if (NS_FAILED (rv)) break; - - rv = resource->ReadAt(start, b.get(), 32768, &bytesRead); - } - - Shutdown(); - return NS_OK; -} - -void -ServeResourceEvent::Shutdown() -{ - // Cleanup resources and exit. - mInput->Close(); - mOutput->Close(); - - // To shutdown the current thread we need to first exit this event. - // The Shutdown event below is posted to the main thread to do this. - nsCOMPtr event = new ShutdownThreadEvent(NS_GetCurrentThread()); - NS_DispatchToMainThread(event); -} - -/* - This is the listener attached to the server socket. When an HTTP - request is made by the client the OnSocketAccepted method is - called. This method will spawn a thread to process the request. - The thread receives a single event which does the parsing of - the HTTP request and forwarding the data from the MediaResource - to the output stream of the request. - - The MediaResource used for providing the request data is obtained - from the AndroidMediaResourceServer that created this listener, using the - URL the client requested. -*/ -class ResourceSocketListener : public nsIServerSocketListener -{ -public: - // The AndroidMediaResourceServer used to look up the MediaResource - // on requests. - RefPtr mServer; - - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSISERVERSOCKETLISTENER - - ResourceSocketListener(AndroidMediaResourceServer* aServer) : - mServer(aServer) - { - } - -private: - virtual ~ResourceSocketListener() { } -}; - -NS_IMPL_ISUPPORTS(ResourceSocketListener, nsIServerSocketListener) - -NS_IMETHODIMP -ResourceSocketListener::OnSocketAccepted(nsIServerSocket* aServ, - nsISocketTransport* aTrans) -{ - nsCOMPtr input; - nsCOMPtr output; - nsresult rv; - - rv = aTrans->OpenInputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(input)); - if (NS_FAILED(rv)) return rv; - - rv = aTrans->OpenOutputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(output)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr thread; - rv = NS_NewThread(getter_AddRefs(thread)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr event = new ServeResourceEvent(input.get(), output.get(), mServer); - return thread->Dispatch(event, NS_DISPATCH_NORMAL); -} - -NS_IMETHODIMP -ResourceSocketListener::OnStopListening(nsIServerSocket* aServ, nsresult aStatus) -{ - return NS_OK; -} - -AndroidMediaResourceServer::AndroidMediaResourceServer() : - mMutex("AndroidMediaResourceServer") -{ -} - -NS_IMETHODIMP -AndroidMediaResourceServer::Run() -{ - MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); - MutexAutoLock lock(mMutex); - - nsresult rv; - mSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID, &rv); - if (NS_FAILED(rv)) return rv; - - rv = mSocket->InitSpecialConnection(-1, - nsIServerSocket::LoopbackOnly - | nsIServerSocket::KeepWhenOffline, - -1); - if (NS_FAILED(rv)) return rv; - - rv = mSocket->AsyncListen(new ResourceSocketListener(this)); - if (NS_FAILED(rv)) return rv; - - return NS_OK; -} - -/* static */ -already_AddRefed -AndroidMediaResourceServer::Start() -{ - MOZ_ASSERT(NS_IsMainThread()); - RefPtr server = new AndroidMediaResourceServer(); - server->Run(); - return server.forget(); -} - -void -AndroidMediaResourceServer::Stop() -{ - MutexAutoLock lock(mMutex); - mSocket->Close(); - mSocket = nullptr; -} - -nsresult -AndroidMediaResourceServer::AppendRandomPath(nsCString& aUrl) -{ - // Use a cryptographic quality PRNG to generate raw random bytes - // and convert that to a base64 string for use as an URL path. This - // is based on code from nsExternalAppHandler::SetUpTempFile. - nsresult rv; - nsAutoCString salt; - rv = GenerateRandomPathName(salt, 16); - if (NS_FAILED(rv)) return rv; - aUrl += "/"; - aUrl += salt; - return NS_OK; -} - -nsresult -AndroidMediaResourceServer::AddResource(mozilla::MediaResource* aResource, nsCString& aUrl) -{ - nsCString url = GetURLPrefix(); - nsresult rv = AppendRandomPath(url); - if (NS_FAILED (rv)) return rv; - - { - MutexAutoLock lock(mMutex); - - // Adding a resource URL that already exists is considered an error. - if (mResources.find(url) != mResources.end()) return NS_ERROR_FAILURE; - mResources[url] = aResource; - } - - aUrl = url; - - return NS_OK; -} - -void -AndroidMediaResourceServer::RemoveResource(nsCString const& aUrl) -{ - MutexAutoLock lock(mMutex); - mResources.erase(aUrl); -} - -nsCString -AndroidMediaResourceServer::GetURLPrefix() -{ - MutexAutoLock lock(mMutex); - - int32_t port = 0; - nsresult rv = mSocket->GetPort(&port); - if (NS_FAILED (rv) || port < 0) { - return nsCString(""); - } - - char buffer[256]; - snprintf(buffer, sizeof(buffer), "http://127.0.0.1:%d", port >= 0 ? port : 0); - return nsCString(buffer); -} - -already_AddRefed -AndroidMediaResourceServer::GetResource(nsCString const& aUrl) -{ - MutexAutoLock lock(mMutex); - ResourceMap::const_iterator it = mResources.find(aUrl); - if (it == mResources.end()) return nullptr; - - RefPtr resource = it->second; - return resource.forget(); -} diff --git a/dom/media/android/AndroidMediaResourceServer.h b/dom/media/android/AndroidMediaResourceServer.h deleted file mode 100644 index 68200f9c0..000000000 --- a/dom/media/android/AndroidMediaResourceServer.h +++ /dev/null @@ -1,96 +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(AndroidMediaResourceServer_h_) -#define AndroidMediaResourceServer_h_ - -#include -#include "nsIServerSocket.h" -#include "MediaResource.h" - -namespace mozilla { - -class MediaResource; - -/* - AndroidMediaResourceServer instantiates a socket server that understands - HTTP requests for MediaResource instances. The server runs on an - automatically selected port and MediaResource instances are registered. - The registration returns a string URL than can be used to fetch the - resource. That URL contains a randomly generated path to make it - difficult for other local applications on the device to guess it. - - The HTTP protocol is limited in that it supports only what the - Android DataSource implementation uses to fetch media. It - understands HTTP GET and byte range requests. - - The intent of this class is to be used in Media backends that - have a system component that does its own network requests. These - requests are made against this server which then uses standard - Gecko network requests and media cache usage. - - The AndroidMediaResourceServer can be instantiated on any thread and - its methods are threadsafe - they can be called on any thread. - The server socket itself is always run on the main thread and - this is done by the Start() static method by synchronously - dispatching to the main thread. -*/ -class AndroidMediaResourceServer : public Runnable -{ -private: - // Mutex protecting private members of AndroidMediaResourceServer. - // All member variables below this point in the class definition - // must acquire the mutex before access. - mozilla::Mutex mMutex; - - // Server socket used to listen for incoming connections - nsCOMPtr mSocket; - - // Mapping between MediaResource URL's to the MediaResource - // object served at that URL. - typedef std::map > ResourceMap; - ResourceMap mResources; - - // Create a AndroidMediaResourceServer that will listen on an automatically - // selected port when started. This is private as it should only be - // called internally from the public 'Start' method. - AndroidMediaResourceServer(); - NS_IMETHOD Run(); - - // Append a random URL path to a string. This is used for creating a - // unique URl for a resource which helps prevent malicious software - // running on the same machine as the server from guessing the URL - // and accessing video data. - nsresult AppendRandomPath(nsCString& aURL); - -public: - // Create a AndroidMediaResourceServer and start it listening. This call will - // perform a synchronous request on the main thread. - static already_AddRefed Start(); - - // Stops the server from listening and accepting further connections. - void Stop(); - - // Add a MediaResource to be served by this server. Stores the - // absolute URL that can be used to access the resource in 'aUrl'. - nsresult AddResource(mozilla::MediaResource* aResource, nsCString& aUrl); - - // Remove a MediaResource so it is no longer served by this server. - // The URL provided must match exactly that provided by a previous - // call to "AddResource". - void RemoveResource(nsCString const& aUrl); - - // Returns the prefix for HTTP requests to the server. This plus - // the result of AddResource results in an Absolute URL. - nsCString GetURLPrefix(); - - // Returns the resource asociated with a given URL - already_AddRefed GetResource(nsCString const& aUrl); -}; - -} // namespace mozilla - -#endif diff --git a/dom/media/android/MPAPI.h b/dom/media/android/MPAPI.h deleted file mode 100644 index 9b289ca09..000000000 --- a/dom/media/android/MPAPI.h +++ /dev/null @@ -1,165 +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(MPAPI_h_) -#define MPAPI_h_ - -#include - -namespace MPAPI { - -enum ColorFormat { - I420, - RGB565 -}; - -/* - * A callback for the plugin to use to request a buffer owned by gecko. This can - * save us a copy or two down the line. - */ -class BufferCallback { -public: - virtual void *operator()(size_t aWidth, size_t aHeight, - ColorFormat aColorFormat) = 0; -}; - -struct VideoPlane { - VideoPlane() : - mData(0), - mStride(0), - mWidth(0), - mHeight(0), - mOffset(0), - mSkip(0) - {} - - void *mData; - int32_t mStride; - int32_t mWidth; - int32_t mHeight; - int32_t mOffset; - int32_t mSkip; -}; - -struct VideoFrame { - int64_t mTimeUs; - bool mKeyFrame; - void *mData; - size_t mSize; - int32_t mStride; - int32_t mSliceHeight; - int32_t mRotation; - VideoPlane Y; - VideoPlane Cb; - VideoPlane Cr; - - VideoFrame() : - mTimeUs(0), - mKeyFrame(false), - mData(0), - mSize(0), - mStride(0), - mSliceHeight(0), - mRotation(0) - {} - - void Set(int64_t aTimeUs, bool aKeyFrame, - void *aData, size_t aSize, int32_t aStride, int32_t aSliceHeight, int32_t aRotation, - void *aYData, int32_t aYStride, int32_t aYWidth, int32_t aYHeight, int32_t aYOffset, int32_t aYSkip, - void *aCbData, int32_t aCbStride, int32_t aCbWidth, int32_t aCbHeight, int32_t aCbOffset, int32_t aCbSkip, - void *aCrData, int32_t aCrStride, int32_t aCrWidth, int32_t aCrHeight, int32_t aCrOffset, int32_t aCrSkip) - { - mTimeUs = aTimeUs; - mKeyFrame = aKeyFrame; - mData = aData; - mSize = aSize; - mStride = aStride; - mSliceHeight = aSliceHeight; - mRotation = aRotation; - Y.mData = aYData; - Y.mStride = aYStride; - Y.mWidth = aYWidth; - Y.mHeight = aYHeight; - Y.mOffset = aYOffset; - Y.mSkip = aYSkip; - Cb.mData = aCbData; - Cb.mStride = aCbStride; - Cb.mWidth = aCbWidth; - Cb.mHeight = aCbHeight; - Cb.mOffset = aCbOffset; - Cb.mSkip = aCbSkip; - Cr.mData = aCrData; - Cr.mStride = aCrStride; - Cr.mWidth = aCrWidth; - Cr.mHeight = aCrHeight; - Cr.mOffset = aCrOffset; - Cr.mSkip = aCrSkip; - } -}; - -struct AudioFrame { - int64_t mTimeUs; - void *mData; // 16PCM interleaved - size_t mSize; // Size of mData in bytes - int32_t mAudioChannels; - int32_t mAudioSampleRate; - - AudioFrame() : - mTimeUs(0), - mData(0), - mSize(0), - mAudioChannels(0), - mAudioSampleRate(0) - { - } - - void Set(int64_t aTimeUs, - void *aData, size_t aSize, - int32_t aAudioChannels, int32_t aAudioSampleRate) - { - mTimeUs = aTimeUs; - mData = aData; - mSize = aSize; - mAudioChannels = aAudioChannels; - mAudioSampleRate = aAudioSampleRate; - } -}; - -struct Decoder; - -struct PluginHost { - bool (*Read)(Decoder *aDecoder, char *aBuffer, int64_t aOffset, uint32_t aCount, uint32_t* aBytes); - uint64_t (*GetLength)(Decoder *aDecoder); - void (*SetMetaDataReadMode)(Decoder *aDecoder); - void (*SetPlaybackReadMode)(Decoder *aDecoder); - bool (*GetIntPref)(const char *aPref, int32_t *aResult); - bool (*GetSystemInfoString)(const char *aKey, char *aResult, size_t aResultLen); -}; - -struct Decoder { - void *mResource; - void *mPrivate; - - Decoder(); - - void (*GetDuration)(Decoder *aDecoder, int64_t *durationUs); - void (*GetVideoParameters)(Decoder *aDecoder, int32_t *aWidth, int32_t *aHeight); - void (*GetAudioParameters)(Decoder *aDecoder, int32_t *aNumChannels, int32_t *aSampleRate); - bool (*HasVideo)(Decoder *aDecoder); - bool (*HasAudio)(Decoder *aDecoder); - bool (*ReadVideo)(Decoder *aDecoder, VideoFrame *aFrame, int64_t aSeekTimeUs, BufferCallback *aBufferCallback); - bool (*ReadAudio)(Decoder *aDecoder, AudioFrame *aFrame, int64_t aSeekTimeUs); - void (*DestroyDecoder)(Decoder *); -}; - -struct Manifest { - bool (*CanDecode)(const char *aMimeChars, size_t aMimeLen, const char* const**aCodecs); - bool (*CreateDecoder)(PluginHost *aPluginHost, Decoder *aDecoder, - const char *aMimeChars, size_t aMimeLen); -}; - -} - -#endif diff --git a/dom/media/android/moz.build b/dom/media/android/moz.build deleted file mode 100644 index 3ad43cd50..000000000 --- a/dom/media/android/moz.build +++ /dev/null @@ -1,27 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXPORTS += [ - 'AndroidMediaDecoder.h', - 'AndroidMediaPluginHost.h', - 'AndroidMediaReader.h', - 'AndroidMediaResourceServer.h', - 'MPAPI.h', -] - -UNIFIED_SOURCES += [ - 'AndroidMediaDecoder.cpp', - 'AndroidMediaPluginHost.cpp', - 'AndroidMediaReader.cpp', - 'AndroidMediaResourceServer.cpp', -] - -LOCAL_INCLUDES += [ - '/dom/base', - '/dom/html', -] - -FINAL_LIBRARY = 'xul' diff --git a/dom/media/moz.build b/dom/media/moz.build index 6d2c71f2d..df8cb619d 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -43,9 +43,6 @@ DIRS += [ 'standalone', ] -if CONFIG['MOZ_ANDROID_OMX']: - DIRS += ['android'] - if CONFIG['MOZ_FMP4']: DIRS += ['fmp4'] -- cgit v1.2.3 From 515e0794f832e6557c0315e0f397be90fd6395af Mon Sep 17 00:00:00 2001 From: trav90 Date: Thu, 27 Dec 2018 11:11:35 -0600 Subject: [FFmpeg] Use new decode API with recent FFmpeg version. In libavcodec 58 and later, the old avcodec_decode_video2 is broken and only return the first visible frame found after a VP9 super-frame. --- dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp | 2 + dom/media/platforms/ffmpeg/FFmpegLibWrapper.h | 4 ++ dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp | 66 ++++++++++++++++++++--- dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h | 1 + 4 files changed, 66 insertions(+), 7 deletions(-) (limited to 'dom/media') diff --git a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp index e1c326818..426e9f74b 100644 --- a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp @@ -145,6 +145,8 @@ FFmpegLibWrapper::Link() AV_FUNC(avcodec_alloc_frame, (AV_FUNC_53 | AV_FUNC_54)) AV_FUNC(avcodec_get_frame_defaults, (AV_FUNC_53 | AV_FUNC_54)) AV_FUNC(avcodec_free_frame, AV_FUNC_54) + AV_FUNC(avcodec_send_packet, AV_FUNC_58) + AV_FUNC(avcodec_receive_frame, AV_FUNC_58) AV_FUNC(av_log_set_level, AV_FUNC_AVUTIL_ALL) AV_FUNC(av_malloc, AV_FUNC_AVUTIL_ALL) AV_FUNC(av_freep, AV_FUNC_AVUTIL_ALL) diff --git a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h index c6c43a4ae..b968edd32 100644 --- a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h +++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h @@ -71,6 +71,10 @@ struct FFmpegLibWrapper // libavcodec v54 only void (*avcodec_free_frame)(AVFrame** frame); + // libavcodec v58 and later only + int (*avcodec_send_packet)(AVCodecContext* avctx, const AVPacket* avpkt); + int (*avcodec_receive_frame)(AVCodecContext* avctx, AVFrame* frame); + // libavutil void (*av_log_set_level)(int level); void* (*av_malloc)(size_t size); diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp index aec1e9136..d71a86b3c 100644 --- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp @@ -224,6 +224,48 @@ FFmpegVideoDecoder::DoDecode(MediaRawData* aSample, packet.flags = aSample->mKeyframe ? AV_PKT_FLAG_KEY : 0; packet.pos = aSample->mOffset; +#if LIBAVCODEC_VERSION_MAJOR >= 58 + packet.duration = aSample->mDuration; + int res = mLib->avcodec_send_packet(mCodecContext, &packet); + if (res < 0) { + // In theory, avcodec_send_packet could sent -EAGAIN should its internal + // buffers be full. In practice this can't happen as we only feed one frame + // at a time, and we immediately call avcodec_receive_frame right after. + FFMPEG_LOG("avcodec_send_packet error: %d", res); + return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, + RESULT_DETAIL("avcodec_send_packet error: %d", res)); + } + + if (aGotFrame) { + *aGotFrame = false; + } + do { + if (!PrepareFrame()) { + NS_WARNING("FFmpeg h264 decoder failed to allocate frame."); + return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__); + } + res = mLib->avcodec_receive_frame(mCodecContext, mFrame); + if (res == int(AVERROR_EOF)) { + return NS_ERROR_DOM_MEDIA_END_OF_STREAM; + } + if (res == AVERROR(EAGAIN)) { + return NS_OK; + } + if (res < 0) { + FFMPEG_LOG("avcodec_receive_frame error: %d", res); + return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, + RESULT_DETAIL("avcodec_receive_frame error: %d", res)); + } + MediaResult rv = CreateImage(mFrame->pkt_pos, mFrame->pkt_pts, + mFrame->pkt_duration); + if (NS_FAILED(rv)) { + return rv; + } + if (aGotFrame) { + *aGotFrame = true; + } + } while (true); +#else // LibAV provides no API to retrieve the decoded sample's duration. // (FFmpeg >= 1.0 provides av_frame_get_pkt_duration) // As such we instead use a map using the dts as key that we will retrieve @@ -276,8 +318,21 @@ FFmpegVideoDecoder::DoDecode(MediaRawData* aSample, // against the map becoming extremely big. mDurationMap.Clear(); } + + MediaResult rv = CreateImage(aSample->mOffset, pts, duration); + if (NS_SUCCEEDED(rv) && aGotFrame) { + *aGotFrame = true; + } + return rv; +#endif +} + +MediaResult +FFmpegVideoDecoder::CreateImage(int64_t aOffset, int64_t aPts, + int64_t aDuration) +{ FFMPEG_LOG("Got one frame output with pts=%lld dts=%lld duration=%lld opaque=%lld", - pts, mFrame->pkt_dts, duration, mCodecContext->reordered_opaque); + aPts, mFrame->pkt_dts, aDuration, mCodecContext->reordered_opaque); VideoData::YCbCrBuffer b; b.mPlanes[0].mData = mFrame->data[0]; @@ -317,9 +372,9 @@ FFmpegVideoDecoder::DoDecode(MediaRawData* aSample, RefPtr v = VideoData::CreateAndCopyData(mInfo, mImageContainer, - aSample->mOffset, - pts, - duration, + aOffset, + aPts, + aDuration, b, !!mFrame->key_frame, -1, @@ -331,9 +386,6 @@ FFmpegVideoDecoder::DoDecode(MediaRawData* aSample, RESULT_DETAIL("image allocation error")); } mCallback->Output(v); - if (aGotFrame) { - *aGotFrame = true; - } return NS_OK; } diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h index 786df0da1..49a55e8a6 100644 --- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h +++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h @@ -49,6 +49,7 @@ private: MediaResult DoDecode(MediaRawData* aSample) override; MediaResult DoDecode(MediaRawData* aSample, bool* aGotFrame); MediaResult DoDecode(MediaRawData* aSample, uint8_t* aData, int aSize, bool* aGotFrame); + MediaResult CreateImage(int64_t aOffset, int64_t aPts, int64_t aDuration); void ProcessDrain() override; void ProcessFlush() override; void OutputDelayedFrames(); -- cgit v1.2.3 From 68acfbb14b48a988e68e056a1d3376cef0f6fba6 Mon Sep 17 00:00:00 2001 From: trav90 Date: Fri, 28 Dec 2018 19:49:47 -0600 Subject: [FFmpeg] Don't define AV_CODEC_ID_VPx with libavcodec 58 --- dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'dom/media') diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp index d71a86b3c..f3101e44c 100644 --- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp @@ -174,12 +174,15 @@ FFmpegVideoDecoder::DoDecode(MediaRawData* aSample, bool* aGotFrame) uint8_t* inputData = const_cast(aSample->Data()); size_t inputSize = aSample->Size(); -#if LIBAVCODEC_VERSION_MAJOR >= 54 +#if LIBAVCODEC_VERSION_MAJOR >= 54 && LIBAVCODEC_VERSION_MAJOR < 58 if (inputSize && mCodecParser && (mCodecID == AV_CODEC_ID_VP8 -#if LIBAVCODEC_VERSION_MAJOR >= 55 +#if LIBAVCODEC_VERSION_MAJOR >= 55 && LIBAVCODEC_VERSION_MAJOR < 58 || mCodecID == AV_CODEC_ID_VP9 #endif - )) { + )) +#endif +#if LIBAVCODEC_VERSION_MAJOR >= 54 + { while (inputSize) { uint8_t* data = inputData; int size = inputSize; -- cgit v1.2.3 From 7a39cd52e5c39f95fb8351a1501e07c9c1aae947 Mon Sep 17 00:00:00 2001 From: trav90 Date: Mon, 31 Dec 2018 09:14:59 -0600 Subject: Avoid redefined macro warning in MediaFormatReader --- dom/media/MediaFormatReader.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'dom/media') diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 06e8b963b..773434710 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -32,6 +32,10 @@ using mozilla::layers::Image; using mozilla::layers::LayerManager; using mozilla::layers::LayersBackend; +// avoid redefined macro warning in unified builds +#undef LOG +#undef LOGV + static mozilla::LazyLogModule sFormatDecoderLog("MediaFormatReader"); mozilla::LazyLogModule gMediaDemuxerLog("MediaDemuxer"); -- cgit v1.2.3 From 7fcb7f54468aaf4e988e545ef1f6a8b9ab23b7dc Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Fri, 4 Jan 2019 16:18:03 +0100 Subject: Remove proprietary constructor on AudioContext. This is a B2G leftover, was proprietary, and is causing issues because `AudioContext` can now have a parameter that is a property bag, per spec (we do not do anything with the property bag now; the audio back-end will use automatic values for everything). This resolves #924. --- dom/media/test/crashtests/1228484.html | 13 ------------- dom/media/test/crashtests/crashtests.list | 2 -- dom/media/webaudio/AudioContext.cpp | 12 +----------- dom/media/webaudio/AudioContext.h | 6 ------ 4 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 dom/media/test/crashtests/1228484.html (limited to 'dom/media') diff --git a/dom/media/test/crashtests/1228484.html b/dom/media/test/crashtests/1228484.html deleted file mode 100644 index 2b2e9b0f9..000000000 --- a/dom/media/test/crashtests/1228484.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/dom/media/test/crashtests/crashtests.list b/dom/media/test/crashtests/crashtests.list index 496fe5ee5..e4f25ca8d 100644 --- a/dom/media/test/crashtests/crashtests.list +++ b/dom/media/test/crashtests/crashtests.list @@ -81,8 +81,6 @@ load 1157994.html load 1158427.html load 1185176.html load 1185192.html -load 1223670.html -load 1228484.html load 1304948.html load 1319486.html load 1291702.html diff --git a/dom/media/webaudio/AudioContext.cpp b/dom/media/webaudio/AudioContext.cpp index 85842c811..d58441309 100755 --- a/dom/media/webaudio/AudioContext.cpp +++ b/dom/media/webaudio/AudioContext.cpp @@ -178,16 +178,6 @@ AudioContext::WrapObject(JSContext* aCx, JS::Handle aGivenProto) /* static */ already_AddRefed AudioContext::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) -{ - return AudioContext::Constructor(aGlobal, - AudioChannelService::GetDefaultAudioChannel(), - aRv); -} - -/* static */ already_AddRefed -AudioContext::Constructor(const GlobalObject& aGlobal, - AudioChannel aChannel, - ErrorResult& aRv) { nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports()); if (!window) { @@ -195,7 +185,7 @@ AudioContext::Constructor(const GlobalObject& aGlobal, return nullptr; } - RefPtr object = new AudioContext(window, false, aChannel); + RefPtr object = new AudioContext(window, false, AudioChannelService::GetDefaultAudioChannel()); aRv = object->Init(); if (NS_WARN_IF(aRv.Failed())) { return nullptr; diff --git a/dom/media/webaudio/AudioContext.h b/dom/media/webaudio/AudioContext.h index 069efa986..599debef8 100644 --- a/dom/media/webaudio/AudioContext.h +++ b/dom/media/webaudio/AudioContext.h @@ -151,12 +151,6 @@ public: static already_AddRefed Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); - // Constructor for regular AudioContext. A default audio channel is needed. - static already_AddRefed - Constructor(const GlobalObject& aGlobal, - AudioChannel aChannel, - ErrorResult& aRv); - // Constructor for offline AudioContext static already_AddRefed Constructor(const GlobalObject& aGlobal, -- cgit v1.2.3