michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "SourceFilter.h" michael@0: #include "MediaResource.h" michael@0: #include "mozilla/RefPtr.h" michael@0: #include "DirectShowUtils.h" michael@0: #include "MP3FrameParser.h" michael@0: #include "prlog.h" michael@0: #include michael@0: michael@0: using namespace mozilla::media; michael@0: michael@0: namespace mozilla { michael@0: michael@0: // Define to trace what's on... michael@0: //#define DEBUG_SOURCE_TRACE 1 michael@0: michael@0: #if defined(PR_LOGGING) && defined (DEBUG_SOURCE_TRACE) michael@0: PRLogModuleInfo* GetDirectShowLog(); michael@0: #define DIRECTSHOW_LOG(...) PR_LOG(GetDirectShowLog(), PR_LOG_DEBUG, (__VA_ARGS__)) michael@0: #else michael@0: #define DIRECTSHOW_LOG(...) michael@0: #endif michael@0: michael@0: static HRESULT michael@0: DoGetInterface(IUnknown* aUnknown, void** aInterface) michael@0: { michael@0: if (!aInterface) michael@0: return E_POINTER; michael@0: *aInterface = aUnknown; michael@0: aUnknown->AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: // Stores details of IAsyncReader::Request(). michael@0: class ReadRequest { michael@0: public: michael@0: michael@0: ReadRequest(IMediaSample* aSample, michael@0: DWORD_PTR aDwUser, michael@0: uint32_t aOffset, michael@0: uint32_t aCount) michael@0: : mSample(aSample), michael@0: mDwUser(aDwUser), michael@0: mOffset(aOffset), michael@0: mCount(aCount) michael@0: { michael@0: MOZ_COUNT_CTOR(ReadRequest); michael@0: } michael@0: michael@0: ~ReadRequest() { michael@0: MOZ_COUNT_DTOR(ReadRequest); michael@0: } michael@0: michael@0: RefPtr mSample; michael@0: DWORD_PTR mDwUser; michael@0: uint32_t mOffset; michael@0: uint32_t mCount; michael@0: }; michael@0: michael@0: // A wrapper around media resource that presents only a partition of the michael@0: // underlying resource to the caller to use. The partition returned is from michael@0: // an offset to the end of stream, and this object deals with ensuring michael@0: // the offsets and lengths etc are translated from the reduced partition michael@0: // exposed to the caller, to the absolute offsets of the underlying stream. michael@0: class MediaResourcePartition { michael@0: public: michael@0: MediaResourcePartition(MediaResource* aResource, michael@0: int64_t aDataStart) michael@0: : mResource(aResource), michael@0: mDataOffset(aDataStart) michael@0: {} michael@0: michael@0: int64_t GetLength() { michael@0: int64_t len = mResource->GetLength(); michael@0: if (len == -1) { michael@0: return len; michael@0: } michael@0: return std::max(0, len - mDataOffset); michael@0: } michael@0: nsresult ReadAt(int64_t aOffset, char* aBuffer, michael@0: uint32_t aCount, uint32_t* aBytes) michael@0: { michael@0: return mResource->ReadAt(aOffset + mDataOffset, michael@0: aBuffer, michael@0: aCount, michael@0: aBytes); michael@0: } michael@0: int64_t GetCachedDataEnd() { michael@0: int64_t tell = mResource->Tell(); michael@0: int64_t dataEnd = mResource->GetCachedDataEnd(tell) - mDataOffset; michael@0: return dataEnd; michael@0: } michael@0: private: michael@0: // MediaResource from which we read data. michael@0: RefPtr mResource; michael@0: int64_t mDataOffset; michael@0: }; michael@0: michael@0: michael@0: // Output pin for SourceFilter, which implements IAsyncReader, to michael@0: // allow downstream filters to pull/read data from it. Downstream pins michael@0: // register to read data using Request(), and asynchronously wait for the michael@0: // reads to complete using WaitForNext(). They may also synchronously read michael@0: // using SyncRead(). This class is a delegate (tear off) of michael@0: // SourceFilter. michael@0: // michael@0: // We can expose only a segment of the MediaResource to the filter graph. michael@0: // This is used to strip off the ID3v2 tags from the stream, as DirectShow michael@0: // has trouble parsing some headers. michael@0: // michael@0: // Implements: michael@0: // * IAsyncReader michael@0: // * IPin michael@0: // * IQualityControl michael@0: // * IUnknown michael@0: // michael@0: class DECLSPEC_UUID("18e5cfb2-1015-440c-a65c-e63853235894") michael@0: OutputPin : public IAsyncReader, michael@0: public BasePin michael@0: { michael@0: public: michael@0: michael@0: OutputPin(MediaResource* aMediaResource, michael@0: SourceFilter* aParent, michael@0: CriticalSection& aFilterLock, michael@0: int64_t aMP3DataStart); michael@0: virtual ~OutputPin(); michael@0: michael@0: // IUnknown michael@0: // Defer to ref counting to BasePin, which defers to owning nsBaseFilter. michael@0: STDMETHODIMP_(ULONG) AddRef() MOZ_OVERRIDE { return BasePin::AddRef(); } michael@0: STDMETHODIMP_(ULONG) Release() MOZ_OVERRIDE { return BasePin::Release(); } michael@0: STDMETHODIMP QueryInterface(REFIID iid, void** ppv) MOZ_OVERRIDE; michael@0: michael@0: // BasePin Overrides. michael@0: // Determines if the pin accepts a specific media type. michael@0: HRESULT CheckMediaType(const MediaType* aMediaType) MOZ_OVERRIDE; michael@0: michael@0: // Retrieves a preferred media type, by index value. michael@0: HRESULT GetMediaType(int aPosition, MediaType* aMediaType) MOZ_OVERRIDE; michael@0: michael@0: // Releases the pin from a connection. michael@0: HRESULT BreakConnect(void) MOZ_OVERRIDE; michael@0: michael@0: // Determines whether a pin connection is suitable. michael@0: HRESULT CheckConnect(IPin* aPin) MOZ_OVERRIDE; michael@0: michael@0: michael@0: // IAsyncReader overrides michael@0: michael@0: // The RequestAllocator method requests an allocator during the michael@0: // pin connection. michael@0: STDMETHODIMP RequestAllocator(IMemAllocator* aPreferred, michael@0: ALLOCATOR_PROPERTIES* aProps, michael@0: IMemAllocator** aActual) MOZ_OVERRIDE; michael@0: michael@0: // The Request method queues an asynchronous request for data. Downstream michael@0: // will call WaitForNext() when they want to retrieve the result. michael@0: STDMETHODIMP Request(IMediaSample* aSample, DWORD_PTR aUserData) MOZ_OVERRIDE; michael@0: michael@0: // The WaitForNext method waits for the next pending read request michael@0: // to complete. This method fails if the graph is flushing. michael@0: // Defers to SyncRead/5. michael@0: STDMETHODIMP WaitForNext(DWORD aTimeout, michael@0: IMediaSample** aSamples, michael@0: DWORD_PTR* aUserData) MOZ_OVERRIDE; michael@0: michael@0: // The SyncReadAligned method performs a synchronous read. The method michael@0: // blocks until the request is completed. Defers to SyncRead/5. This michael@0: // method does not fail if the graph is flushing. michael@0: STDMETHODIMP SyncReadAligned(IMediaSample* aSample) MOZ_OVERRIDE; michael@0: michael@0: // The SyncRead method performs a synchronous read. The method blocks michael@0: // until the request is completed. Defers to SyncRead/5. This michael@0: // method does not fail if the graph is flushing. michael@0: STDMETHODIMP SyncRead(LONGLONG aPosition, LONG aLength, BYTE* aBuffer) MOZ_OVERRIDE; michael@0: michael@0: // The Length method retrieves the total length of the stream. michael@0: STDMETHODIMP Length(LONGLONG* aTotal, LONGLONG* aAvailable) MOZ_OVERRIDE; michael@0: michael@0: // IPin Overrides michael@0: STDMETHODIMP BeginFlush(void) MOZ_OVERRIDE; michael@0: STDMETHODIMP EndFlush(void) MOZ_OVERRIDE; michael@0: michael@0: uint32_t GetAndResetBytesConsumedCount(); michael@0: michael@0: private: michael@0: michael@0: // Protects thread-shared data/structures (mFlushCount, mPendingReads). michael@0: // WaitForNext() also waits on this monitor michael@0: CriticalSection& mPinLock; michael@0: michael@0: // Signal used with mPinLock to implement WaitForNext(). michael@0: Signal mSignal; michael@0: michael@0: // The filter that owns us. Weak reference, as we're a delegate (tear off). michael@0: SourceFilter* mParentSource; michael@0: michael@0: MediaResourcePartition mResource; michael@0: michael@0: // Counter, inc'd in BeginFlush(), dec'd in EndFlush(). Calls to this can michael@0: // come from multiple threads and can interleave, hence the counter. michael@0: int32_t mFlushCount; michael@0: michael@0: // Number of bytes that have been read from the output pin since the last michael@0: // time GetAndResetBytesConsumedCount() was called. michael@0: uint32_t mBytesConsumed; michael@0: michael@0: // Deque of ReadRequest* for reads that are yet to be serviced. michael@0: // nsReadRequest's are stored on the heap, popper must delete them. michael@0: nsDeque mPendingReads; michael@0: michael@0: // Flags if the downstream pin has QI'd for IAsyncReader. We refuse michael@0: // connection if they don't query, as it means they're assuming that we're michael@0: // a push filter, and we're not. michael@0: bool mQueriedForAsyncReader; michael@0: michael@0: }; michael@0: michael@0: // For mingw __uuidof support michael@0: #ifdef __CRT_UUID_DECL michael@0: } michael@0: __CRT_UUID_DECL(mozilla::OutputPin, 0x18e5cfb2,0x1015,0x440c,0xa6,0x5c,0xe6,0x38,0x53,0x23,0x58,0x94); michael@0: namespace mozilla { michael@0: #endif michael@0: michael@0: OutputPin::OutputPin(MediaResource* aResource, michael@0: SourceFilter* aParent, michael@0: CriticalSection& aFilterLock, michael@0: int64_t aMP3DataStart) michael@0: : BasePin(static_cast(aParent), michael@0: &aFilterLock, michael@0: L"MozillaOutputPin", michael@0: PINDIR_OUTPUT), michael@0: mPinLock(aFilterLock), michael@0: mSignal(&mPinLock), michael@0: mParentSource(aParent), michael@0: mResource(aResource, aMP3DataStart), michael@0: mFlushCount(0), michael@0: mBytesConsumed(0), michael@0: mQueriedForAsyncReader(false) michael@0: { michael@0: MOZ_COUNT_CTOR(OutputPin); michael@0: DIRECTSHOW_LOG("OutputPin::OutputPin()"); michael@0: } michael@0: michael@0: OutputPin::~OutputPin() michael@0: { michael@0: MOZ_COUNT_DTOR(OutputPin); michael@0: DIRECTSHOW_LOG("OutputPin::~OutputPin()"); michael@0: } michael@0: michael@0: HRESULT michael@0: OutputPin::BreakConnect() michael@0: { michael@0: mQueriedForAsyncReader = false; michael@0: return BasePin::BreakConnect(); michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: OutputPin::QueryInterface(REFIID aIId, void** aInterface) michael@0: { michael@0: if (aIId == IID_IAsyncReader) { michael@0: mQueriedForAsyncReader = true; michael@0: return DoGetInterface(static_cast(this), aInterface); michael@0: } michael@0: michael@0: if (aIId == __uuidof(OutputPin)) { michael@0: AddRef(); michael@0: *aInterface = this; michael@0: return S_OK; michael@0: } michael@0: michael@0: return BasePin::QueryInterface(aIId, aInterface); michael@0: } michael@0: michael@0: HRESULT michael@0: OutputPin::CheckConnect(IPin* aPin) michael@0: { michael@0: // Our connection is only suitable if the downstream pin knows michael@0: // that we're asynchronous (i.e. it queried for IAsyncReader). michael@0: return mQueriedForAsyncReader ? S_OK : S_FALSE; michael@0: } michael@0: michael@0: HRESULT michael@0: OutputPin::CheckMediaType(const MediaType* aMediaType) michael@0: { michael@0: const MediaType *myMediaType = mParentSource->GetMediaType(); michael@0: michael@0: if (IsEqualGUID(aMediaType->majortype, myMediaType->majortype) && michael@0: IsEqualGUID(aMediaType->subtype, myMediaType->subtype) && michael@0: IsEqualGUID(aMediaType->formattype, myMediaType->formattype)) michael@0: { michael@0: DIRECTSHOW_LOG("OutputPin::CheckMediaType() Match: major=%s minor=%s TC=%d FSS=%d SS=%u", michael@0: GetDirectShowGuidName(aMediaType->majortype), michael@0: GetDirectShowGuidName(aMediaType->subtype), michael@0: aMediaType->TemporalCompression(), michael@0: aMediaType->bFixedSizeSamples, michael@0: aMediaType->SampleSize()); michael@0: return S_OK; michael@0: } michael@0: michael@0: DIRECTSHOW_LOG("OutputPin::CheckMediaType() Failed to match: major=%s minor=%s TC=%d FSS=%d SS=%u", michael@0: GetDirectShowGuidName(aMediaType->majortype), michael@0: GetDirectShowGuidName(aMediaType->subtype), michael@0: aMediaType->TemporalCompression(), michael@0: aMediaType->bFixedSizeSamples, michael@0: aMediaType->SampleSize()); michael@0: return S_FALSE; michael@0: } michael@0: michael@0: HRESULT michael@0: OutputPin::GetMediaType(int aPosition, MediaType* aMediaType) michael@0: { michael@0: if (!aMediaType) michael@0: return E_POINTER; michael@0: michael@0: if (aPosition == 0) { michael@0: aMediaType->Assign(mParentSource->GetMediaType()); michael@0: return S_OK; michael@0: } michael@0: return VFW_S_NO_MORE_ITEMS; michael@0: } michael@0: michael@0: static inline bool michael@0: IsPowerOf2(int32_t x) { michael@0: return ((-x & x) != x); michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: OutputPin::RequestAllocator(IMemAllocator* aPreferred, michael@0: ALLOCATOR_PROPERTIES* aProps, michael@0: IMemAllocator** aActual) michael@0: { michael@0: // Require the downstream pin to suggest what they want... michael@0: if (!aPreferred) return E_POINTER; michael@0: if (!aProps) return E_POINTER; michael@0: if (!aActual) return E_POINTER; michael@0: michael@0: // We only care about alignment - our allocator will reject anything michael@0: // which isn't power-of-2 aligned, so so try a 4-byte aligned allocator. michael@0: ALLOCATOR_PROPERTIES props; michael@0: memcpy(&props, aProps, sizeof(ALLOCATOR_PROPERTIES)); michael@0: if (aProps->cbAlign == 0 || IsPowerOf2(aProps->cbAlign)) { michael@0: props.cbAlign = 4; michael@0: } michael@0: michael@0: // Limit allocator's number of buffers. We know that the media will most michael@0: // likely be bound by network speed, not by decoding speed. We also michael@0: // store the incoming data in a Gecko stream, if we don't limit buffers michael@0: // here we'll end up duplicating a lot of storage. We must have enough michael@0: // space for audio key frames to fit in the first batch of buffers however, michael@0: // else pausing may fail for some downstream decoders. michael@0: if (props.cBuffers > BaseFilter::sMaxNumBuffers) { michael@0: props.cBuffers = BaseFilter::sMaxNumBuffers; michael@0: } michael@0: michael@0: // The allocator properties that are actually used. We don't store michael@0: // this, we need it for SetProperties() below to succeed. michael@0: ALLOCATOR_PROPERTIES actualProps; michael@0: HRESULT hr; michael@0: michael@0: if (aPreferred) { michael@0: // Play nice and prefer the downstream pin's preferred allocator. michael@0: hr = aPreferred->SetProperties(&props, &actualProps); michael@0: if (SUCCEEDED(hr)) { michael@0: aPreferred->AddRef(); michael@0: *aActual = aPreferred; michael@0: return S_OK; michael@0: } michael@0: } michael@0: michael@0: // Else downstream hasn't requested a specific allocator, so create one... michael@0: michael@0: // Just create a default allocator. It's highly unlikely that we'll use michael@0: // this anyway, as most parsers insist on using their own allocators. michael@0: nsRefPtr allocator; michael@0: hr = CoCreateInstance(CLSID_MemoryAllocator, michael@0: 0, michael@0: CLSCTX_INPROC_SERVER, michael@0: IID_IMemAllocator, michael@0: getter_AddRefs(allocator)); michael@0: if(FAILED(hr) || (allocator == nullptr)) { michael@0: NS_WARNING("Can't create our own DirectShow allocator."); michael@0: return hr; michael@0: } michael@0: michael@0: // See if we can make it suitable michael@0: hr = allocator->SetProperties(&props, &actualProps); michael@0: if (SUCCEEDED(hr)) { michael@0: // We need to release our refcount on pAlloc, and addref michael@0: // it to pass a refcount to the caller - this is a net nothing. michael@0: allocator.forget(aActual); michael@0: return S_OK; michael@0: } michael@0: michael@0: NS_WARNING("Failed to pick an allocator"); michael@0: return hr; michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: OutputPin::Request(IMediaSample* aSample, DWORD_PTR aDwUser) michael@0: { michael@0: if (!aSample) return E_FAIL; michael@0: michael@0: CriticalSectionAutoEnter lock(*mLock); michael@0: NS_ASSERTION(!mFlushCount, "Request() while flushing"); michael@0: michael@0: if (mFlushCount) michael@0: return VFW_E_WRONG_STATE; michael@0: michael@0: REFERENCE_TIME refStart = 0, refEnd = 0; michael@0: if (FAILED(aSample->GetTime(&refStart, &refEnd))) { michael@0: NS_WARNING("Sample incorrectly timestamped"); michael@0: return VFW_E_SAMPLE_TIME_NOT_SET; michael@0: } michael@0: michael@0: // Convert reference time to bytes. michael@0: uint32_t start = (uint32_t)(refStart / 10000000); michael@0: uint32_t end = (uint32_t)(refEnd / 10000000); michael@0: michael@0: uint32_t numBytes = end - start; michael@0: michael@0: ReadRequest* request = new ReadRequest(aSample, michael@0: aDwUser, michael@0: start, michael@0: numBytes); michael@0: // Memory for |request| is free when it's popped from the completed michael@0: // reads list. michael@0: michael@0: // Push this onto the queue of reads to be serviced. michael@0: mPendingReads.Push(request); michael@0: michael@0: // Notify any threads blocked in WaitForNext() which are waiting for mPendingReads michael@0: // to become non-empty. michael@0: mSignal.Notify(); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: OutputPin::WaitForNext(DWORD aTimeout, michael@0: IMediaSample** aOutSample, michael@0: DWORD_PTR* aOutDwUser) michael@0: { michael@0: NS_ASSERTION(aTimeout == 0 || aTimeout == INFINITE, michael@0: "Oops, we don't handle this!"); michael@0: michael@0: *aOutSample = nullptr; michael@0: *aOutDwUser = 0; michael@0: michael@0: LONGLONG offset = 0; michael@0: LONG count = 0; michael@0: BYTE* buf = nullptr; michael@0: michael@0: { michael@0: CriticalSectionAutoEnter lock(*mLock); michael@0: michael@0: // Wait until there's a pending read to service. michael@0: while (aTimeout && mPendingReads.GetSize() == 0 && !mFlushCount) { michael@0: // Note: No need to guard against shutdown-during-wait here, as michael@0: // typically the thread doing the pull will have already called michael@0: // Request(), so we won't Wait() here anyway. SyncRead() will fail michael@0: // on shutdown. michael@0: mSignal.Wait(); michael@0: } michael@0: michael@0: nsAutoPtr request(reinterpret_cast(mPendingReads.PopFront())); michael@0: if (!request) michael@0: return VFW_E_WRONG_STATE; michael@0: michael@0: *aOutSample = request->mSample; michael@0: *aOutDwUser = request->mDwUser; michael@0: michael@0: offset = request->mOffset; michael@0: count = request->mCount; michael@0: buf = nullptr; michael@0: request->mSample->GetPointer(&buf); michael@0: NS_ASSERTION(buf != nullptr, "Invalid buffer!"); michael@0: michael@0: if (mFlushCount) { michael@0: return VFW_E_TIMEOUT; michael@0: } michael@0: } michael@0: michael@0: return SyncRead(offset, count, buf); michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: OutputPin::SyncReadAligned(IMediaSample* aSample) michael@0: { michael@0: { michael@0: // Ignore reads while flushing. michael@0: CriticalSectionAutoEnter lock(*mLock); michael@0: if (mFlushCount) { michael@0: return S_FALSE; michael@0: } michael@0: } michael@0: michael@0: if (!aSample) michael@0: return E_FAIL; michael@0: michael@0: REFERENCE_TIME lStart = 0, lEnd = 0; michael@0: if (FAILED(aSample->GetTime(&lStart, &lEnd))) { michael@0: NS_WARNING("Sample incorrectly timestamped"); michael@0: return VFW_E_SAMPLE_TIME_NOT_SET; michael@0: } michael@0: michael@0: // Convert reference time to bytes. michael@0: int32_t start = (int32_t)(lStart / 10000000); michael@0: int32_t end = (int32_t)(lEnd / 10000000); michael@0: michael@0: int32_t numBytes = end - start; michael@0: michael@0: // If the range extends off the end of stream, truncate to the end of stream michael@0: // as per IAsyncReader specificiation. michael@0: int64_t streamLength = mResource.GetLength(); michael@0: if (streamLength != -1) { michael@0: // We know the exact length of the stream, fail if the requested offset michael@0: // is beyond it. michael@0: if (start > streamLength) { michael@0: return VFW_E_BADALIGN; michael@0: } michael@0: michael@0: // If the end of the chunk to read is off the end of the stream, michael@0: // truncate it to the end of the stream. michael@0: if ((start + numBytes) > streamLength) { michael@0: numBytes = (uint32_t)(streamLength - start); michael@0: } michael@0: } michael@0: michael@0: BYTE* buf=0; michael@0: aSample->GetPointer(&buf); michael@0: michael@0: return SyncRead(start, numBytes, buf); michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: OutputPin::SyncRead(LONGLONG aPosition, michael@0: LONG aLength, michael@0: BYTE* aBuffer) michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: NS_ENSURE_TRUE(aPosition >= 0, E_FAIL); michael@0: NS_ENSURE_TRUE(aLength > 0, E_FAIL); michael@0: NS_ENSURE_TRUE(aBuffer, E_POINTER); michael@0: michael@0: DIRECTSHOW_LOG("OutputPin::SyncRead(%lld, %d)", aPosition, aLength); michael@0: { michael@0: // Ignore reads while flushing. michael@0: CriticalSectionAutoEnter lock(*mLock); michael@0: if (mFlushCount) { michael@0: return S_FALSE; michael@0: } michael@0: } michael@0: michael@0: // Read in a loop to ensure we fill the buffer, when possible. michael@0: LONG totalBytesRead = 0; michael@0: while (totalBytesRead < aLength) { michael@0: BYTE* readBuffer = aBuffer + totalBytesRead; michael@0: uint32_t bytesRead = 0; michael@0: LONG length = aLength - totalBytesRead; michael@0: nsresult rv = mResource.ReadAt(aPosition + totalBytesRead, michael@0: reinterpret_cast(readBuffer), michael@0: length, michael@0: &bytesRead); michael@0: if (NS_FAILED(rv)) { michael@0: return E_FAIL; michael@0: } michael@0: totalBytesRead += bytesRead; michael@0: if (bytesRead == 0) { michael@0: break; michael@0: } michael@0: } michael@0: if (totalBytesRead > 0) { michael@0: CriticalSectionAutoEnter lock(*mLock); michael@0: mBytesConsumed += totalBytesRead; michael@0: } michael@0: return (totalBytesRead == aLength) ? S_OK : S_FALSE; michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: OutputPin::Length(LONGLONG* aTotal, LONGLONG* aAvailable) michael@0: { michael@0: HRESULT hr = S_OK; michael@0: int64_t length = mResource.GetLength(); michael@0: if (length == -1) { michael@0: hr = VFW_S_ESTIMATED; michael@0: // Don't have a length. Just lie, it seems to work... michael@0: *aTotal = INT32_MAX; michael@0: } else { michael@0: *aTotal = length; michael@0: } michael@0: if (aAvailable) { michael@0: *aAvailable = mResource.GetCachedDataEnd(); michael@0: } michael@0: michael@0: DIRECTSHOW_LOG("OutputPin::Length() len=%lld avail=%lld", *aTotal, *aAvailable); michael@0: michael@0: return hr; michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: OutputPin::BeginFlush() michael@0: { michael@0: CriticalSectionAutoEnter lock(*mLock); michael@0: mFlushCount++; michael@0: mSignal.Notify(); michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: OutputPin::EndFlush(void) michael@0: { michael@0: CriticalSectionAutoEnter lock(*mLock); michael@0: mFlushCount--; michael@0: return S_OK; michael@0: } michael@0: michael@0: uint32_t michael@0: OutputPin::GetAndResetBytesConsumedCount() michael@0: { michael@0: CriticalSectionAutoEnter lock(*mLock); michael@0: uint32_t bytesConsumed = mBytesConsumed; michael@0: mBytesConsumed = 0; michael@0: return bytesConsumed; michael@0: } michael@0: michael@0: SourceFilter::SourceFilter(const GUID& aMajorType, michael@0: const GUID& aSubType) michael@0: : BaseFilter(L"MozillaDirectShowSource", __uuidof(SourceFilter)) michael@0: { michael@0: MOZ_COUNT_CTOR(SourceFilter); michael@0: mMediaType.majortype = aMajorType; michael@0: mMediaType.subtype = aSubType; michael@0: michael@0: DIRECTSHOW_LOG("SourceFilter Constructor(%s, %s)", michael@0: GetDirectShowGuidName(aMajorType), michael@0: GetDirectShowGuidName(aSubType)); michael@0: } michael@0: michael@0: SourceFilter::~SourceFilter() michael@0: { michael@0: MOZ_COUNT_DTOR(SourceFilter); michael@0: DIRECTSHOW_LOG("SourceFilter Destructor()"); michael@0: } michael@0: michael@0: BasePin* michael@0: SourceFilter::GetPin(int n) michael@0: { michael@0: if (n == 0) { michael@0: NS_ASSERTION(mOutputPin != 0, "GetPin with no pin!"); michael@0: return static_cast(mOutputPin); michael@0: } else { michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: // Get's the media type we're supplying. michael@0: const MediaType* michael@0: SourceFilter::GetMediaType() const michael@0: { michael@0: return &mMediaType; michael@0: } michael@0: michael@0: nsresult michael@0: SourceFilter::Init(MediaResource* aResource, int64_t aMP3Offset) michael@0: { michael@0: DIRECTSHOW_LOG("SourceFilter::Init()"); michael@0: michael@0: mOutputPin = new OutputPin(aResource, michael@0: this, michael@0: mLock, michael@0: aMP3Offset); michael@0: NS_ENSURE_TRUE(mOutputPin != nullptr, NS_ERROR_FAILURE); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint32_t michael@0: SourceFilter::GetAndResetBytesConsumedCount() michael@0: { michael@0: return mOutputPin->GetAndResetBytesConsumedCount(); michael@0: } michael@0: michael@0: michael@0: } // namespace mozilla