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:
Jean-Yves Avenard 2017-06-05 13:04:27 +02:00
parent 7310f412e5
commit 484055c07b
18 changed files with 0 additions and 2780 deletions

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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',
]

View File

@ -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',

View File

@ -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)

View File

@ -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',