| |
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 |