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