mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-19 19:29:32 +00:00
684 lines
19 KiB
C++
684 lines
19 KiB
C++
/* -*- 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)
|
|
extern LogModule* GetDirectShowLog();
|
|
#define DIRECTSHOW_LOG(...) MOZ_LOG(GetDirectShowLog(), 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
|