mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-11 04:15:43 +00:00
Bug 1370192: P2. Remove DirectShow's reader and relative. r=cpearce
MozReview-Commit-ID: ESYagjKpS5E --HG-- extra : rebase_source : 6a267996f09deb11c8929cdb4f00f83a4bf02163
This commit is contained in:
parent
7310f412e5
commit
484055c07b
@ -1,285 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SampleSink.h"
|
||||
#include "AudioSinkFilter.h"
|
||||
#include "AudioSinkInputPin.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
|
||||
#include <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();
|
||||
}
|
||||
|
||||
|
||||
// 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,95 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(AudioSinkFilter_h_)
|
||||
#define AudioSinkFilter_h_
|
||||
|
||||
#include "BaseFilter.h"
|
||||
#include "DirectShowUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AudioSinkInputPin;
|
||||
class SampleSink;
|
||||
|
||||
// Filter that acts as the end of the graph. Audio samples input into
|
||||
// this filter block the calling thread, and the calling thread is
|
||||
// unblocked when the decode thread extracts the sample. The samples
|
||||
// input into this filter are stored in the SampleSink, where the blocking
|
||||
// is implemented. The input pin owns the SampleSink.
|
||||
class AudioSinkFilter: public mozilla::media::BaseFilter,
|
||||
public IMediaSeeking
|
||||
{
|
||||
|
||||
public:
|
||||
AudioSinkFilter(const wchar_t* aObjectName, HRESULT* aOutResult);
|
||||
virtual ~AudioSinkFilter();
|
||||
|
||||
// Gets the input pin's sample sink.
|
||||
SampleSink* GetSampleSink();
|
||||
|
||||
// IUnknown implementation.
|
||||
STDMETHODIMP QueryInterface(REFIID aIId, void **aInterface);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// CBaseFilter methods
|
||||
int GetPinCount ();
|
||||
mozilla::media::BasePin* GetPin ( IN int Index);
|
||||
STDMETHODIMP Pause ();
|
||||
STDMETHODIMP Stop ();
|
||||
STDMETHODIMP GetClassID ( OUT CLSID * pCLSID);
|
||||
STDMETHODIMP Run(REFERENCE_TIME tStart);
|
||||
// IMediaSeeking Methods...
|
||||
|
||||
// We defer to SourceFilter, but we must expose the interface on
|
||||
// the output pins. Seeking commands come upstream from the renderers,
|
||||
// but they must be actioned at the source filters.
|
||||
STDMETHODIMP GetCapabilities(DWORD* aCapabilities);
|
||||
STDMETHODIMP CheckCapabilities(DWORD* aCapabilities);
|
||||
STDMETHODIMP IsFormatSupported(const GUID* aFormat);
|
||||
STDMETHODIMP QueryPreferredFormat(GUID* aFormat);
|
||||
STDMETHODIMP GetTimeFormat(GUID* aFormat);
|
||||
STDMETHODIMP IsUsingTimeFormat(const GUID* aFormat);
|
||||
STDMETHODIMP SetTimeFormat(const GUID* aFormat);
|
||||
STDMETHODIMP GetDuration(LONGLONG* pDuration);
|
||||
STDMETHODIMP GetStopPosition(LONGLONG* pStop);
|
||||
STDMETHODIMP GetCurrentPosition(LONGLONG* aCurrent);
|
||||
STDMETHODIMP ConvertTimeFormat(LONGLONG* aTarget,
|
||||
const GUID* aTargetFormat,
|
||||
LONGLONG aSource,
|
||||
const GUID* aSourceFormat);
|
||||
STDMETHODIMP SetPositions(LONGLONG* aCurrent,
|
||||
DWORD aCurrentFlags,
|
||||
LONGLONG* aStop,
|
||||
DWORD aStopFlags);
|
||||
STDMETHODIMP GetPositions(LONGLONG* aCurrent,
|
||||
LONGLONG* aStop);
|
||||
STDMETHODIMP GetAvailable(LONGLONG* aEarliest,
|
||||
LONGLONG* aLatest);
|
||||
STDMETHODIMP SetRate(double aRate);
|
||||
STDMETHODIMP GetRate(double* aRate);
|
||||
STDMETHODIMP GetPreroll(LONGLONG* aPreroll);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// class factory calls this
|
||||
static IUnknown * CreateInstance (IN LPUNKNOWN punk, OUT HRESULT * phr);
|
||||
|
||||
private:
|
||||
CriticalSection mFilterCritSec;
|
||||
|
||||
// Note: The input pin defers its refcounting to the sink filter, so when
|
||||
// the input pin is addrefed, what actually happens is the sink filter is
|
||||
// addrefed.
|
||||
nsAutoPtr<AudioSinkInputPin> mInputPin;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // AudioSinkFilter_h_
|
@ -1,195 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AudioSinkInputPin.h"
|
||||
#include "AudioSinkFilter.h"
|
||||
#include "SampleSink.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
#include <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();
|
||||
}
|
||||
|
||||
AudioSinkInputPin::~AudioSinkInputPin()
|
||||
{
|
||||
MOZ_COUNT_DTOR(AudioSinkInputPin);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
AudioSinkInputPin::GetMediaType(int aPosition, MediaType* aOutMediaType)
|
||||
{
|
||||
NS_ENSURE_TRUE(aPosition >= 0, E_INVALIDARG);
|
||||
NS_ENSURE_TRUE(aOutMediaType, E_POINTER);
|
||||
|
||||
if (aPosition > 0) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
// Note: We set output as PCM, as IEEE_FLOAT only works when using the
|
||||
// MP3 decoder as an MFT, and we can't do that while using DirectShow.
|
||||
aOutMediaType->SetType(&MEDIATYPE_Audio);
|
||||
aOutMediaType->SetSubtype(&MEDIASUBTYPE_PCM);
|
||||
aOutMediaType->SetType(&FORMAT_WaveFormatEx);
|
||||
aOutMediaType->SetTemporalCompression(FALSE);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
AudioSinkInputPin::CheckMediaType(const MediaType* aMediaType)
|
||||
{
|
||||
if (!aMediaType) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
GUID majorType = *aMediaType->Type();
|
||||
if (majorType != MEDIATYPE_Audio && majorType != WMMEDIATYPE_Audio) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (*aMediaType->Subtype() != MEDIASUBTYPE_PCM) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (*aMediaType->FormatType() != FORMAT_WaveFormatEx) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
// We accept the media type, stash its layout format!
|
||||
WAVEFORMATEX* wfx = (WAVEFORMATEX*)(aMediaType->pbFormat);
|
||||
GetSampleSink()->SetAudioFormat(wfx);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
AudioSinkFilter*
|
||||
AudioSinkInputPin::GetAudioSinkFilter()
|
||||
{
|
||||
return reinterpret_cast<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,76 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(AudioSinkInputPin_h_)
|
||||
#define AudioSinkInputPin_h_
|
||||
|
||||
#include "BaseInputPin.h"
|
||||
#include "DirectShowUtils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace media {
|
||||
class MediaType;
|
||||
}
|
||||
|
||||
class AudioSinkFilter;
|
||||
class SampleSink;
|
||||
|
||||
|
||||
// Input pin for capturing audio output of a DirectShow filter graph.
|
||||
// This is the input pin for the AudioSinkFilter.
|
||||
class AudioSinkInputPin: public mozilla::media::BaseInputPin
|
||||
{
|
||||
public:
|
||||
AudioSinkInputPin(wchar_t* aObjectName,
|
||||
AudioSinkFilter* aFilter,
|
||||
mozilla::CriticalSection* aLock,
|
||||
HRESULT* aOutResult);
|
||||
virtual ~AudioSinkInputPin();
|
||||
|
||||
HRESULT GetMediaType (IN int iPos, OUT mozilla::media::MediaType * pmt);
|
||||
HRESULT CheckMediaType (IN const mozilla::media::MediaType * pmt);
|
||||
STDMETHODIMP Receive (IN IMediaSample *);
|
||||
STDMETHODIMP BeginFlush() override;
|
||||
STDMETHODIMP EndFlush() override;
|
||||
|
||||
// Called when we start decoding a new segment, that happens directly after
|
||||
// a seek. This captures the segment's start time. Samples decoded by the
|
||||
// MP3 decoder have their timestamps offset from the segment start time.
|
||||
// Storing the segment start time enables us to set each sample's MediaTime
|
||||
// as an offset in the stream relative to the start of the stream, rather
|
||||
// than the start of the segment, i.e. its absolute time in the stream.
|
||||
STDMETHODIMP NewSegment(REFERENCE_TIME tStart,
|
||||
REFERENCE_TIME tStop,
|
||||
double dRate) override;
|
||||
|
||||
STDMETHODIMP EndOfStream() override;
|
||||
|
||||
// Returns the IMediaSeeking interface of the connected output pin.
|
||||
// We forward seeking requests upstream from the sink to the source
|
||||
// filters.
|
||||
already_AddRefed<IMediaSeeking> GetConnectedPinSeeking();
|
||||
|
||||
SampleSink* GetSampleSink();
|
||||
|
||||
private:
|
||||
AudioSinkFilter* GetAudioSinkFilter();
|
||||
|
||||
// Sets the media time on the media sample, relative to the segment
|
||||
// start time.
|
||||
HRESULT SetAbsoluteMediaTime(IMediaSample* aSample);
|
||||
|
||||
nsAutoPtr<SampleSink> mSampleSink;
|
||||
|
||||
// Synchronized by the filter lock; BaseInputPin::mLock.
|
||||
REFERENCE_TIME mSegmentStartTime;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // AudioSinkInputPin_h_
|
@ -1,58 +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 "MediaContainerType.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 MediaContainerType& aType,
|
||||
MediaCodecs* aOutCodecs)
|
||||
{
|
||||
if (!IsEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aType.Type() == MEDIAMIMETYPE("audio/mpeg")
|
||||
|| aType.Type() == MEDIAMIMETYPE("audio/mp3")) {
|
||||
if (aOutCodecs) {
|
||||
*aOutCodecs = MediaCodecs("mp3");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
DirectShowDecoder::IsEnabled()
|
||||
{
|
||||
return CanDecodeMP3UsingDirectShow() &&
|
||||
Preferences::GetBool("media.directshow.enabled");
|
||||
}
|
||||
|
||||
DirectShowDecoder::DirectShowDecoder(MediaDecoderOwner* aOwner)
|
||||
: MediaDecoder(aOwner)
|
||||
{
|
||||
}
|
||||
|
||||
DirectShowDecoder::~DirectShowDecoder() = default;
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -1,48 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(DirectShowDecoder_h_)
|
||||
#define DirectShowDecoder_h_
|
||||
|
||||
#include "MediaDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaCodecs;
|
||||
class MediaContainerType;
|
||||
|
||||
// Decoder that uses DirectShow to playback MP3 files only.
|
||||
class DirectShowDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
|
||||
explicit DirectShowDecoder(MediaDecoderOwner* aOwner);
|
||||
virtual ~DirectShowDecoder();
|
||||
|
||||
MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
|
||||
if (!IsEnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new DirectShowDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaDecoderStateMachine* CreateStateMachine() override;
|
||||
|
||||
// Returns true if aType is a MIME type that we render with the
|
||||
// DirectShow backend. If aCodecList is non null,
|
||||
// it is filled with a (static const) null-terminated list of strings
|
||||
// denoting the codecs we'll playback. Note that playback is strictly
|
||||
// limited to MP3 only.
|
||||
static bool GetSupportedCodecs(const MediaContainerType& aType,
|
||||
MediaCodecs* aOutCodecs);
|
||||
|
||||
// Returns true if the DirectShow backend is preffed on.
|
||||
static bool IsEnabled();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -1,360 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DirectShowReader.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "DirectShowUtils.h"
|
||||
#include "AudioSinkFilter.h"
|
||||
#include "SourceFilter.h"
|
||||
#include "SampleSink.h"
|
||||
#include "VideoUtils.h"
|
||||
|
||||
using namespace mozilla::media;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Windows XP's MP3 decoder filter. This is available on XP only, on Vista
|
||||
// and later we can use the DMO Wrapper filter and MP3 decoder DMO.
|
||||
const GUID DirectShowReader::CLSID_MPEG_LAYER_3_DECODER_FILTER =
|
||||
{ 0x38BE3000, 0xDBF4, 0x11D0, {0x86, 0x0E, 0x00, 0xA0, 0x24, 0xCF, 0xEF, 0x6D} };
|
||||
|
||||
|
||||
static LazyLogModule gDirectShowLog("DirectShowDecoder");
|
||||
#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
|
||||
DirectShowReader::DirectShowReader(AbstractMediaDecoder* aDecoder)
|
||||
: MediaDecoderReader(aDecoder),
|
||||
mMP3FrameParser(aDecoder->GetResource()->GetLength()),
|
||||
#ifdef DIRECTSHOW_REGISTER_GRAPH
|
||||
mRotRegister(0),
|
||||
#endif
|
||||
mNumChannels(0),
|
||||
mAudioRate(0),
|
||||
mBytesPerSample(0)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
MOZ_COUNT_CTOR(DirectShowReader);
|
||||
}
|
||||
|
||||
DirectShowReader::~DirectShowReader()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
MOZ_COUNT_DTOR(DirectShowReader);
|
||||
#ifdef DIRECTSHOW_REGISTER_GRAPH
|
||||
if (mRotRegister) {
|
||||
RemoveGraphFromRunningObjectTable(mRotRegister);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Try to parse the MP3 stream to make sure this is indeed an MP3, get the
|
||||
// estimated duration of the stream, and find the offset of the actual MP3
|
||||
// frames in the stream, as DirectShow doesn't like large ID3 sections.
|
||||
static nsresult
|
||||
ParseMP3Headers(MP3FrameParser *aParser, MediaResource *aResource)
|
||||
{
|
||||
const uint32_t MAX_READ_SIZE = 4096;
|
||||
|
||||
uint64_t offset = 0;
|
||||
while (aParser->NeedsData() && !aParser->ParsedHeaders()) {
|
||||
uint32_t bytesRead;
|
||||
char buffer[MAX_READ_SIZE];
|
||||
nsresult rv = aResource->ReadAt(offset, buffer,
|
||||
MAX_READ_SIZE, &bytesRead);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!bytesRead) {
|
||||
// End of stream.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
aParser->Parse(reinterpret_cast<uint8_t*>(buffer), bytesRead, offset);
|
||||
offset += bytesRead;
|
||||
}
|
||||
|
||||
return aParser->IsMP3() ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DirectShowReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
HRESULT hr;
|
||||
nsresult rv;
|
||||
|
||||
// Create the filter graph, reference it by the GraphBuilder interface,
|
||||
// to make graph building more convenient.
|
||||
hr = CoCreateInstance(CLSID_FilterGraph,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IGraphBuilder,
|
||||
reinterpret_cast<void**>(static_cast<IGraphBuilder**>(getter_AddRefs(mGraph))));
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr) && mGraph, NS_ERROR_FAILURE);
|
||||
|
||||
rv = ParseMP3Headers(&mMP3FrameParser, mDecoder->GetResource());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef DIRECTSHOW_REGISTER_GRAPH
|
||||
hr = AddGraphToRunningObjectTable(mGraph, &mRotRegister);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
#endif
|
||||
|
||||
// Extract the interface pointers we'll need from the filter graph.
|
||||
hr = mGraph->QueryInterface(static_cast<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);
|
||||
|
||||
// Build the graph. Create the filters we need, and connect them. We
|
||||
// build the entire graph ourselves to prevent other decoders installed
|
||||
// on the system being created and used.
|
||||
|
||||
// Our source filters, wraps the MediaResource.
|
||||
mSourceFilter = new SourceFilter(MEDIATYPE_Stream, MEDIASUBTYPE_MPEG1Audio);
|
||||
NS_ENSURE_TRUE(mSourceFilter, NS_ERROR_FAILURE);
|
||||
|
||||
rv = mSourceFilter->Init(mDecoder->GetResource(), mMP3FrameParser.GetMP3Offset());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
hr = mGraph->AddFilter(mSourceFilter, L"MozillaDirectShowSource");
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
|
||||
|
||||
// The MPEG demuxer.
|
||||
RefPtr<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);
|
||||
|
||||
int64_t duration = mMP3FrameParser.GetDuration();
|
||||
if (SUCCEEDED(hr)) {
|
||||
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration));
|
||||
}
|
||||
|
||||
LOG("Successfully initialized DirectShow MP3 decoder.");
|
||||
LOG("Channels=%u Hz=%u duration=%lld bytesPerSample=%d",
|
||||
mInfo.mAudio.mChannels,
|
||||
mInfo.mAudio.mRate,
|
||||
RefTimeToUsecs(duration),
|
||||
mBytesPerSample);
|
||||
|
||||
*aInfo = mInfo;
|
||||
// Note: The SourceFilter strips ID3v2 tags out of the stream.
|
||||
*aTags = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
inline float
|
||||
UnsignedByteToAudioSample(uint8_t aValue)
|
||||
{
|
||||
return aValue * (2.0f / UINT8_MAX) - 1.0f;
|
||||
}
|
||||
|
||||
bool
|
||||
DirectShowReader::Finish(HRESULT aStatus)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
LOG("DirectShowReader::Finish(0x%x)", aStatus);
|
||||
// Notify the filter graph of end of stream.
|
||||
RefPtr<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,
|
||||
const media::TimeUnit& aTimeThreshold)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoderReader::SeekPromise>
|
||||
DirectShowReader::Seek(const SeekTarget& aTarget)
|
||||
{
|
||||
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,109 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(DirectShowReader_h_)
|
||||
#define DirectShowReader_h_
|
||||
|
||||
#include "windows.h" // HRESULT, DWORD
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "MediaResource.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "MP3FrameParser.h"
|
||||
|
||||
// Add the graph to the Running Object Table so that we can connect
|
||||
// to this graph with GraphEdit/GraphStudio. Note: on Vista and up you must
|
||||
// also regsvr32 proppage.dll from the Windows SDK.
|
||||
// See: http://msdn.microsoft.com/en-us/library/ms787252(VS.85).aspx
|
||||
// #define DIRECTSHOW_REGISTER_GRAPH
|
||||
|
||||
struct IGraphBuilder;
|
||||
struct IMediaControl;
|
||||
struct IMediaSeeking;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AudioSinkFilter;
|
||||
class SourceFilter;
|
||||
|
||||
// Decoder backend for decoding MP3 using DirectShow. DirectShow operates as
|
||||
// a filter graph. The basic design of the DirectShowReader is that we have
|
||||
// a SourceFilter that wraps the MediaResource that connects to the
|
||||
// MP3 decoder filter. The MP3 decoder filter "pulls" data as it requires it
|
||||
// downstream on its own thread. When the MP3 decoder has produced a block of
|
||||
// decoded samples, its thread calls downstream into our AudioSinkFilter,
|
||||
// passing the decoded buffer in. The AudioSinkFilter inserts the samples into
|
||||
// a SampleSink object. The SampleSink blocks the MP3 decoder's thread until
|
||||
// the decode thread calls DecodeAudioData(), whereupon the SampleSink
|
||||
// releases the decoded samples to the decode thread, and unblocks the MP3
|
||||
// decoder's thread. The MP3 decoder can then request more data from the
|
||||
// SourceFilter, and decode more data. If the decode thread calls
|
||||
// DecodeAudioData() and there's no decoded samples waiting to be extracted
|
||||
// in the SampleSink, the SampleSink blocks the decode thread until the MP3
|
||||
// decoder produces a decoded sample.
|
||||
class DirectShowReader : public MediaDecoderReader
|
||||
{
|
||||
public:
|
||||
explicit DirectShowReader(AbstractMediaDecoder* aDecoder);
|
||||
|
||||
virtual ~DirectShowReader();
|
||||
|
||||
bool DecodeAudioData() override;
|
||||
bool DecodeVideoFrame(bool& aKeyframeSkip,
|
||||
const media::TimeUnit& aTimeThreshold) override;
|
||||
|
||||
nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) override;
|
||||
|
||||
RefPtr<SeekPromise> Seek(const SeekTarget& aTarget) override;
|
||||
|
||||
static const GUID CLSID_MPEG_LAYER_3_DECODER_FILTER;
|
||||
|
||||
private:
|
||||
// Notifies the filter graph that playback is complete. aStatus is
|
||||
// the code to send to the filter graph. Always returns false, so
|
||||
// that we can just "return Finish()" from DecodeAudioData().
|
||||
bool Finish(HRESULT aStatus);
|
||||
|
||||
nsresult SeekInternal(int64_t aTime);
|
||||
|
||||
// DirectShow filter graph, and associated playback and seeking
|
||||
// control interfaces.
|
||||
RefPtr<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;
|
||||
|
||||
// Some MP3s are variable bitrate, so DirectShow's duration estimation
|
||||
// can make its duration estimation based on the wrong bitrate. So we parse
|
||||
// the MP3 frames to get a more accuate estimate of the duration.
|
||||
MP3FrameParser mMP3FrameParser;
|
||||
|
||||
#ifdef DIRECTSHOW_REGISTER_GRAPH
|
||||
// Used to add/remove the filter graph to the Running Object Table. You can
|
||||
// connect GraphEdit/GraphStudio to the graph to observe and/or debug its
|
||||
// topology and state.
|
||||
DWORD mRotRegister;
|
||||
#endif
|
||||
|
||||
// Number of channels in the audio stream.
|
||||
uint32_t mNumChannels;
|
||||
|
||||
// Samples per second in the audio stream.
|
||||
uint32_t mAudioRate;
|
||||
|
||||
// Number of bytes per sample. Can be either 1 or 2.
|
||||
uint32_t mBytesPerSample;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -1,369 +0,0 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DirectShowUtils.h"
|
||||
#include "dmodshow.h"
|
||||
#include "wmcodecdsp.h"
|
||||
#include "dmoreg.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
#define WARN(...) NS_WARNING(nsPrintfCString(__VA_ARGS__).get())
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Create a table which maps GUIDs to a string representation of the GUID.
|
||||
// This is useful for debugging purposes, for logging the GUIDs of media types.
|
||||
// This is only available when logging is enabled, i.e. not in release builds.
|
||||
struct GuidToName {
|
||||
const char* name;
|
||||
const GUID guid;
|
||||
};
|
||||
|
||||
#pragma push_macro("OUR_GUID_ENTRY")
|
||||
#undef OUR_GUID_ENTRY
|
||||
#define OUR_GUID_ENTRY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
{ #name, {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} },
|
||||
|
||||
static const GuidToName GuidToNameTable[] = {
|
||||
#include <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;
|
||||
}
|
||||
|
||||
// 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,125 +0,0 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _DirectShowUtils_h_
|
||||
#define _DirectShowUtils_h_
|
||||
|
||||
#include <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:
|
||||
|
||||
explicit Signal(CriticalSection* aLock)
|
||||
: mLock(aLock)
|
||||
{
|
||||
CriticalSectionAutoEnter lock(*mLock);
|
||||
mEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
}
|
||||
|
||||
~Signal() {
|
||||
CriticalSectionAutoEnter lock(*mLock);
|
||||
CloseHandle(mEvent);
|
||||
}
|
||||
|
||||
// Lock must be held.
|
||||
void Notify() {
|
||||
SetEvent(mEvent);
|
||||
}
|
||||
|
||||
// Lock must be held. Check the wait condition before waiting!
|
||||
HRESULT Wait() {
|
||||
mLock->Leave();
|
||||
DWORD result = WaitForSingleObject(mEvent, INFINITE);
|
||||
mLock->Enter();
|
||||
return result == WAIT_OBJECT_0 ? S_OK : E_FAIL;
|
||||
}
|
||||
|
||||
private:
|
||||
CriticalSection* mLock;
|
||||
HANDLE mEvent;
|
||||
};
|
||||
|
||||
HRESULT
|
||||
AddGraphToRunningObjectTable(IUnknown *aUnkGraph, DWORD *aOutRotRegister);
|
||||
|
||||
void
|
||||
RemoveGraphFromRunningObjectTable(DWORD aRotRegister);
|
||||
|
||||
const char*
|
||||
GetGraphNotifyString(long evCode);
|
||||
|
||||
// Creates a filter and adds it to a graph.
|
||||
HRESULT
|
||||
CreateAndAddFilter(IGraphBuilder* aGraph,
|
||||
REFGUID aFilterClsId,
|
||||
LPCWSTR aFilterName,
|
||||
IBaseFilter **aOutFilter);
|
||||
|
||||
HRESULT
|
||||
AddMP3DMOWrapperFilter(IGraphBuilder* aGraph,
|
||||
IBaseFilter **aOutFilter);
|
||||
|
||||
// Connects the output pin on aOutputFilter to an input pin on
|
||||
// aInputFilter, in aGraph.
|
||||
HRESULT
|
||||
ConnectFilters(IGraphBuilder* aGraph,
|
||||
IBaseFilter* aOutputFilter,
|
||||
IBaseFilter* aInputFilter);
|
||||
|
||||
HRESULT
|
||||
MatchUnconnectedPin(IPin* aPin,
|
||||
PIN_DIRECTION aPinDir,
|
||||
bool *aOutMatches);
|
||||
|
||||
// Converts from microseconds to DirectShow "Reference Time"
|
||||
// (hundreds of nanoseconds).
|
||||
inline int64_t
|
||||
UsecsToRefTime(const int64_t aUsecs)
|
||||
{
|
||||
return aUsecs * 10;
|
||||
}
|
||||
|
||||
// Converts from DirectShow "Reference Time" (hundreds of nanoseconds)
|
||||
// to microseconds.
|
||||
inline int64_t
|
||||
RefTimeToUsecs(const int64_t hRefTime)
|
||||
{
|
||||
return hRefTime / 10;
|
||||
}
|
||||
|
||||
// Converts from DirectShow "Reference Time" (hundreds of nanoseconds)
|
||||
// to seconds.
|
||||
inline double
|
||||
RefTimeToSeconds(const REFERENCE_TIME aRefTime)
|
||||
{
|
||||
return double(aRefTime) / 10000000;
|
||||
}
|
||||
|
||||
const char*
|
||||
GetDirectShowGuidName(const GUID& aGuid);
|
||||
|
||||
// Returns true if we can instantiate an MP3 demuxer and decoder filters.
|
||||
// Use this to detect whether MP3 support is installed.
|
||||
bool
|
||||
CanDecodeMP3UsingDirectShow();
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -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,683 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SourceFilter.h"
|
||||
#include "MediaResource.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "DirectShowUtils.h"
|
||||
#include "MP3FrameParser.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include <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,41 +0,0 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS += [
|
||||
'AudioSinkFilter.h',
|
||||
'AudioSinkInputPin.h',
|
||||
'DirectShowDecoder.h',
|
||||
'DirectShowReader.h',
|
||||
'DirectShowUtils.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'DirectShowDecoder.cpp',
|
||||
'DirectShowUtils.cpp',
|
||||
'SourceFilter.cpp',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'AudioSinkFilter.cpp',
|
||||
'AudioSinkInputPin.cpp',
|
||||
'DirectShowReader.cpp',
|
||||
'SampleSink.cpp',
|
||||
]
|
||||
|
||||
# If WebRTC isn't being built, we need to compile the DirectShow base classes so that
|
||||
# they're available at link time.
|
||||
if not CONFIG['MOZ_WEBRTC']:
|
||||
SOURCES += [
|
||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseFilter.cpp',
|
||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseInputPin.cpp',
|
||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows/BasePin.cpp',
|
||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows/MediaType.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
LOCAL_INCLUDES += [
|
||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows',
|
||||
]
|
@ -50,9 +50,6 @@ DIRS += [
|
||||
'webvtt',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_DIRECTSHOW']:
|
||||
DIRS += ['directshow']
|
||||
|
||||
if CONFIG['MOZ_ANDROID_OMX']:
|
||||
DIRS += ['android']
|
||||
|
||||
@ -297,11 +294,6 @@ LOCAL_INCLUDES += [
|
||||
'/netwerk/base',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_DIRECTSHOW']:
|
||||
LOCAL_INCLUDES += [
|
||||
'/media/webrtc/trunk/webrtc/modules/video_capture/windows',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WEBRTC']:
|
||||
LOCAL_INCLUDES += [
|
||||
'/media/webrtc/signaling/src/common',
|
||||
|
@ -2185,7 +2185,6 @@ MOZ_VORBIS=
|
||||
MOZ_TREMOR=
|
||||
MOZ_SAMPLE_TYPE_FLOAT32=
|
||||
MOZ_SAMPLE_TYPE_S16=
|
||||
MOZ_DIRECTSHOW=
|
||||
MOZ_WEBRTC=1
|
||||
MOZ_PEERCONNECTION=
|
||||
MOZ_SRTP=
|
||||
@ -2793,23 +2792,6 @@ if test -n "$MOZ_APPLEMEDIA"; then
|
||||
fi
|
||||
fi # COMPILE_ENVIRONMENT
|
||||
|
||||
dnl ========================================================
|
||||
dnl = DirectShow support
|
||||
dnl ========================================================
|
||||
if test "$OS_ARCH" = "WINNT"; then
|
||||
dnl Enable DirectShow support by default.
|
||||
MOZ_DIRECTSHOW=1
|
||||
fi
|
||||
|
||||
MOZ_ARG_DISABLE_BOOL(directshow,
|
||||
[ --disable-directshow Disable support for DirectShow],
|
||||
MOZ_DIRECTSHOW=,
|
||||
MOZ_DIRECTSHOW=1)
|
||||
|
||||
if test -n "$MOZ_DIRECTSHOW"; then
|
||||
AC_DEFINE(MOZ_DIRECTSHOW)
|
||||
fi;
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Built-in fragmented MP4 support.
|
||||
dnl ========================================================
|
||||
@ -5335,7 +5317,6 @@ AC_SUBST(MOZ_VORBIS)
|
||||
AC_SUBST(MOZ_TREMOR)
|
||||
AC_SUBST(MOZ_FFVPX)
|
||||
AC_SUBST_LIST(FFVPX_ASFLAGS)
|
||||
AC_SUBST(MOZ_DIRECTSHOW)
|
||||
AC_SUBST(MOZ_ANDROID_OMX)
|
||||
AC_SUBST(MOZ_OMX_PLUGIN)
|
||||
AC_SUBST(VPX_USE_YASM)
|
||||
|
@ -177,14 +177,6 @@ if CONFIG['MOZ_WMF']:
|
||||
'strmiids',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_DIRECTSHOW']:
|
||||
OS_LIBS += [
|
||||
'dmoguids',
|
||||
'wmcodecdspuuid',
|
||||
'strmiids',
|
||||
'msdmo',
|
||||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'FreeBSD':
|
||||
OS_LIBS += [
|
||||
'util',
|
||||
|
Loading…
Reference in New Issue
Block a user