mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-21 07:47:32 +09:00
Remove DirectShow as it doesn't work properly.
This commit is contained in:
parent
c6ed045774
commit
5ec85f3abe
23 changed files with 1 additions and 3012 deletions
|
|
@ -34,11 +34,6 @@
|
||||||
#include "FlacDecoder.h"
|
#include "FlacDecoder.h"
|
||||||
#include "FlacDemuxer.h"
|
#include "FlacDemuxer.h"
|
||||||
|
|
||||||
#ifdef MOZ_DIRECTSHOW
|
|
||||||
#include "DirectShowDecoder.h"
|
|
||||||
#include "DirectShowReader.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "nsPluginHost.h"
|
#include "nsPluginHost.h"
|
||||||
#include "MediaPrefs.h"
|
#include "MediaPrefs.h"
|
||||||
|
|
||||||
|
|
@ -143,14 +138,6 @@ IsFlacSupportedType(const nsACString& aType,
|
||||||
return FlacDecoder::CanHandleMediaType(aType, aCodecs);
|
return FlacDecoder::CanHandleMediaType(aType, aCodecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MOZ_DIRECTSHOW
|
|
||||||
static bool
|
|
||||||
IsDirectShowSupportedType(const nsACString& aType)
|
|
||||||
{
|
|
||||||
return DirectShowDecoder::GetSupportedCodecs(aType, nullptr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static
|
static
|
||||||
CanPlayStatus
|
CanPlayStatus
|
||||||
CanHandleCodecsType(const MediaContentType& aType,
|
CanHandleCodecsType(const MediaContentType& aType,
|
||||||
|
|
@ -210,11 +197,6 @@ CanHandleCodecsType(const MediaContentType& aType,
|
||||||
if (IsFlacSupportedType(aType.GetMIMEType(), aType.GetCodecs())) {
|
if (IsFlacSupportedType(aType.GetMIMEType(), aType.GetCodecs())) {
|
||||||
return CANPLAY_YES;
|
return CANPLAY_YES;
|
||||||
}
|
}
|
||||||
#ifdef MOZ_DIRECTSHOW
|
|
||||||
if (IsDirectShowSupportedType(aType.GetMIMEType())) {
|
|
||||||
return CANPLAY_YES;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (!codecList) {
|
if (!codecList) {
|
||||||
return CANPLAY_MAYBE;
|
return CANPLAY_MAYBE;
|
||||||
}
|
}
|
||||||
|
|
@ -276,11 +258,6 @@ CanHandleMediaType(const MediaContentType& aType,
|
||||||
if (IsFlacSupportedType(aType.GetMIMEType())) {
|
if (IsFlacSupportedType(aType.GetMIMEType())) {
|
||||||
return CANPLAY_MAYBE;
|
return CANPLAY_MAYBE;
|
||||||
}
|
}
|
||||||
#ifdef MOZ_DIRECTSHOW
|
|
||||||
if (IsDirectShowSupportedType(aType.GetMIMEType())) {
|
|
||||||
return CANPLAY_MAYBE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return CANPLAY_NO;
|
return CANPLAY_NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -370,13 +347,6 @@ InstantiateDecoder(const nsACString& aType,
|
||||||
return decoder.forget();
|
return decoder.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MOZ_DIRECTSHOW
|
|
||||||
if (IsDirectShowSupportedType(aType)) {
|
|
||||||
decoder = new DirectShowDecoder(aOwner);
|
|
||||||
return decoder.forget();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -423,11 +393,6 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
|
||||||
decoderReader =
|
decoderReader =
|
||||||
new MediaFormatReader(aDecoder, new WebMDemuxer(aDecoder->GetResource()));
|
new MediaFormatReader(aDecoder, new WebMDemuxer(aDecoder->GetResource()));
|
||||||
}
|
}
|
||||||
#ifdef MOZ_DIRECTSHOW
|
|
||||||
else if (IsDirectShowSupportedType(aType)) {
|
|
||||||
decoderReader = new DirectShowReader(aDecoder);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return decoderReader;
|
return decoderReader;
|
||||||
}
|
}
|
||||||
|
|
@ -453,9 +418,6 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
|
||||||
IsAACSupportedType(aType) ||
|
IsAACSupportedType(aType) ||
|
||||||
IsWaveSupportedType(aType) ||
|
IsWaveSupportedType(aType) ||
|
||||||
IsFlacSupportedType(aType) ||
|
IsFlacSupportedType(aType) ||
|
||||||
#ifdef MOZ_DIRECTSHOW
|
|
||||||
IsDirectShowSupportedType(aType) ||
|
|
||||||
#endif
|
|
||||||
false;
|
false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,290 +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 <initguid.h>
|
|
||||||
#include <wmsdkidl.h>
|
|
||||||
|
|
||||||
#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<BasePin*>(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<IMediaSeeking*>(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
const ::VIDEOINFOHEADER*
|
|
||||||
AudioSinkFilter::GetVideoInfo() const
|
|
||||||
{
|
|
||||||
return mInputPin->GetVideoInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 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<IMediaSeeking> 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
|
|
||||||
|
|
@ -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(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();
|
|
||||||
const ::VIDEOINFOHEADER* GetVideoInfo() const;
|
|
||||||
|
|
||||||
// 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<AudioSinkInputPin> mInputPin;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // AudioSinkFilter_h_
|
|
||||||
|
|
@ -1,210 +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 <wmsdkidl.h>
|
|
||||||
|
|
||||||
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();
|
|
||||||
memset(&mVideoInfo, 0, sizeof(mVideoInfo));
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// 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->SetFormatType(&FORMAT_WaveFormatEx);
|
|
||||||
aOutMediaType->SetTemporalCompression(FALSE);
|
|
||||||
|
|
||||||
if (aPosition == 0) {
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
if (aPosition == 1) {
|
|
||||||
aOutMediaType->SetType(&MEDIATYPE_Video);
|
|
||||||
aOutMediaType->SetSubtype(&MEDIASUBTYPE_YUY2);
|
|
||||||
aOutMediaType->SetFormatType(&FORMAT_VideoInfo);
|
|
||||||
aOutMediaType->SetTemporalCompression(FALSE);
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
return S_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT
|
|
||||||
AudioSinkInputPin::CheckMediaType(const MediaType* aMediaType)
|
|
||||||
{
|
|
||||||
if (!aMediaType) {
|
|
||||||
return E_INVALIDARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
GUID majorType = *aMediaType->Type();
|
|
||||||
if (majorType == MEDIATYPE_Video &&
|
|
||||||
*aMediaType->Subtype() == MEDIASUBTYPE_YUY2 &&
|
|
||||||
*aMediaType->FormatType() == FORMAT_VideoInfo) {
|
|
||||||
if (aMediaType->cbFormat >= sizeof(VIDEOINFOHEADER)) {
|
|
||||||
memcpy(&mVideoInfo, aMediaType->pbFormat, sizeof(VIDEOINFOHEADER));
|
|
||||||
}
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<AudioSinkFilter*>(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<IMediaSeeking>
|
|
||||||
AudioSinkInputPin::GetConnectedPinSeeking()
|
|
||||||
{
|
|
||||||
RefPtr<IPin> peer = GetConnected();
|
|
||||||
if (!peer)
|
|
||||||
return nullptr;
|
|
||||||
RefPtr<IMediaSeeking> seeking;
|
|
||||||
peer->QueryInterface(static_cast<IMediaSeeking**>(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
|
|
||||||
|
|
@ -1,78 +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<IMediaSeeking> GetConnectedPinSeeking();
|
|
||||||
|
|
||||||
SampleSink* GetSampleSink();
|
|
||||||
const VIDEOINFOHEADER* GetVideoInfo() const { return &mVideoInfo; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
AudioSinkFilter* GetAudioSinkFilter();
|
|
||||||
|
|
||||||
// Sets the media time on the media sample, relative to the segment
|
|
||||||
// start time.
|
|
||||||
HRESULT SetAbsoluteMediaTime(IMediaSample* aSample);
|
|
||||||
|
|
||||||
nsAutoPtr<SampleSink> mSampleSink;
|
|
||||||
|
|
||||||
// Synchronized by the filter lock; BaseInputPin::mLock.
|
|
||||||
REFERENCE_TIME mSegmentStartTime;
|
|
||||||
VIDEOINFOHEADER mVideoInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // AudioSinkInputPin_h_
|
|
||||||
|
|
@ -1,78 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char const *const h264VideoCodecs[] = {
|
|
||||||
"avc1",
|
|
||||||
"h264",
|
|
||||||
nullptr
|
|
||||||
};
|
|
||||||
if ((aType.EqualsASCII("video/h264") ||
|
|
||||||
aType.EqualsASCII("video/avc")) &&
|
|
||||||
CanDecodeH264UsingDirectShow()) {
|
|
||||||
if (aCodecList) {
|
|
||||||
*aCodecList = h264VideoCodecs;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */
|
|
||||||
bool
|
|
||||||
DirectShowDecoder::IsEnabled()
|
|
||||||
{
|
|
||||||
return Preferences::GetBool("media.directshow.enabled") &&
|
|
||||||
(CanDecodeMP3UsingDirectShow() || CanDecodeH264UsingDirectShow());
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectShowDecoder::DirectShowDecoder(MediaDecoderOwner* aOwner)
|
|
||||||
: MediaDecoder(aOwner)
|
|
||||||
{
|
|
||||||
MOZ_COUNT_CTOR(DirectShowDecoder);
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectShowDecoder::~DirectShowDecoder()
|
|
||||||
{
|
|
||||||
MOZ_COUNT_DTOR(DirectShowDecoder);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
@ -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 the legacy DirectShow graph on systems without WMF.
|
|
||||||
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
|
|
||||||
|
|
@ -1,417 +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"
|
|
||||||
#include "mozilla/Preferences.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),
|
|
||||||
#ifdef DIRECTSHOW_REGISTER_GRAPH
|
|
||||||
mRotRegister(0),
|
|
||||||
#endif
|
|
||||||
mNumChannels(0),
|
|
||||||
mAudioRate(0),
|
|
||||||
mBytesPerSample(0),
|
|
||||||
mIsH264(aDecoder->GetResource()->GetContentType().EqualsLiteral("video/h264") ||
|
|
||||||
aDecoder->GetResource()->GetContentType().EqualsLiteral("video/avc"))
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
|
||||||
MOZ_COUNT_CTOR(DirectShowReader);
|
|
||||||
if (mIsH264) {
|
|
||||||
Preferences::SetBool("media.directshow.h264.active", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectShowReader::~DirectShowReader()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
|
||||||
MOZ_COUNT_DTOR(DirectShowReader);
|
|
||||||
if (mIsH264) {
|
|
||||||
Preferences::SetBool("media.directshow.h264.active", false);
|
|
||||||
}
|
|
||||||
#ifdef DIRECTSHOW_REGISTER_GRAPH
|
|
||||||
if (mRotRegister) {
|
|
||||||
RemoveGraphFromRunningObjectTable(mRotRegister);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
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<void**>(static_cast<IGraphBuilder**>(getter_AddRefs(mGraph))));
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(hr) && mGraph, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
#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<IMediaControl**>(getter_AddRefs(mControl)));
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(hr) && mControl, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
hr = mGraph->QueryInterface(static_cast<IMediaSeeking**>(getter_AddRefs(mMediaSeeking)));
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(hr) && mMediaSeeking, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
if (mIsH264) {
|
|
||||||
return ReadH264Metadata(aInfo, aTags);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(), 0);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
hr = mGraph->AddFilter(mSourceFilter, L"MozillaDirectShowSource");
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
// The MPEG demuxer.
|
|
||||||
RefPtr<IBaseFilter> demuxer;
|
|
||||||
hr = CreateAndAddFilter(mGraph,
|
|
||||||
CLSID_MPEG1Splitter,
|
|
||||||
L"MPEG1Splitter",
|
|
||||||
getter_AddRefs(demuxer));
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
// Platform MP3 decoder.
|
|
||||||
RefPtr<IBaseFilter> 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);
|
|
||||||
|
|
||||||
LOG("Successfully initialized DirectShow MP3 decoder.");
|
|
||||||
LOG("Channels=%u Hz=%u duration=%lld bytesPerSample=%d",
|
|
||||||
mInfo.mAudio.mChannels,
|
|
||||||
mInfo.mAudio.mRate,
|
|
||||||
0LL,
|
|
||||||
mBytesPerSample);
|
|
||||||
|
|
||||||
*aInfo = mInfo;
|
|
||||||
// Note: The SourceFilter strips ID3v2 tags out of the stream.
|
|
||||||
*aTags = nullptr;
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
DirectShowReader::ReadH264Metadata(MediaInfo* aInfo, MetadataTags** aTags)
|
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
mSourceFilter = new SourceFilter(MEDIATYPE_Stream, MEDIASUBTYPE_H264);
|
|
||||||
NS_ENSURE_TRUE(mSourceFilter, NS_ERROR_OUT_OF_MEMORY);
|
|
||||||
nsresult rv = mSourceFilter->Init(mDecoder->GetResource(), 0);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
hr = mGraph->AddFilter(mSourceFilter, L"Mozilla H.264 source");
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
RefPtr<IBaseFilter> decoder;
|
|
||||||
hr = AddH264DecoderFilter(mGraph, getter_AddRefs(decoder));
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
|
||||||
mAudioSinkFilter = new AudioSinkFilter(L"Mozilla H.264 video sink", &hr);
|
|
||||||
NS_ENSURE_TRUE(mAudioSinkFilter && SUCCEEDED(hr), NS_ERROR_FAILURE);
|
|
||||||
hr = mGraph->AddFilter(mAudioSinkFilter, L"Mozilla H.264 video sink");
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
|
||||||
hr = ConnectFilters(mGraph, mSourceFilter, decoder);
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
|
||||||
hr = ConnectFilters(mGraph, decoder, mAudioSinkFilter);
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
const VIDEOINFOHEADER* vih = mAudioSinkFilter->GetVideoInfo();
|
|
||||||
int32_t width = abs(vih->bmiHeader.biWidth);
|
|
||||||
int32_t height = abs(vih->bmiHeader.biHeight);
|
|
||||||
NS_ENSURE_TRUE(width > 0 && height > 0, NS_ERROR_FAILURE);
|
|
||||||
mInfo.mVideo = VideoInfo(width, height);
|
|
||||||
mInfo.mVideo.mMimeType = "video/h264";
|
|
||||||
hr = mControl->Run();
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
|
||||||
*aInfo = mInfo;
|
|
||||||
*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<IMediaEventSink> eventSink;
|
|
||||||
HRESULT hr = mGraph->QueryInterface(static_cast<IMediaEventSink**>(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<int16_t *>(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<IMediaSample> 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<uint8_t *>(data),
|
|
||||||
mBytesPerSample,
|
|
||||||
numSamples,
|
|
||||||
mNumChannels));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
DirectShowReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
|
||||||
int64_t aTimeThreshold)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(OnTaskQueue());
|
|
||||||
if (!mIsH264) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
RefPtr<IMediaSample> sample;
|
|
||||||
HRESULT hr = mAudioSinkFilter->GetSampleSink()->Extract(sample);
|
|
||||||
if (FAILED(hr) || hr == S_FALSE) {
|
|
||||||
return Finish(hr);
|
|
||||||
}
|
|
||||||
REFERENCE_TIME start = 0, end = 0;
|
|
||||||
sample->GetMediaTime(&start, &end);
|
|
||||||
BYTE* data = nullptr;
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(sample->GetPointer(&data)), Finish(E_FAIL));
|
|
||||||
LONG length = sample->GetActualDataLength();
|
|
||||||
int32_t width = mInfo.mVideo.mImage.width;
|
|
||||||
int32_t height = mInfo.mVideo.mImage.height;
|
|
||||||
int32_t stride = width * 2;
|
|
||||||
NS_ENSURE_TRUE(length >= stride * height, Finish(E_FAIL));
|
|
||||||
nsTArray<uint8_t> y;
|
|
||||||
nsTArray<uint8_t> u;
|
|
||||||
nsTArray<uint8_t> v;
|
|
||||||
y.SetLength(width * height);
|
|
||||||
u.SetLength((width / 2) * (height / 2));
|
|
||||||
v.SetLength((width / 2) * (height / 2));
|
|
||||||
for (int32_t row = 0; row < height; ++row) {
|
|
||||||
const uint8_t* src = data + row * stride;
|
|
||||||
for (int32_t x = 0; x < width; x += 2) {
|
|
||||||
int32_t p = row * width + x;
|
|
||||||
y[p] = src[0]; y[p + 1] = src[2];
|
|
||||||
if (!(row & 1)) {
|
|
||||||
u[(row / 2) * (width / 2) + x / 2] = src[1];
|
|
||||||
v[(row / 2) * (width / 2) + x / 2] = src[3];
|
|
||||||
}
|
|
||||||
src += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VideoData::YCbCrBuffer buffer;
|
|
||||||
buffer.mPlanes[0] = { y.Elements(), uint32_t(width), uint32_t(height), uint32_t(width), 0, 1 };
|
|
||||||
buffer.mPlanes[1] = { u.Elements(), uint32_t(width / 2), uint32_t(height / 2), uint32_t(width / 2), 0, 1 };
|
|
||||||
buffer.mPlanes[2] = { v.Elements(), uint32_t(width / 2), uint32_t(height / 2), uint32_t(width / 2), 0, 1 };
|
|
||||||
RefPtr<VideoData> video = VideoData::CreateAndCopyData(
|
|
||||||
mInfo.mVideo, mDecoder->GetImageContainer(), 0, RefTimeToUsecs(start),
|
|
||||||
RefTimeToUsecs(end - start), buffer, true, -1,
|
|
||||||
gfx::IntRect(0, 0, width, height));
|
|
||||||
NS_ENSURE_TRUE(video, Finish(E_OUTOFMEMORY));
|
|
||||||
VideoQueue().Push(video);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<MediaDecoderReader::SeekPromise>
|
|
||||||
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
|
|
||||||
|
|
@ -1,106 +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"
|
|
||||||
|
|
||||||
// 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 legacy media 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<SeekPromise>
|
|
||||||
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);
|
|
||||||
nsresult ReadH264Metadata(MediaInfo* aInfo, MetadataTags** aTags);
|
|
||||||
|
|
||||||
// DirectShow filter graph, and associated playback and seeking
|
|
||||||
// control interfaces.
|
|
||||||
RefPtr<IGraphBuilder> mGraph;
|
|
||||||
RefPtr<IMediaControl> mControl;
|
|
||||||
RefPtr<IMediaSeeking> mMediaSeeking;
|
|
||||||
|
|
||||||
// Wraps the MediaResource, and feeds undecoded data into the filter graph.
|
|
||||||
RefPtr<SourceFilter> 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<AudioSinkFilter> mAudioSinkFilter;
|
|
||||||
bool mIsH264;
|
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
@ -1,457 +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 "DirectShowReader.h"
|
|
||||||
#include "dmodshow.h"
|
|
||||||
#include "wmcodecdsp.h"
|
|
||||||
#include "dmoreg.h"
|
|
||||||
#include "mozilla/ArrayUtils.h"
|
|
||||||
#include "mozilla/RefPtr.h"
|
|
||||||
#include "nsPrintfCString.h"
|
|
||||||
#include <wchar.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 <uuids.h>
|
|
||||||
};
|
|
||||||
|
|
||||||
#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<IRunningObjectTable> runningObjectTable;
|
|
||||||
if (SUCCEEDED(GetRunningObjectTable(0, getter_AddRefs(runningObjectTable)))) {
|
|
||||||
runningObjectTable->Revoke(aRotRegister);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT
|
|
||||||
AddGraphToRunningObjectTable(IUnknown *aUnkGraph, DWORD *aOutRotRegister)
|
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
RefPtr<IMoniker> moniker;
|
|
||||||
RefPtr<IRunningObjectTable> 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<IBaseFilter> 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<IBaseFilter> 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<IDMOWrapperFilter> 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<IBaseFilter> 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<IBaseFilter> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
CanDecodeH264UsingDirectShow()
|
|
||||||
{
|
|
||||||
RefPtr<ICreateDevEnum> devEnum;
|
|
||||||
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr,
|
|
||||||
CLSCTX_INPROC_SERVER,
|
|
||||||
IID_ICreateDevEnum, getter_AddRefs(devEnum));
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<IEnumMoniker> filters;
|
|
||||||
hr = devEnum->CreateClassEnumerator(CLSID_LegacyAmFilterCategory,
|
|
||||||
getter_AddRefs(filters), 0);
|
|
||||||
if (hr != S_OK) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<IMoniker> moniker;
|
|
||||||
ULONG fetched = 0;
|
|
||||||
while (filters->Next(1, getter_AddRefs(moniker), &fetched) == S_OK) {
|
|
||||||
RefPtr<IPropertyBag> bag;
|
|
||||||
if (SUCCEEDED(moniker->BindToStorage(nullptr, nullptr,
|
|
||||||
IID_IPropertyBag,
|
|
||||||
getter_AddRefs(bag)))) {
|
|
||||||
VARIANT value;
|
|
||||||
VariantInit(&value);
|
|
||||||
if (SUCCEEDED(bag->Read(L"FriendlyName", &value, nullptr)) &&
|
|
||||||
value.vt == VT_BSTR && value.bstrVal &&
|
|
||||||
(wcsstr(value.bstrVal, L"H264") ||
|
|
||||||
wcsstr(value.bstrVal, L"H.264") ||
|
|
||||||
wcsstr(value.bstrVal, L"AVC"))) {
|
|
||||||
VariantClear(&value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
VariantClear(&value);
|
|
||||||
}
|
|
||||||
moniker = nullptr;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT
|
|
||||||
AddH264DecoderFilter(IGraphBuilder* aGraph, IBaseFilter** aOutFilter)
|
|
||||||
{
|
|
||||||
NS_ENSURE_TRUE(aGraph && aOutFilter, E_POINTER);
|
|
||||||
*aOutFilter = nullptr;
|
|
||||||
RefPtr<ICreateDevEnum> devEnum;
|
|
||||||
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr,
|
|
||||||
CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
|
|
||||||
getter_AddRefs(devEnum));
|
|
||||||
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
|
|
||||||
RefPtr<IEnumMoniker> filters;
|
|
||||||
hr = devEnum->CreateClassEnumerator(CLSID_LegacyAmFilterCategory,
|
|
||||||
getter_AddRefs(filters), 0);
|
|
||||||
NS_ENSURE_TRUE(hr == S_OK, VFW_E_NOT_FOUND);
|
|
||||||
RefPtr<IMoniker> moniker;
|
|
||||||
ULONG fetched = 0;
|
|
||||||
while (filters->Next(1, getter_AddRefs(moniker), &fetched) == S_OK) {
|
|
||||||
RefPtr<IPropertyBag> bag;
|
|
||||||
VARIANT value;
|
|
||||||
VariantInit(&value);
|
|
||||||
bool h264 = false;
|
|
||||||
if (SUCCEEDED(moniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag,
|
|
||||||
getter_AddRefs(bag))) &&
|
|
||||||
SUCCEEDED(bag->Read(L"FriendlyName", &value, nullptr)) &&
|
|
||||||
value.vt == VT_BSTR && value.bstrVal) {
|
|
||||||
h264 = wcsstr(value.bstrVal, L"H264") ||
|
|
||||||
wcsstr(value.bstrVal, L"H.264") ||
|
|
||||||
wcsstr(value.bstrVal, L"AVC");
|
|
||||||
}
|
|
||||||
VariantClear(&value);
|
|
||||||
if (h264 && SUCCEEDED(moniker->BindToObject(nullptr, nullptr, IID_IBaseFilter,
|
|
||||||
reinterpret_cast<void**>(aOutFilter)))) {
|
|
||||||
hr = aGraph->AddFilter(*aOutFilter, L"DirectShow H.264 decoder");
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
(*aOutFilter)->Release();
|
|
||||||
*aOutFilter = nullptr;
|
|
||||||
}
|
|
||||||
moniker = nullptr;
|
|
||||||
}
|
|
||||||
return VFW_E_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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<IPin> 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<IPin>
|
|
||||||
GetUnconnectedPin(IBaseFilter* aFilter, PIN_DIRECTION aPinDir)
|
|
||||||
{
|
|
||||||
RefPtr<IEnumPins> 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<IPin> 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<IPin> output = GetUnconnectedPin(aOutputFilter, PINDIR_OUTPUT);
|
|
||||||
NS_ENSURE_TRUE(output, E_FAIL);
|
|
||||||
|
|
||||||
RefPtr<IPin> 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
|
|
||||||
|
|
@ -1,134 +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 <stdint.h>
|
|
||||||
#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();
|
|
||||||
|
|
||||||
// Returns true when a registered DirectShow filter advertises H.264/AVC
|
|
||||||
// decoding. This works on Windows 2000 because it uses the legacy filter
|
|
||||||
// enumerator instead of Media Foundation.
|
|
||||||
bool
|
|
||||||
CanDecodeH264UsingDirectShow();
|
|
||||||
|
|
||||||
HRESULT
|
|
||||||
AddH264DecoderFilter(IGraphBuilder* aGraph, IBaseFilter** aOutFilter);
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -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<IMediaSample>& 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
|
|
||||||
|
|
||||||
|
|
@ -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<IMediaSample>& 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<IMediaSample> mSample;
|
|
||||||
|
|
||||||
// Format of the audio stream we're receiving.
|
|
||||||
WAVEFORMATEX mAudioFormat;
|
|
||||||
|
|
||||||
bool mIsFlushing;
|
|
||||||
bool mAtEOS;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mozilla
|
|
||||||
|
|
||||||
#endif // SampleSink_h_
|
|
||||||
|
|
@ -1,682 +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 "mozilla/Logging.h"
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
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<IMediaSample> 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<int64_t>(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<BaseFilter*>(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<IAsyncReader*>(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<IMemAllocator> 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<ReadRequest> request(reinterpret_cast<ReadRequest*>(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<char*>(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<BasePin*>(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
|
|
||||||
|
|
@ -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<OutputPin> 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
|
|
||||||
|
|
@ -1,42 +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 += [
|
|
||||||
'/dom/media',
|
|
||||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows',
|
|
||||||
]
|
|
||||||
|
|
@ -42,12 +42,6 @@ DIRS += [
|
||||||
'standalone',
|
'standalone',
|
||||||
]
|
]
|
||||||
|
|
||||||
if CONFIG['MOZ_DIRECTSHOW']:
|
|
||||||
DIRS += ['directshow']
|
|
||||||
LOCAL_INCLUDES += [
|
|
||||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows',
|
|
||||||
]
|
|
||||||
|
|
||||||
if CONFIG['MOZ_FMP4']:
|
if CONFIG['MOZ_FMP4']:
|
||||||
DIRS += ['fmp4']
|
DIRS += ['fmp4']
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -392,11 +392,6 @@ pref("media.wave.play-stand-alone", false);
|
||||||
pref("media.hardware-video-decoding.enabled", true);
|
pref("media.hardware-video-decoding.enabled", true);
|
||||||
pref("media.hardware-video-decoding.force-enabled", false);
|
pref("media.hardware-video-decoding.force-enabled", false);
|
||||||
|
|
||||||
#ifdef MOZ_DIRECTSHOW
|
|
||||||
pref("media.directshow.enabled", true);
|
|
||||||
pref("media.directshow.h264.active", false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef MOZ_FMP4
|
#ifdef MOZ_FMP4
|
||||||
pref("media.mp4.enabled", true);
|
pref("media.mp4.enabled", true);
|
||||||
// Specifies whether the PDMFactory can create a test decoder that
|
// Specifies whether the PDMFactory can create a test decoder that
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,6 @@
|
||||||
|
|
||||||
include('build/moz.configure/init.configure')
|
include('build/moz.configure/init.configure')
|
||||||
|
|
||||||
# DirectShow is the legacy Windows media backend. Keep this independent of
|
|
||||||
# the old configure scripts so the backend is actually compiled on Windows.
|
|
||||||
@depends(target)
|
|
||||||
def directshow_enabled(target):
|
|
||||||
if target.kernel == 'WINNT':
|
|
||||||
return True
|
|
||||||
|
|
||||||
set_config('MOZ_DIRECTSHOW', directshow_enabled)
|
|
||||||
set_define('MOZ_DIRECTSHOW', directshow_enabled)
|
|
||||||
|
|
||||||
# Note:
|
# Note:
|
||||||
# - Gecko-specific options and rules should go in toolkit/moz.configure.
|
# - Gecko-specific options and rules should go in toolkit/moz.configure.
|
||||||
# - Firefox-specific options and rules should go in browser/moz.configure.
|
# - Firefox-specific options and rules should go in browser/moz.configure.
|
||||||
|
|
|
||||||
|
|
@ -337,16 +337,6 @@ var snapshotFormatters = {
|
||||||
addRowFromKey("features", "webgl2Extensions");
|
addRowFromKey("features", "webgl2Extensions");
|
||||||
addRowFromKey("features", "supportsHardwareH264", "hardwareH264");
|
addRowFromKey("features", "supportsHardwareH264", "hardwareH264");
|
||||||
addRowFromKey("features", "currentAudioBackend", "audioBackend");
|
addRowFromKey("features", "currentAudioBackend", "audioBackend");
|
||||||
if ("directShowEnabled" in data) {
|
|
||||||
addRow("features", "directShowEnabled",
|
|
||||||
strings.GetStringFromName(data.directShowEnabled ? "yes" : "no"));
|
|
||||||
delete data.directShowEnabled;
|
|
||||||
}
|
|
||||||
if ("directShowH264Active" in data) {
|
|
||||||
addRow("features", "directShowH264Active",
|
|
||||||
strings.GetStringFromName(data.directShowH264Active ? "yes" : "no"));
|
|
||||||
delete data.directShowH264Active;
|
|
||||||
}
|
|
||||||
addRowFromKey("features", "direct2DEnabled", "#Direct2D");
|
addRowFromKey("features", "direct2DEnabled", "#Direct2D");
|
||||||
|
|
||||||
if ("directWriteEnabled" in data) {
|
if ("directWriteEnabled" in data) {
|
||||||
|
|
|
||||||
|
|
@ -173,9 +173,6 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
||||||
'crypt32',
|
'crypt32',
|
||||||
'shell32',
|
'shell32',
|
||||||
'ole32',
|
'ole32',
|
||||||
'strmiids',
|
|
||||||
'dmoguids',
|
|
||||||
'msdmo',
|
|
||||||
'version',
|
'version',
|
||||||
'winspool',
|
'winspool',
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,6 @@ clearTypeParameters = ClearType Parameters
|
||||||
compositing = Compositing
|
compositing = Compositing
|
||||||
hardwareH264 = Hardware H264 Decoding
|
hardwareH264 = Hardware H264 Decoding
|
||||||
audioBackend = Audio Backend
|
audioBackend = Audio Backend
|
||||||
directShowEnabled = DirectShow H264 Backend Enabled
|
|
||||||
directShowH264Active = DirectShow H264 Decoding Active
|
|
||||||
mainThreadNoOMTC = main thread, no OMTC
|
mainThreadNoOMTC = main thread, no OMTC
|
||||||
yes = Yes
|
yes = Yes
|
||||||
no = No
|
no = No
|
||||||
|
|
|
||||||
|
|
@ -358,8 +358,6 @@ var dataProviders = {
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
data.currentAudioBackend = winUtils.currentAudioBackend;
|
data.currentAudioBackend = winUtils.currentAudioBackend;
|
||||||
data.directShowEnabled = Services.prefs.getBoolPref("media.directshow.enabled", false);
|
|
||||||
data.directShowH264Active = Services.prefs.getBoolPref("media.directshow.h264.active", false);
|
|
||||||
|
|
||||||
if (!data.numAcceleratedWindows && gfxInfo) {
|
if (!data.numAcceleratedWindows && gfxInfo) {
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
|
|
@ -553,3 +551,4 @@ var dataProviders = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue