1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/directshow/SourceFilter.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,692 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "SourceFilter.h" 1.11 +#include "MediaResource.h" 1.12 +#include "mozilla/RefPtr.h" 1.13 +#include "DirectShowUtils.h" 1.14 +#include "MP3FrameParser.h" 1.15 +#include "prlog.h" 1.16 +#include <algorithm> 1.17 + 1.18 +using namespace mozilla::media; 1.19 + 1.20 +namespace mozilla { 1.21 + 1.22 +// Define to trace what's on... 1.23 +//#define DEBUG_SOURCE_TRACE 1 1.24 + 1.25 +#if defined(PR_LOGGING) && defined (DEBUG_SOURCE_TRACE) 1.26 +PRLogModuleInfo* GetDirectShowLog(); 1.27 +#define DIRECTSHOW_LOG(...) PR_LOG(GetDirectShowLog(), PR_LOG_DEBUG, (__VA_ARGS__)) 1.28 +#else 1.29 +#define DIRECTSHOW_LOG(...) 1.30 +#endif 1.31 + 1.32 +static HRESULT 1.33 +DoGetInterface(IUnknown* aUnknown, void** aInterface) 1.34 +{ 1.35 + if (!aInterface) 1.36 + return E_POINTER; 1.37 + *aInterface = aUnknown; 1.38 + aUnknown->AddRef(); 1.39 + return S_OK; 1.40 +} 1.41 + 1.42 +// Stores details of IAsyncReader::Request(). 1.43 +class ReadRequest { 1.44 +public: 1.45 + 1.46 + ReadRequest(IMediaSample* aSample, 1.47 + DWORD_PTR aDwUser, 1.48 + uint32_t aOffset, 1.49 + uint32_t aCount) 1.50 + : mSample(aSample), 1.51 + mDwUser(aDwUser), 1.52 + mOffset(aOffset), 1.53 + mCount(aCount) 1.54 + { 1.55 + MOZ_COUNT_CTOR(ReadRequest); 1.56 + } 1.57 + 1.58 + ~ReadRequest() { 1.59 + MOZ_COUNT_DTOR(ReadRequest); 1.60 + } 1.61 + 1.62 + RefPtr<IMediaSample> mSample; 1.63 + DWORD_PTR mDwUser; 1.64 + uint32_t mOffset; 1.65 + uint32_t mCount; 1.66 +}; 1.67 + 1.68 +// A wrapper around media resource that presents only a partition of the 1.69 +// underlying resource to the caller to use. The partition returned is from 1.70 +// an offset to the end of stream, and this object deals with ensuring 1.71 +// the offsets and lengths etc are translated from the reduced partition 1.72 +// exposed to the caller, to the absolute offsets of the underlying stream. 1.73 +class MediaResourcePartition { 1.74 +public: 1.75 + MediaResourcePartition(MediaResource* aResource, 1.76 + int64_t aDataStart) 1.77 + : mResource(aResource), 1.78 + mDataOffset(aDataStart) 1.79 + {} 1.80 + 1.81 + int64_t GetLength() { 1.82 + int64_t len = mResource->GetLength(); 1.83 + if (len == -1) { 1.84 + return len; 1.85 + } 1.86 + return std::max<int64_t>(0, len - mDataOffset); 1.87 + } 1.88 + nsresult ReadAt(int64_t aOffset, char* aBuffer, 1.89 + uint32_t aCount, uint32_t* aBytes) 1.90 + { 1.91 + return mResource->ReadAt(aOffset + mDataOffset, 1.92 + aBuffer, 1.93 + aCount, 1.94 + aBytes); 1.95 + } 1.96 + int64_t GetCachedDataEnd() { 1.97 + int64_t tell = mResource->Tell(); 1.98 + int64_t dataEnd = mResource->GetCachedDataEnd(tell) - mDataOffset; 1.99 + return dataEnd; 1.100 + } 1.101 +private: 1.102 + // MediaResource from which we read data. 1.103 + RefPtr<MediaResource> mResource; 1.104 + int64_t mDataOffset; 1.105 +}; 1.106 + 1.107 + 1.108 +// Output pin for SourceFilter, which implements IAsyncReader, to 1.109 +// allow downstream filters to pull/read data from it. Downstream pins 1.110 +// register to read data using Request(), and asynchronously wait for the 1.111 +// reads to complete using WaitForNext(). They may also synchronously read 1.112 +// using SyncRead(). This class is a delegate (tear off) of 1.113 +// SourceFilter. 1.114 +// 1.115 +// We can expose only a segment of the MediaResource to the filter graph. 1.116 +// This is used to strip off the ID3v2 tags from the stream, as DirectShow 1.117 +// has trouble parsing some headers. 1.118 +// 1.119 +// Implements: 1.120 +// * IAsyncReader 1.121 +// * IPin 1.122 +// * IQualityControl 1.123 +// * IUnknown 1.124 +// 1.125 +class DECLSPEC_UUID("18e5cfb2-1015-440c-a65c-e63853235894") 1.126 +OutputPin : public IAsyncReader, 1.127 + public BasePin 1.128 +{ 1.129 +public: 1.130 + 1.131 + OutputPin(MediaResource* aMediaResource, 1.132 + SourceFilter* aParent, 1.133 + CriticalSection& aFilterLock, 1.134 + int64_t aMP3DataStart); 1.135 + virtual ~OutputPin(); 1.136 + 1.137 + // IUnknown 1.138 + // Defer to ref counting to BasePin, which defers to owning nsBaseFilter. 1.139 + STDMETHODIMP_(ULONG) AddRef() MOZ_OVERRIDE { return BasePin::AddRef(); } 1.140 + STDMETHODIMP_(ULONG) Release() MOZ_OVERRIDE { return BasePin::Release(); } 1.141 + STDMETHODIMP QueryInterface(REFIID iid, void** ppv) MOZ_OVERRIDE; 1.142 + 1.143 + // BasePin Overrides. 1.144 + // Determines if the pin accepts a specific media type. 1.145 + HRESULT CheckMediaType(const MediaType* aMediaType) MOZ_OVERRIDE; 1.146 + 1.147 + // Retrieves a preferred media type, by index value. 1.148 + HRESULT GetMediaType(int aPosition, MediaType* aMediaType) MOZ_OVERRIDE; 1.149 + 1.150 + // Releases the pin from a connection. 1.151 + HRESULT BreakConnect(void) MOZ_OVERRIDE; 1.152 + 1.153 + // Determines whether a pin connection is suitable. 1.154 + HRESULT CheckConnect(IPin* aPin) MOZ_OVERRIDE; 1.155 + 1.156 + 1.157 + // IAsyncReader overrides 1.158 + 1.159 + // The RequestAllocator method requests an allocator during the 1.160 + // pin connection. 1.161 + STDMETHODIMP RequestAllocator(IMemAllocator* aPreferred, 1.162 + ALLOCATOR_PROPERTIES* aProps, 1.163 + IMemAllocator** aActual) MOZ_OVERRIDE; 1.164 + 1.165 + // The Request method queues an asynchronous request for data. Downstream 1.166 + // will call WaitForNext() when they want to retrieve the result. 1.167 + STDMETHODIMP Request(IMediaSample* aSample, DWORD_PTR aUserData) MOZ_OVERRIDE; 1.168 + 1.169 + // The WaitForNext method waits for the next pending read request 1.170 + // to complete. This method fails if the graph is flushing. 1.171 + // Defers to SyncRead/5. 1.172 + STDMETHODIMP WaitForNext(DWORD aTimeout, 1.173 + IMediaSample** aSamples, 1.174 + DWORD_PTR* aUserData) MOZ_OVERRIDE; 1.175 + 1.176 + // The SyncReadAligned method performs a synchronous read. The method 1.177 + // blocks until the request is completed. Defers to SyncRead/5. This 1.178 + // method does not fail if the graph is flushing. 1.179 + STDMETHODIMP SyncReadAligned(IMediaSample* aSample) MOZ_OVERRIDE; 1.180 + 1.181 + // The SyncRead method performs a synchronous read. The method blocks 1.182 + // until the request is completed. Defers to SyncRead/5. This 1.183 + // method does not fail if the graph is flushing. 1.184 + STDMETHODIMP SyncRead(LONGLONG aPosition, LONG aLength, BYTE* aBuffer) MOZ_OVERRIDE; 1.185 + 1.186 + // The Length method retrieves the total length of the stream. 1.187 + STDMETHODIMP Length(LONGLONG* aTotal, LONGLONG* aAvailable) MOZ_OVERRIDE; 1.188 + 1.189 + // IPin Overrides 1.190 + STDMETHODIMP BeginFlush(void) MOZ_OVERRIDE; 1.191 + STDMETHODIMP EndFlush(void) MOZ_OVERRIDE; 1.192 + 1.193 + uint32_t GetAndResetBytesConsumedCount(); 1.194 + 1.195 +private: 1.196 + 1.197 + // Protects thread-shared data/structures (mFlushCount, mPendingReads). 1.198 + // WaitForNext() also waits on this monitor 1.199 + CriticalSection& mPinLock; 1.200 + 1.201 + // Signal used with mPinLock to implement WaitForNext(). 1.202 + Signal mSignal; 1.203 + 1.204 + // The filter that owns us. Weak reference, as we're a delegate (tear off). 1.205 + SourceFilter* mParentSource; 1.206 + 1.207 + MediaResourcePartition mResource; 1.208 + 1.209 + // Counter, inc'd in BeginFlush(), dec'd in EndFlush(). Calls to this can 1.210 + // come from multiple threads and can interleave, hence the counter. 1.211 + int32_t mFlushCount; 1.212 + 1.213 + // Number of bytes that have been read from the output pin since the last 1.214 + // time GetAndResetBytesConsumedCount() was called. 1.215 + uint32_t mBytesConsumed; 1.216 + 1.217 + // Deque of ReadRequest* for reads that are yet to be serviced. 1.218 + // nsReadRequest's are stored on the heap, popper must delete them. 1.219 + nsDeque mPendingReads; 1.220 + 1.221 + // Flags if the downstream pin has QI'd for IAsyncReader. We refuse 1.222 + // connection if they don't query, as it means they're assuming that we're 1.223 + // a push filter, and we're not. 1.224 + bool mQueriedForAsyncReader; 1.225 + 1.226 +}; 1.227 + 1.228 +// For mingw __uuidof support 1.229 +#ifdef __CRT_UUID_DECL 1.230 +} 1.231 +__CRT_UUID_DECL(mozilla::OutputPin, 0x18e5cfb2,0x1015,0x440c,0xa6,0x5c,0xe6,0x38,0x53,0x23,0x58,0x94); 1.232 +namespace mozilla { 1.233 +#endif 1.234 + 1.235 +OutputPin::OutputPin(MediaResource* aResource, 1.236 + SourceFilter* aParent, 1.237 + CriticalSection& aFilterLock, 1.238 + int64_t aMP3DataStart) 1.239 + : BasePin(static_cast<BaseFilter*>(aParent), 1.240 + &aFilterLock, 1.241 + L"MozillaOutputPin", 1.242 + PINDIR_OUTPUT), 1.243 + mPinLock(aFilterLock), 1.244 + mSignal(&mPinLock), 1.245 + mParentSource(aParent), 1.246 + mResource(aResource, aMP3DataStart), 1.247 + mFlushCount(0), 1.248 + mBytesConsumed(0), 1.249 + mQueriedForAsyncReader(false) 1.250 +{ 1.251 + MOZ_COUNT_CTOR(OutputPin); 1.252 + DIRECTSHOW_LOG("OutputPin::OutputPin()"); 1.253 +} 1.254 + 1.255 +OutputPin::~OutputPin() 1.256 +{ 1.257 + MOZ_COUNT_DTOR(OutputPin); 1.258 + DIRECTSHOW_LOG("OutputPin::~OutputPin()"); 1.259 +} 1.260 + 1.261 +HRESULT 1.262 +OutputPin::BreakConnect() 1.263 +{ 1.264 + mQueriedForAsyncReader = false; 1.265 + return BasePin::BreakConnect(); 1.266 +} 1.267 + 1.268 +STDMETHODIMP 1.269 +OutputPin::QueryInterface(REFIID aIId, void** aInterface) 1.270 +{ 1.271 + if (aIId == IID_IAsyncReader) { 1.272 + mQueriedForAsyncReader = true; 1.273 + return DoGetInterface(static_cast<IAsyncReader*>(this), aInterface); 1.274 + } 1.275 + 1.276 + if (aIId == __uuidof(OutputPin)) { 1.277 + AddRef(); 1.278 + *aInterface = this; 1.279 + return S_OK; 1.280 + } 1.281 + 1.282 + return BasePin::QueryInterface(aIId, aInterface); 1.283 +} 1.284 + 1.285 +HRESULT 1.286 +OutputPin::CheckConnect(IPin* aPin) 1.287 +{ 1.288 + // Our connection is only suitable if the downstream pin knows 1.289 + // that we're asynchronous (i.e. it queried for IAsyncReader). 1.290 + return mQueriedForAsyncReader ? S_OK : S_FALSE; 1.291 +} 1.292 + 1.293 +HRESULT 1.294 +OutputPin::CheckMediaType(const MediaType* aMediaType) 1.295 +{ 1.296 + const MediaType *myMediaType = mParentSource->GetMediaType(); 1.297 + 1.298 + if (IsEqualGUID(aMediaType->majortype, myMediaType->majortype) && 1.299 + IsEqualGUID(aMediaType->subtype, myMediaType->subtype) && 1.300 + IsEqualGUID(aMediaType->formattype, myMediaType->formattype)) 1.301 + { 1.302 + DIRECTSHOW_LOG("OutputPin::CheckMediaType() Match: major=%s minor=%s TC=%d FSS=%d SS=%u", 1.303 + GetDirectShowGuidName(aMediaType->majortype), 1.304 + GetDirectShowGuidName(aMediaType->subtype), 1.305 + aMediaType->TemporalCompression(), 1.306 + aMediaType->bFixedSizeSamples, 1.307 + aMediaType->SampleSize()); 1.308 + return S_OK; 1.309 + } 1.310 + 1.311 + DIRECTSHOW_LOG("OutputPin::CheckMediaType() Failed to match: major=%s minor=%s TC=%d FSS=%d SS=%u", 1.312 + GetDirectShowGuidName(aMediaType->majortype), 1.313 + GetDirectShowGuidName(aMediaType->subtype), 1.314 + aMediaType->TemporalCompression(), 1.315 + aMediaType->bFixedSizeSamples, 1.316 + aMediaType->SampleSize()); 1.317 + return S_FALSE; 1.318 +} 1.319 + 1.320 +HRESULT 1.321 +OutputPin::GetMediaType(int aPosition, MediaType* aMediaType) 1.322 +{ 1.323 + if (!aMediaType) 1.324 + return E_POINTER; 1.325 + 1.326 + if (aPosition == 0) { 1.327 + aMediaType->Assign(mParentSource->GetMediaType()); 1.328 + return S_OK; 1.329 + } 1.330 + return VFW_S_NO_MORE_ITEMS; 1.331 +} 1.332 + 1.333 +static inline bool 1.334 +IsPowerOf2(int32_t x) { 1.335 + return ((-x & x) != x); 1.336 +} 1.337 + 1.338 +STDMETHODIMP 1.339 +OutputPin::RequestAllocator(IMemAllocator* aPreferred, 1.340 + ALLOCATOR_PROPERTIES* aProps, 1.341 + IMemAllocator** aActual) 1.342 +{ 1.343 + // Require the downstream pin to suggest what they want... 1.344 + if (!aPreferred) return E_POINTER; 1.345 + if (!aProps) return E_POINTER; 1.346 + if (!aActual) return E_POINTER; 1.347 + 1.348 + // We only care about alignment - our allocator will reject anything 1.349 + // which isn't power-of-2 aligned, so so try a 4-byte aligned allocator. 1.350 + ALLOCATOR_PROPERTIES props; 1.351 + memcpy(&props, aProps, sizeof(ALLOCATOR_PROPERTIES)); 1.352 + if (aProps->cbAlign == 0 || IsPowerOf2(aProps->cbAlign)) { 1.353 + props.cbAlign = 4; 1.354 + } 1.355 + 1.356 + // Limit allocator's number of buffers. We know that the media will most 1.357 + // likely be bound by network speed, not by decoding speed. We also 1.358 + // store the incoming data in a Gecko stream, if we don't limit buffers 1.359 + // here we'll end up duplicating a lot of storage. We must have enough 1.360 + // space for audio key frames to fit in the first batch of buffers however, 1.361 + // else pausing may fail for some downstream decoders. 1.362 + if (props.cBuffers > BaseFilter::sMaxNumBuffers) { 1.363 + props.cBuffers = BaseFilter::sMaxNumBuffers; 1.364 + } 1.365 + 1.366 + // The allocator properties that are actually used. We don't store 1.367 + // this, we need it for SetProperties() below to succeed. 1.368 + ALLOCATOR_PROPERTIES actualProps; 1.369 + HRESULT hr; 1.370 + 1.371 + if (aPreferred) { 1.372 + // Play nice and prefer the downstream pin's preferred allocator. 1.373 + hr = aPreferred->SetProperties(&props, &actualProps); 1.374 + if (SUCCEEDED(hr)) { 1.375 + aPreferred->AddRef(); 1.376 + *aActual = aPreferred; 1.377 + return S_OK; 1.378 + } 1.379 + } 1.380 + 1.381 + // Else downstream hasn't requested a specific allocator, so create one... 1.382 + 1.383 + // Just create a default allocator. It's highly unlikely that we'll use 1.384 + // this anyway, as most parsers insist on using their own allocators. 1.385 + nsRefPtr<IMemAllocator> allocator; 1.386 + hr = CoCreateInstance(CLSID_MemoryAllocator, 1.387 + 0, 1.388 + CLSCTX_INPROC_SERVER, 1.389 + IID_IMemAllocator, 1.390 + getter_AddRefs(allocator)); 1.391 + if(FAILED(hr) || (allocator == nullptr)) { 1.392 + NS_WARNING("Can't create our own DirectShow allocator."); 1.393 + return hr; 1.394 + } 1.395 + 1.396 + // See if we can make it suitable 1.397 + hr = allocator->SetProperties(&props, &actualProps); 1.398 + if (SUCCEEDED(hr)) { 1.399 + // We need to release our refcount on pAlloc, and addref 1.400 + // it to pass a refcount to the caller - this is a net nothing. 1.401 + allocator.forget(aActual); 1.402 + return S_OK; 1.403 + } 1.404 + 1.405 + NS_WARNING("Failed to pick an allocator"); 1.406 + return hr; 1.407 +} 1.408 + 1.409 +STDMETHODIMP 1.410 +OutputPin::Request(IMediaSample* aSample, DWORD_PTR aDwUser) 1.411 +{ 1.412 + if (!aSample) return E_FAIL; 1.413 + 1.414 + CriticalSectionAutoEnter lock(*mLock); 1.415 + NS_ASSERTION(!mFlushCount, "Request() while flushing"); 1.416 + 1.417 + if (mFlushCount) 1.418 + return VFW_E_WRONG_STATE; 1.419 + 1.420 + REFERENCE_TIME refStart = 0, refEnd = 0; 1.421 + if (FAILED(aSample->GetTime(&refStart, &refEnd))) { 1.422 + NS_WARNING("Sample incorrectly timestamped"); 1.423 + return VFW_E_SAMPLE_TIME_NOT_SET; 1.424 + } 1.425 + 1.426 + // Convert reference time to bytes. 1.427 + uint32_t start = (uint32_t)(refStart / 10000000); 1.428 + uint32_t end = (uint32_t)(refEnd / 10000000); 1.429 + 1.430 + uint32_t numBytes = end - start; 1.431 + 1.432 + ReadRequest* request = new ReadRequest(aSample, 1.433 + aDwUser, 1.434 + start, 1.435 + numBytes); 1.436 + // Memory for |request| is free when it's popped from the completed 1.437 + // reads list. 1.438 + 1.439 + // Push this onto the queue of reads to be serviced. 1.440 + mPendingReads.Push(request); 1.441 + 1.442 + // Notify any threads blocked in WaitForNext() which are waiting for mPendingReads 1.443 + // to become non-empty. 1.444 + mSignal.Notify(); 1.445 + 1.446 + return S_OK; 1.447 +} 1.448 + 1.449 +STDMETHODIMP 1.450 +OutputPin::WaitForNext(DWORD aTimeout, 1.451 + IMediaSample** aOutSample, 1.452 + DWORD_PTR* aOutDwUser) 1.453 +{ 1.454 + NS_ASSERTION(aTimeout == 0 || aTimeout == INFINITE, 1.455 + "Oops, we don't handle this!"); 1.456 + 1.457 + *aOutSample = nullptr; 1.458 + *aOutDwUser = 0; 1.459 + 1.460 + LONGLONG offset = 0; 1.461 + LONG count = 0; 1.462 + BYTE* buf = nullptr; 1.463 + 1.464 + { 1.465 + CriticalSectionAutoEnter lock(*mLock); 1.466 + 1.467 + // Wait until there's a pending read to service. 1.468 + while (aTimeout && mPendingReads.GetSize() == 0 && !mFlushCount) { 1.469 + // Note: No need to guard against shutdown-during-wait here, as 1.470 + // typically the thread doing the pull will have already called 1.471 + // Request(), so we won't Wait() here anyway. SyncRead() will fail 1.472 + // on shutdown. 1.473 + mSignal.Wait(); 1.474 + } 1.475 + 1.476 + nsAutoPtr<ReadRequest> request(reinterpret_cast<ReadRequest*>(mPendingReads.PopFront())); 1.477 + if (!request) 1.478 + return VFW_E_WRONG_STATE; 1.479 + 1.480 + *aOutSample = request->mSample; 1.481 + *aOutDwUser = request->mDwUser; 1.482 + 1.483 + offset = request->mOffset; 1.484 + count = request->mCount; 1.485 + buf = nullptr; 1.486 + request->mSample->GetPointer(&buf); 1.487 + NS_ASSERTION(buf != nullptr, "Invalid buffer!"); 1.488 + 1.489 + if (mFlushCount) { 1.490 + return VFW_E_TIMEOUT; 1.491 + } 1.492 + } 1.493 + 1.494 + return SyncRead(offset, count, buf); 1.495 +} 1.496 + 1.497 +STDMETHODIMP 1.498 +OutputPin::SyncReadAligned(IMediaSample* aSample) 1.499 +{ 1.500 + { 1.501 + // Ignore reads while flushing. 1.502 + CriticalSectionAutoEnter lock(*mLock); 1.503 + if (mFlushCount) { 1.504 + return S_FALSE; 1.505 + } 1.506 + } 1.507 + 1.508 + if (!aSample) 1.509 + return E_FAIL; 1.510 + 1.511 + REFERENCE_TIME lStart = 0, lEnd = 0; 1.512 + if (FAILED(aSample->GetTime(&lStart, &lEnd))) { 1.513 + NS_WARNING("Sample incorrectly timestamped"); 1.514 + return VFW_E_SAMPLE_TIME_NOT_SET; 1.515 + } 1.516 + 1.517 + // Convert reference time to bytes. 1.518 + int32_t start = (int32_t)(lStart / 10000000); 1.519 + int32_t end = (int32_t)(lEnd / 10000000); 1.520 + 1.521 + int32_t numBytes = end - start; 1.522 + 1.523 + // If the range extends off the end of stream, truncate to the end of stream 1.524 + // as per IAsyncReader specificiation. 1.525 + int64_t streamLength = mResource.GetLength(); 1.526 + if (streamLength != -1) { 1.527 + // We know the exact length of the stream, fail if the requested offset 1.528 + // is beyond it. 1.529 + if (start > streamLength) { 1.530 + return VFW_E_BADALIGN; 1.531 + } 1.532 + 1.533 + // If the end of the chunk to read is off the end of the stream, 1.534 + // truncate it to the end of the stream. 1.535 + if ((start + numBytes) > streamLength) { 1.536 + numBytes = (uint32_t)(streamLength - start); 1.537 + } 1.538 + } 1.539 + 1.540 + BYTE* buf=0; 1.541 + aSample->GetPointer(&buf); 1.542 + 1.543 + return SyncRead(start, numBytes, buf); 1.544 +} 1.545 + 1.546 +STDMETHODIMP 1.547 +OutputPin::SyncRead(LONGLONG aPosition, 1.548 + LONG aLength, 1.549 + BYTE* aBuffer) 1.550 +{ 1.551 + MOZ_ASSERT(!NS_IsMainThread()); 1.552 + NS_ENSURE_TRUE(aPosition >= 0, E_FAIL); 1.553 + NS_ENSURE_TRUE(aLength > 0, E_FAIL); 1.554 + NS_ENSURE_TRUE(aBuffer, E_POINTER); 1.555 + 1.556 + DIRECTSHOW_LOG("OutputPin::SyncRead(%lld, %d)", aPosition, aLength); 1.557 + { 1.558 + // Ignore reads while flushing. 1.559 + CriticalSectionAutoEnter lock(*mLock); 1.560 + if (mFlushCount) { 1.561 + return S_FALSE; 1.562 + } 1.563 + } 1.564 + 1.565 + // Read in a loop to ensure we fill the buffer, when possible. 1.566 + LONG totalBytesRead = 0; 1.567 + while (totalBytesRead < aLength) { 1.568 + BYTE* readBuffer = aBuffer + totalBytesRead; 1.569 + uint32_t bytesRead = 0; 1.570 + LONG length = aLength - totalBytesRead; 1.571 + nsresult rv = mResource.ReadAt(aPosition + totalBytesRead, 1.572 + reinterpret_cast<char*>(readBuffer), 1.573 + length, 1.574 + &bytesRead); 1.575 + if (NS_FAILED(rv)) { 1.576 + return E_FAIL; 1.577 + } 1.578 + totalBytesRead += bytesRead; 1.579 + if (bytesRead == 0) { 1.580 + break; 1.581 + } 1.582 + } 1.583 + if (totalBytesRead > 0) { 1.584 + CriticalSectionAutoEnter lock(*mLock); 1.585 + mBytesConsumed += totalBytesRead; 1.586 + } 1.587 + return (totalBytesRead == aLength) ? S_OK : S_FALSE; 1.588 +} 1.589 + 1.590 +STDMETHODIMP 1.591 +OutputPin::Length(LONGLONG* aTotal, LONGLONG* aAvailable) 1.592 +{ 1.593 + HRESULT hr = S_OK; 1.594 + int64_t length = mResource.GetLength(); 1.595 + if (length == -1) { 1.596 + hr = VFW_S_ESTIMATED; 1.597 + // Don't have a length. Just lie, it seems to work... 1.598 + *aTotal = INT32_MAX; 1.599 + } else { 1.600 + *aTotal = length; 1.601 + } 1.602 + if (aAvailable) { 1.603 + *aAvailable = mResource.GetCachedDataEnd(); 1.604 + } 1.605 + 1.606 + DIRECTSHOW_LOG("OutputPin::Length() len=%lld avail=%lld", *aTotal, *aAvailable); 1.607 + 1.608 + return hr; 1.609 +} 1.610 + 1.611 +STDMETHODIMP 1.612 +OutputPin::BeginFlush() 1.613 +{ 1.614 + CriticalSectionAutoEnter lock(*mLock); 1.615 + mFlushCount++; 1.616 + mSignal.Notify(); 1.617 + return S_OK; 1.618 +} 1.619 + 1.620 +STDMETHODIMP 1.621 +OutputPin::EndFlush(void) 1.622 +{ 1.623 + CriticalSectionAutoEnter lock(*mLock); 1.624 + mFlushCount--; 1.625 + return S_OK; 1.626 +} 1.627 + 1.628 +uint32_t 1.629 +OutputPin::GetAndResetBytesConsumedCount() 1.630 +{ 1.631 + CriticalSectionAutoEnter lock(*mLock); 1.632 + uint32_t bytesConsumed = mBytesConsumed; 1.633 + mBytesConsumed = 0; 1.634 + return bytesConsumed; 1.635 +} 1.636 + 1.637 +SourceFilter::SourceFilter(const GUID& aMajorType, 1.638 + const GUID& aSubType) 1.639 + : BaseFilter(L"MozillaDirectShowSource", __uuidof(SourceFilter)) 1.640 +{ 1.641 + MOZ_COUNT_CTOR(SourceFilter); 1.642 + mMediaType.majortype = aMajorType; 1.643 + mMediaType.subtype = aSubType; 1.644 + 1.645 + DIRECTSHOW_LOG("SourceFilter Constructor(%s, %s)", 1.646 + GetDirectShowGuidName(aMajorType), 1.647 + GetDirectShowGuidName(aSubType)); 1.648 +} 1.649 + 1.650 +SourceFilter::~SourceFilter() 1.651 +{ 1.652 + MOZ_COUNT_DTOR(SourceFilter); 1.653 + DIRECTSHOW_LOG("SourceFilter Destructor()"); 1.654 +} 1.655 + 1.656 +BasePin* 1.657 +SourceFilter::GetPin(int n) 1.658 +{ 1.659 + if (n == 0) { 1.660 + NS_ASSERTION(mOutputPin != 0, "GetPin with no pin!"); 1.661 + return static_cast<BasePin*>(mOutputPin); 1.662 + } else { 1.663 + return nullptr; 1.664 + } 1.665 +} 1.666 + 1.667 +// Get's the media type we're supplying. 1.668 +const MediaType* 1.669 +SourceFilter::GetMediaType() const 1.670 +{ 1.671 + return &mMediaType; 1.672 +} 1.673 + 1.674 +nsresult 1.675 +SourceFilter::Init(MediaResource* aResource, int64_t aMP3Offset) 1.676 +{ 1.677 + DIRECTSHOW_LOG("SourceFilter::Init()"); 1.678 + 1.679 + mOutputPin = new OutputPin(aResource, 1.680 + this, 1.681 + mLock, 1.682 + aMP3Offset); 1.683 + NS_ENSURE_TRUE(mOutputPin != nullptr, NS_ERROR_FAILURE); 1.684 + 1.685 + return NS_OK; 1.686 +} 1.687 + 1.688 +uint32_t 1.689 +SourceFilter::GetAndResetBytesConsumedCount() 1.690 +{ 1.691 + return mOutputPin->GetAndResetBytesConsumedCount(); 1.692 +} 1.693 + 1.694 + 1.695 +} // namespace mozilla