content/media/directshow/SourceFilter.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "SourceFilter.h"
michael@0 8 #include "MediaResource.h"
michael@0 9 #include "mozilla/RefPtr.h"
michael@0 10 #include "DirectShowUtils.h"
michael@0 11 #include "MP3FrameParser.h"
michael@0 12 #include "prlog.h"
michael@0 13 #include <algorithm>
michael@0 14
michael@0 15 using namespace mozilla::media;
michael@0 16
michael@0 17 namespace mozilla {
michael@0 18
michael@0 19 // Define to trace what's on...
michael@0 20 //#define DEBUG_SOURCE_TRACE 1
michael@0 21
michael@0 22 #if defined(PR_LOGGING) && defined (DEBUG_SOURCE_TRACE)
michael@0 23 PRLogModuleInfo* GetDirectShowLog();
michael@0 24 #define DIRECTSHOW_LOG(...) PR_LOG(GetDirectShowLog(), PR_LOG_DEBUG, (__VA_ARGS__))
michael@0 25 #else
michael@0 26 #define DIRECTSHOW_LOG(...)
michael@0 27 #endif
michael@0 28
michael@0 29 static HRESULT
michael@0 30 DoGetInterface(IUnknown* aUnknown, void** aInterface)
michael@0 31 {
michael@0 32 if (!aInterface)
michael@0 33 return E_POINTER;
michael@0 34 *aInterface = aUnknown;
michael@0 35 aUnknown->AddRef();
michael@0 36 return S_OK;
michael@0 37 }
michael@0 38
michael@0 39 // Stores details of IAsyncReader::Request().
michael@0 40 class ReadRequest {
michael@0 41 public:
michael@0 42
michael@0 43 ReadRequest(IMediaSample* aSample,
michael@0 44 DWORD_PTR aDwUser,
michael@0 45 uint32_t aOffset,
michael@0 46 uint32_t aCount)
michael@0 47 : mSample(aSample),
michael@0 48 mDwUser(aDwUser),
michael@0 49 mOffset(aOffset),
michael@0 50 mCount(aCount)
michael@0 51 {
michael@0 52 MOZ_COUNT_CTOR(ReadRequest);
michael@0 53 }
michael@0 54
michael@0 55 ~ReadRequest() {
michael@0 56 MOZ_COUNT_DTOR(ReadRequest);
michael@0 57 }
michael@0 58
michael@0 59 RefPtr<IMediaSample> mSample;
michael@0 60 DWORD_PTR mDwUser;
michael@0 61 uint32_t mOffset;
michael@0 62 uint32_t mCount;
michael@0 63 };
michael@0 64
michael@0 65 // A wrapper around media resource that presents only a partition of the
michael@0 66 // underlying resource to the caller to use. The partition returned is from
michael@0 67 // an offset to the end of stream, and this object deals with ensuring
michael@0 68 // the offsets and lengths etc are translated from the reduced partition
michael@0 69 // exposed to the caller, to the absolute offsets of the underlying stream.
michael@0 70 class MediaResourcePartition {
michael@0 71 public:
michael@0 72 MediaResourcePartition(MediaResource* aResource,
michael@0 73 int64_t aDataStart)
michael@0 74 : mResource(aResource),
michael@0 75 mDataOffset(aDataStart)
michael@0 76 {}
michael@0 77
michael@0 78 int64_t GetLength() {
michael@0 79 int64_t len = mResource->GetLength();
michael@0 80 if (len == -1) {
michael@0 81 return len;
michael@0 82 }
michael@0 83 return std::max<int64_t>(0, len - mDataOffset);
michael@0 84 }
michael@0 85 nsresult ReadAt(int64_t aOffset, char* aBuffer,
michael@0 86 uint32_t aCount, uint32_t* aBytes)
michael@0 87 {
michael@0 88 return mResource->ReadAt(aOffset + mDataOffset,
michael@0 89 aBuffer,
michael@0 90 aCount,
michael@0 91 aBytes);
michael@0 92 }
michael@0 93 int64_t GetCachedDataEnd() {
michael@0 94 int64_t tell = mResource->Tell();
michael@0 95 int64_t dataEnd = mResource->GetCachedDataEnd(tell) - mDataOffset;
michael@0 96 return dataEnd;
michael@0 97 }
michael@0 98 private:
michael@0 99 // MediaResource from which we read data.
michael@0 100 RefPtr<MediaResource> mResource;
michael@0 101 int64_t mDataOffset;
michael@0 102 };
michael@0 103
michael@0 104
michael@0 105 // Output pin for SourceFilter, which implements IAsyncReader, to
michael@0 106 // allow downstream filters to pull/read data from it. Downstream pins
michael@0 107 // register to read data using Request(), and asynchronously wait for the
michael@0 108 // reads to complete using WaitForNext(). They may also synchronously read
michael@0 109 // using SyncRead(). This class is a delegate (tear off) of
michael@0 110 // SourceFilter.
michael@0 111 //
michael@0 112 // We can expose only a segment of the MediaResource to the filter graph.
michael@0 113 // This is used to strip off the ID3v2 tags from the stream, as DirectShow
michael@0 114 // has trouble parsing some headers.
michael@0 115 //
michael@0 116 // Implements:
michael@0 117 // * IAsyncReader
michael@0 118 // * IPin
michael@0 119 // * IQualityControl
michael@0 120 // * IUnknown
michael@0 121 //
michael@0 122 class DECLSPEC_UUID("18e5cfb2-1015-440c-a65c-e63853235894")
michael@0 123 OutputPin : public IAsyncReader,
michael@0 124 public BasePin
michael@0 125 {
michael@0 126 public:
michael@0 127
michael@0 128 OutputPin(MediaResource* aMediaResource,
michael@0 129 SourceFilter* aParent,
michael@0 130 CriticalSection& aFilterLock,
michael@0 131 int64_t aMP3DataStart);
michael@0 132 virtual ~OutputPin();
michael@0 133
michael@0 134 // IUnknown
michael@0 135 // Defer to ref counting to BasePin, which defers to owning nsBaseFilter.
michael@0 136 STDMETHODIMP_(ULONG) AddRef() MOZ_OVERRIDE { return BasePin::AddRef(); }
michael@0 137 STDMETHODIMP_(ULONG) Release() MOZ_OVERRIDE { return BasePin::Release(); }
michael@0 138 STDMETHODIMP QueryInterface(REFIID iid, void** ppv) MOZ_OVERRIDE;
michael@0 139
michael@0 140 // BasePin Overrides.
michael@0 141 // Determines if the pin accepts a specific media type.
michael@0 142 HRESULT CheckMediaType(const MediaType* aMediaType) MOZ_OVERRIDE;
michael@0 143
michael@0 144 // Retrieves a preferred media type, by index value.
michael@0 145 HRESULT GetMediaType(int aPosition, MediaType* aMediaType) MOZ_OVERRIDE;
michael@0 146
michael@0 147 // Releases the pin from a connection.
michael@0 148 HRESULT BreakConnect(void) MOZ_OVERRIDE;
michael@0 149
michael@0 150 // Determines whether a pin connection is suitable.
michael@0 151 HRESULT CheckConnect(IPin* aPin) MOZ_OVERRIDE;
michael@0 152
michael@0 153
michael@0 154 // IAsyncReader overrides
michael@0 155
michael@0 156 // The RequestAllocator method requests an allocator during the
michael@0 157 // pin connection.
michael@0 158 STDMETHODIMP RequestAllocator(IMemAllocator* aPreferred,
michael@0 159 ALLOCATOR_PROPERTIES* aProps,
michael@0 160 IMemAllocator** aActual) MOZ_OVERRIDE;
michael@0 161
michael@0 162 // The Request method queues an asynchronous request for data. Downstream
michael@0 163 // will call WaitForNext() when they want to retrieve the result.
michael@0 164 STDMETHODIMP Request(IMediaSample* aSample, DWORD_PTR aUserData) MOZ_OVERRIDE;
michael@0 165
michael@0 166 // The WaitForNext method waits for the next pending read request
michael@0 167 // to complete. This method fails if the graph is flushing.
michael@0 168 // Defers to SyncRead/5.
michael@0 169 STDMETHODIMP WaitForNext(DWORD aTimeout,
michael@0 170 IMediaSample** aSamples,
michael@0 171 DWORD_PTR* aUserData) MOZ_OVERRIDE;
michael@0 172
michael@0 173 // The SyncReadAligned method performs a synchronous read. The method
michael@0 174 // blocks until the request is completed. Defers to SyncRead/5. This
michael@0 175 // method does not fail if the graph is flushing.
michael@0 176 STDMETHODIMP SyncReadAligned(IMediaSample* aSample) MOZ_OVERRIDE;
michael@0 177
michael@0 178 // The SyncRead method performs a synchronous read. The method blocks
michael@0 179 // until the request is completed. Defers to SyncRead/5. This
michael@0 180 // method does not fail if the graph is flushing.
michael@0 181 STDMETHODIMP SyncRead(LONGLONG aPosition, LONG aLength, BYTE* aBuffer) MOZ_OVERRIDE;
michael@0 182
michael@0 183 // The Length method retrieves the total length of the stream.
michael@0 184 STDMETHODIMP Length(LONGLONG* aTotal, LONGLONG* aAvailable) MOZ_OVERRIDE;
michael@0 185
michael@0 186 // IPin Overrides
michael@0 187 STDMETHODIMP BeginFlush(void) MOZ_OVERRIDE;
michael@0 188 STDMETHODIMP EndFlush(void) MOZ_OVERRIDE;
michael@0 189
michael@0 190 uint32_t GetAndResetBytesConsumedCount();
michael@0 191
michael@0 192 private:
michael@0 193
michael@0 194 // Protects thread-shared data/structures (mFlushCount, mPendingReads).
michael@0 195 // WaitForNext() also waits on this monitor
michael@0 196 CriticalSection& mPinLock;
michael@0 197
michael@0 198 // Signal used with mPinLock to implement WaitForNext().
michael@0 199 Signal mSignal;
michael@0 200
michael@0 201 // The filter that owns us. Weak reference, as we're a delegate (tear off).
michael@0 202 SourceFilter* mParentSource;
michael@0 203
michael@0 204 MediaResourcePartition mResource;
michael@0 205
michael@0 206 // Counter, inc'd in BeginFlush(), dec'd in EndFlush(). Calls to this can
michael@0 207 // come from multiple threads and can interleave, hence the counter.
michael@0 208 int32_t mFlushCount;
michael@0 209
michael@0 210 // Number of bytes that have been read from the output pin since the last
michael@0 211 // time GetAndResetBytesConsumedCount() was called.
michael@0 212 uint32_t mBytesConsumed;
michael@0 213
michael@0 214 // Deque of ReadRequest* for reads that are yet to be serviced.
michael@0 215 // nsReadRequest's are stored on the heap, popper must delete them.
michael@0 216 nsDeque mPendingReads;
michael@0 217
michael@0 218 // Flags if the downstream pin has QI'd for IAsyncReader. We refuse
michael@0 219 // connection if they don't query, as it means they're assuming that we're
michael@0 220 // a push filter, and we're not.
michael@0 221 bool mQueriedForAsyncReader;
michael@0 222
michael@0 223 };
michael@0 224
michael@0 225 // For mingw __uuidof support
michael@0 226 #ifdef __CRT_UUID_DECL
michael@0 227 }
michael@0 228 __CRT_UUID_DECL(mozilla::OutputPin, 0x18e5cfb2,0x1015,0x440c,0xa6,0x5c,0xe6,0x38,0x53,0x23,0x58,0x94);
michael@0 229 namespace mozilla {
michael@0 230 #endif
michael@0 231
michael@0 232 OutputPin::OutputPin(MediaResource* aResource,
michael@0 233 SourceFilter* aParent,
michael@0 234 CriticalSection& aFilterLock,
michael@0 235 int64_t aMP3DataStart)
michael@0 236 : BasePin(static_cast<BaseFilter*>(aParent),
michael@0 237 &aFilterLock,
michael@0 238 L"MozillaOutputPin",
michael@0 239 PINDIR_OUTPUT),
michael@0 240 mPinLock(aFilterLock),
michael@0 241 mSignal(&mPinLock),
michael@0 242 mParentSource(aParent),
michael@0 243 mResource(aResource, aMP3DataStart),
michael@0 244 mFlushCount(0),
michael@0 245 mBytesConsumed(0),
michael@0 246 mQueriedForAsyncReader(false)
michael@0 247 {
michael@0 248 MOZ_COUNT_CTOR(OutputPin);
michael@0 249 DIRECTSHOW_LOG("OutputPin::OutputPin()");
michael@0 250 }
michael@0 251
michael@0 252 OutputPin::~OutputPin()
michael@0 253 {
michael@0 254 MOZ_COUNT_DTOR(OutputPin);
michael@0 255 DIRECTSHOW_LOG("OutputPin::~OutputPin()");
michael@0 256 }
michael@0 257
michael@0 258 HRESULT
michael@0 259 OutputPin::BreakConnect()
michael@0 260 {
michael@0 261 mQueriedForAsyncReader = false;
michael@0 262 return BasePin::BreakConnect();
michael@0 263 }
michael@0 264
michael@0 265 STDMETHODIMP
michael@0 266 OutputPin::QueryInterface(REFIID aIId, void** aInterface)
michael@0 267 {
michael@0 268 if (aIId == IID_IAsyncReader) {
michael@0 269 mQueriedForAsyncReader = true;
michael@0 270 return DoGetInterface(static_cast<IAsyncReader*>(this), aInterface);
michael@0 271 }
michael@0 272
michael@0 273 if (aIId == __uuidof(OutputPin)) {
michael@0 274 AddRef();
michael@0 275 *aInterface = this;
michael@0 276 return S_OK;
michael@0 277 }
michael@0 278
michael@0 279 return BasePin::QueryInterface(aIId, aInterface);
michael@0 280 }
michael@0 281
michael@0 282 HRESULT
michael@0 283 OutputPin::CheckConnect(IPin* aPin)
michael@0 284 {
michael@0 285 // Our connection is only suitable if the downstream pin knows
michael@0 286 // that we're asynchronous (i.e. it queried for IAsyncReader).
michael@0 287 return mQueriedForAsyncReader ? S_OK : S_FALSE;
michael@0 288 }
michael@0 289
michael@0 290 HRESULT
michael@0 291 OutputPin::CheckMediaType(const MediaType* aMediaType)
michael@0 292 {
michael@0 293 const MediaType *myMediaType = mParentSource->GetMediaType();
michael@0 294
michael@0 295 if (IsEqualGUID(aMediaType->majortype, myMediaType->majortype) &&
michael@0 296 IsEqualGUID(aMediaType->subtype, myMediaType->subtype) &&
michael@0 297 IsEqualGUID(aMediaType->formattype, myMediaType->formattype))
michael@0 298 {
michael@0 299 DIRECTSHOW_LOG("OutputPin::CheckMediaType() Match: major=%s minor=%s TC=%d FSS=%d SS=%u",
michael@0 300 GetDirectShowGuidName(aMediaType->majortype),
michael@0 301 GetDirectShowGuidName(aMediaType->subtype),
michael@0 302 aMediaType->TemporalCompression(),
michael@0 303 aMediaType->bFixedSizeSamples,
michael@0 304 aMediaType->SampleSize());
michael@0 305 return S_OK;
michael@0 306 }
michael@0 307
michael@0 308 DIRECTSHOW_LOG("OutputPin::CheckMediaType() Failed to match: major=%s minor=%s TC=%d FSS=%d SS=%u",
michael@0 309 GetDirectShowGuidName(aMediaType->majortype),
michael@0 310 GetDirectShowGuidName(aMediaType->subtype),
michael@0 311 aMediaType->TemporalCompression(),
michael@0 312 aMediaType->bFixedSizeSamples,
michael@0 313 aMediaType->SampleSize());
michael@0 314 return S_FALSE;
michael@0 315 }
michael@0 316
michael@0 317 HRESULT
michael@0 318 OutputPin::GetMediaType(int aPosition, MediaType* aMediaType)
michael@0 319 {
michael@0 320 if (!aMediaType)
michael@0 321 return E_POINTER;
michael@0 322
michael@0 323 if (aPosition == 0) {
michael@0 324 aMediaType->Assign(mParentSource->GetMediaType());
michael@0 325 return S_OK;
michael@0 326 }
michael@0 327 return VFW_S_NO_MORE_ITEMS;
michael@0 328 }
michael@0 329
michael@0 330 static inline bool
michael@0 331 IsPowerOf2(int32_t x) {
michael@0 332 return ((-x & x) != x);
michael@0 333 }
michael@0 334
michael@0 335 STDMETHODIMP
michael@0 336 OutputPin::RequestAllocator(IMemAllocator* aPreferred,
michael@0 337 ALLOCATOR_PROPERTIES* aProps,
michael@0 338 IMemAllocator** aActual)
michael@0 339 {
michael@0 340 // Require the downstream pin to suggest what they want...
michael@0 341 if (!aPreferred) return E_POINTER;
michael@0 342 if (!aProps) return E_POINTER;
michael@0 343 if (!aActual) return E_POINTER;
michael@0 344
michael@0 345 // We only care about alignment - our allocator will reject anything
michael@0 346 // which isn't power-of-2 aligned, so so try a 4-byte aligned allocator.
michael@0 347 ALLOCATOR_PROPERTIES props;
michael@0 348 memcpy(&props, aProps, sizeof(ALLOCATOR_PROPERTIES));
michael@0 349 if (aProps->cbAlign == 0 || IsPowerOf2(aProps->cbAlign)) {
michael@0 350 props.cbAlign = 4;
michael@0 351 }
michael@0 352
michael@0 353 // Limit allocator's number of buffers. We know that the media will most
michael@0 354 // likely be bound by network speed, not by decoding speed. We also
michael@0 355 // store the incoming data in a Gecko stream, if we don't limit buffers
michael@0 356 // here we'll end up duplicating a lot of storage. We must have enough
michael@0 357 // space for audio key frames to fit in the first batch of buffers however,
michael@0 358 // else pausing may fail for some downstream decoders.
michael@0 359 if (props.cBuffers > BaseFilter::sMaxNumBuffers) {
michael@0 360 props.cBuffers = BaseFilter::sMaxNumBuffers;
michael@0 361 }
michael@0 362
michael@0 363 // The allocator properties that are actually used. We don't store
michael@0 364 // this, we need it for SetProperties() below to succeed.
michael@0 365 ALLOCATOR_PROPERTIES actualProps;
michael@0 366 HRESULT hr;
michael@0 367
michael@0 368 if (aPreferred) {
michael@0 369 // Play nice and prefer the downstream pin's preferred allocator.
michael@0 370 hr = aPreferred->SetProperties(&props, &actualProps);
michael@0 371 if (SUCCEEDED(hr)) {
michael@0 372 aPreferred->AddRef();
michael@0 373 *aActual = aPreferred;
michael@0 374 return S_OK;
michael@0 375 }
michael@0 376 }
michael@0 377
michael@0 378 // Else downstream hasn't requested a specific allocator, so create one...
michael@0 379
michael@0 380 // Just create a default allocator. It's highly unlikely that we'll use
michael@0 381 // this anyway, as most parsers insist on using their own allocators.
michael@0 382 nsRefPtr<IMemAllocator> allocator;
michael@0 383 hr = CoCreateInstance(CLSID_MemoryAllocator,
michael@0 384 0,
michael@0 385 CLSCTX_INPROC_SERVER,
michael@0 386 IID_IMemAllocator,
michael@0 387 getter_AddRefs(allocator));
michael@0 388 if(FAILED(hr) || (allocator == nullptr)) {
michael@0 389 NS_WARNING("Can't create our own DirectShow allocator.");
michael@0 390 return hr;
michael@0 391 }
michael@0 392
michael@0 393 // See if we can make it suitable
michael@0 394 hr = allocator->SetProperties(&props, &actualProps);
michael@0 395 if (SUCCEEDED(hr)) {
michael@0 396 // We need to release our refcount on pAlloc, and addref
michael@0 397 // it to pass a refcount to the caller - this is a net nothing.
michael@0 398 allocator.forget(aActual);
michael@0 399 return S_OK;
michael@0 400 }
michael@0 401
michael@0 402 NS_WARNING("Failed to pick an allocator");
michael@0 403 return hr;
michael@0 404 }
michael@0 405
michael@0 406 STDMETHODIMP
michael@0 407 OutputPin::Request(IMediaSample* aSample, DWORD_PTR aDwUser)
michael@0 408 {
michael@0 409 if (!aSample) return E_FAIL;
michael@0 410
michael@0 411 CriticalSectionAutoEnter lock(*mLock);
michael@0 412 NS_ASSERTION(!mFlushCount, "Request() while flushing");
michael@0 413
michael@0 414 if (mFlushCount)
michael@0 415 return VFW_E_WRONG_STATE;
michael@0 416
michael@0 417 REFERENCE_TIME refStart = 0, refEnd = 0;
michael@0 418 if (FAILED(aSample->GetTime(&refStart, &refEnd))) {
michael@0 419 NS_WARNING("Sample incorrectly timestamped");
michael@0 420 return VFW_E_SAMPLE_TIME_NOT_SET;
michael@0 421 }
michael@0 422
michael@0 423 // Convert reference time to bytes.
michael@0 424 uint32_t start = (uint32_t)(refStart / 10000000);
michael@0 425 uint32_t end = (uint32_t)(refEnd / 10000000);
michael@0 426
michael@0 427 uint32_t numBytes = end - start;
michael@0 428
michael@0 429 ReadRequest* request = new ReadRequest(aSample,
michael@0 430 aDwUser,
michael@0 431 start,
michael@0 432 numBytes);
michael@0 433 // Memory for |request| is free when it's popped from the completed
michael@0 434 // reads list.
michael@0 435
michael@0 436 // Push this onto the queue of reads to be serviced.
michael@0 437 mPendingReads.Push(request);
michael@0 438
michael@0 439 // Notify any threads blocked in WaitForNext() which are waiting for mPendingReads
michael@0 440 // to become non-empty.
michael@0 441 mSignal.Notify();
michael@0 442
michael@0 443 return S_OK;
michael@0 444 }
michael@0 445
michael@0 446 STDMETHODIMP
michael@0 447 OutputPin::WaitForNext(DWORD aTimeout,
michael@0 448 IMediaSample** aOutSample,
michael@0 449 DWORD_PTR* aOutDwUser)
michael@0 450 {
michael@0 451 NS_ASSERTION(aTimeout == 0 || aTimeout == INFINITE,
michael@0 452 "Oops, we don't handle this!");
michael@0 453
michael@0 454 *aOutSample = nullptr;
michael@0 455 *aOutDwUser = 0;
michael@0 456
michael@0 457 LONGLONG offset = 0;
michael@0 458 LONG count = 0;
michael@0 459 BYTE* buf = nullptr;
michael@0 460
michael@0 461 {
michael@0 462 CriticalSectionAutoEnter lock(*mLock);
michael@0 463
michael@0 464 // Wait until there's a pending read to service.
michael@0 465 while (aTimeout && mPendingReads.GetSize() == 0 && !mFlushCount) {
michael@0 466 // Note: No need to guard against shutdown-during-wait here, as
michael@0 467 // typically the thread doing the pull will have already called
michael@0 468 // Request(), so we won't Wait() here anyway. SyncRead() will fail
michael@0 469 // on shutdown.
michael@0 470 mSignal.Wait();
michael@0 471 }
michael@0 472
michael@0 473 nsAutoPtr<ReadRequest> request(reinterpret_cast<ReadRequest*>(mPendingReads.PopFront()));
michael@0 474 if (!request)
michael@0 475 return VFW_E_WRONG_STATE;
michael@0 476
michael@0 477 *aOutSample = request->mSample;
michael@0 478 *aOutDwUser = request->mDwUser;
michael@0 479
michael@0 480 offset = request->mOffset;
michael@0 481 count = request->mCount;
michael@0 482 buf = nullptr;
michael@0 483 request->mSample->GetPointer(&buf);
michael@0 484 NS_ASSERTION(buf != nullptr, "Invalid buffer!");
michael@0 485
michael@0 486 if (mFlushCount) {
michael@0 487 return VFW_E_TIMEOUT;
michael@0 488 }
michael@0 489 }
michael@0 490
michael@0 491 return SyncRead(offset, count, buf);
michael@0 492 }
michael@0 493
michael@0 494 STDMETHODIMP
michael@0 495 OutputPin::SyncReadAligned(IMediaSample* aSample)
michael@0 496 {
michael@0 497 {
michael@0 498 // Ignore reads while flushing.
michael@0 499 CriticalSectionAutoEnter lock(*mLock);
michael@0 500 if (mFlushCount) {
michael@0 501 return S_FALSE;
michael@0 502 }
michael@0 503 }
michael@0 504
michael@0 505 if (!aSample)
michael@0 506 return E_FAIL;
michael@0 507
michael@0 508 REFERENCE_TIME lStart = 0, lEnd = 0;
michael@0 509 if (FAILED(aSample->GetTime(&lStart, &lEnd))) {
michael@0 510 NS_WARNING("Sample incorrectly timestamped");
michael@0 511 return VFW_E_SAMPLE_TIME_NOT_SET;
michael@0 512 }
michael@0 513
michael@0 514 // Convert reference time to bytes.
michael@0 515 int32_t start = (int32_t)(lStart / 10000000);
michael@0 516 int32_t end = (int32_t)(lEnd / 10000000);
michael@0 517
michael@0 518 int32_t numBytes = end - start;
michael@0 519
michael@0 520 // If the range extends off the end of stream, truncate to the end of stream
michael@0 521 // as per IAsyncReader specificiation.
michael@0 522 int64_t streamLength = mResource.GetLength();
michael@0 523 if (streamLength != -1) {
michael@0 524 // We know the exact length of the stream, fail if the requested offset
michael@0 525 // is beyond it.
michael@0 526 if (start > streamLength) {
michael@0 527 return VFW_E_BADALIGN;
michael@0 528 }
michael@0 529
michael@0 530 // If the end of the chunk to read is off the end of the stream,
michael@0 531 // truncate it to the end of the stream.
michael@0 532 if ((start + numBytes) > streamLength) {
michael@0 533 numBytes = (uint32_t)(streamLength - start);
michael@0 534 }
michael@0 535 }
michael@0 536
michael@0 537 BYTE* buf=0;
michael@0 538 aSample->GetPointer(&buf);
michael@0 539
michael@0 540 return SyncRead(start, numBytes, buf);
michael@0 541 }
michael@0 542
michael@0 543 STDMETHODIMP
michael@0 544 OutputPin::SyncRead(LONGLONG aPosition,
michael@0 545 LONG aLength,
michael@0 546 BYTE* aBuffer)
michael@0 547 {
michael@0 548 MOZ_ASSERT(!NS_IsMainThread());
michael@0 549 NS_ENSURE_TRUE(aPosition >= 0, E_FAIL);
michael@0 550 NS_ENSURE_TRUE(aLength > 0, E_FAIL);
michael@0 551 NS_ENSURE_TRUE(aBuffer, E_POINTER);
michael@0 552
michael@0 553 DIRECTSHOW_LOG("OutputPin::SyncRead(%lld, %d)", aPosition, aLength);
michael@0 554 {
michael@0 555 // Ignore reads while flushing.
michael@0 556 CriticalSectionAutoEnter lock(*mLock);
michael@0 557 if (mFlushCount) {
michael@0 558 return S_FALSE;
michael@0 559 }
michael@0 560 }
michael@0 561
michael@0 562 // Read in a loop to ensure we fill the buffer, when possible.
michael@0 563 LONG totalBytesRead = 0;
michael@0 564 while (totalBytesRead < aLength) {
michael@0 565 BYTE* readBuffer = aBuffer + totalBytesRead;
michael@0 566 uint32_t bytesRead = 0;
michael@0 567 LONG length = aLength - totalBytesRead;
michael@0 568 nsresult rv = mResource.ReadAt(aPosition + totalBytesRead,
michael@0 569 reinterpret_cast<char*>(readBuffer),
michael@0 570 length,
michael@0 571 &bytesRead);
michael@0 572 if (NS_FAILED(rv)) {
michael@0 573 return E_FAIL;
michael@0 574 }
michael@0 575 totalBytesRead += bytesRead;
michael@0 576 if (bytesRead == 0) {
michael@0 577 break;
michael@0 578 }
michael@0 579 }
michael@0 580 if (totalBytesRead > 0) {
michael@0 581 CriticalSectionAutoEnter lock(*mLock);
michael@0 582 mBytesConsumed += totalBytesRead;
michael@0 583 }
michael@0 584 return (totalBytesRead == aLength) ? S_OK : S_FALSE;
michael@0 585 }
michael@0 586
michael@0 587 STDMETHODIMP
michael@0 588 OutputPin::Length(LONGLONG* aTotal, LONGLONG* aAvailable)
michael@0 589 {
michael@0 590 HRESULT hr = S_OK;
michael@0 591 int64_t length = mResource.GetLength();
michael@0 592 if (length == -1) {
michael@0 593 hr = VFW_S_ESTIMATED;
michael@0 594 // Don't have a length. Just lie, it seems to work...
michael@0 595 *aTotal = INT32_MAX;
michael@0 596 } else {
michael@0 597 *aTotal = length;
michael@0 598 }
michael@0 599 if (aAvailable) {
michael@0 600 *aAvailable = mResource.GetCachedDataEnd();
michael@0 601 }
michael@0 602
michael@0 603 DIRECTSHOW_LOG("OutputPin::Length() len=%lld avail=%lld", *aTotal, *aAvailable);
michael@0 604
michael@0 605 return hr;
michael@0 606 }
michael@0 607
michael@0 608 STDMETHODIMP
michael@0 609 OutputPin::BeginFlush()
michael@0 610 {
michael@0 611 CriticalSectionAutoEnter lock(*mLock);
michael@0 612 mFlushCount++;
michael@0 613 mSignal.Notify();
michael@0 614 return S_OK;
michael@0 615 }
michael@0 616
michael@0 617 STDMETHODIMP
michael@0 618 OutputPin::EndFlush(void)
michael@0 619 {
michael@0 620 CriticalSectionAutoEnter lock(*mLock);
michael@0 621 mFlushCount--;
michael@0 622 return S_OK;
michael@0 623 }
michael@0 624
michael@0 625 uint32_t
michael@0 626 OutputPin::GetAndResetBytesConsumedCount()
michael@0 627 {
michael@0 628 CriticalSectionAutoEnter lock(*mLock);
michael@0 629 uint32_t bytesConsumed = mBytesConsumed;
michael@0 630 mBytesConsumed = 0;
michael@0 631 return bytesConsumed;
michael@0 632 }
michael@0 633
michael@0 634 SourceFilter::SourceFilter(const GUID& aMajorType,
michael@0 635 const GUID& aSubType)
michael@0 636 : BaseFilter(L"MozillaDirectShowSource", __uuidof(SourceFilter))
michael@0 637 {
michael@0 638 MOZ_COUNT_CTOR(SourceFilter);
michael@0 639 mMediaType.majortype = aMajorType;
michael@0 640 mMediaType.subtype = aSubType;
michael@0 641
michael@0 642 DIRECTSHOW_LOG("SourceFilter Constructor(%s, %s)",
michael@0 643 GetDirectShowGuidName(aMajorType),
michael@0 644 GetDirectShowGuidName(aSubType));
michael@0 645 }
michael@0 646
michael@0 647 SourceFilter::~SourceFilter()
michael@0 648 {
michael@0 649 MOZ_COUNT_DTOR(SourceFilter);
michael@0 650 DIRECTSHOW_LOG("SourceFilter Destructor()");
michael@0 651 }
michael@0 652
michael@0 653 BasePin*
michael@0 654 SourceFilter::GetPin(int n)
michael@0 655 {
michael@0 656 if (n == 0) {
michael@0 657 NS_ASSERTION(mOutputPin != 0, "GetPin with no pin!");
michael@0 658 return static_cast<BasePin*>(mOutputPin);
michael@0 659 } else {
michael@0 660 return nullptr;
michael@0 661 }
michael@0 662 }
michael@0 663
michael@0 664 // Get's the media type we're supplying.
michael@0 665 const MediaType*
michael@0 666 SourceFilter::GetMediaType() const
michael@0 667 {
michael@0 668 return &mMediaType;
michael@0 669 }
michael@0 670
michael@0 671 nsresult
michael@0 672 SourceFilter::Init(MediaResource* aResource, int64_t aMP3Offset)
michael@0 673 {
michael@0 674 DIRECTSHOW_LOG("SourceFilter::Init()");
michael@0 675
michael@0 676 mOutputPin = new OutputPin(aResource,
michael@0 677 this,
michael@0 678 mLock,
michael@0 679 aMP3Offset);
michael@0 680 NS_ENSURE_TRUE(mOutputPin != nullptr, NS_ERROR_FAILURE);
michael@0 681
michael@0 682 return NS_OK;
michael@0 683 }
michael@0 684
michael@0 685 uint32_t
michael@0 686 SourceFilter::GetAndResetBytesConsumedCount()
michael@0 687 {
michael@0 688 return mOutputPin->GetAndResetBytesConsumedCount();
michael@0 689 }
michael@0 690
michael@0 691
michael@0 692 } // namespace mozilla

mercurial