4 * Copyright 2005 Christian Costa
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "quartz_private.h"
29 #include "wine/unicode.h"
30 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
39 static const WCHAR wcsOutputPinName
[] = {'o','u','t','p','u','t',' ','p','i','n',0};
41 typedef struct WAVEParserImpl
44 LONGLONG StartOfFile
; /* in media time */
51 static inline WAVEParserImpl
*impl_from_IMediaSeeking( IMediaSeeking
*iface
)
53 return CONTAINING_RECORD(iface
, WAVEParserImpl
, Parser
.sourceSeeking
.IMediaSeeking_iface
);
56 static inline WAVEParserImpl
*impl_from_IBaseFilter( IBaseFilter
*iface
)
58 return CONTAINING_RECORD(iface
, WAVEParserImpl
, Parser
.filter
.IBaseFilter_iface
);
61 static LONGLONG
bytepos_to_duration(WAVEParserImpl
*This
, LONGLONG bytepos
)
63 LONGLONG duration
= BYTES_FROM_MEDIATIME(bytepos
- This
->StartOfFile
);
65 duration
/= (This
->dwSampleSize
* This
->nSamplesPerSec
);
70 static LONGLONG
duration_to_bytepos(WAVEParserImpl
*This
, LONGLONG duration
)
74 bytepos
= (This
->dwSampleSize
* This
->nSamplesPerSec
);
77 bytepos
+= BYTES_FROM_MEDIATIME(This
->StartOfFile
);
78 bytepos
-= bytepos
% This
->dwSampleSize
;
80 return MEDIATIME_FROM_BYTES(bytepos
);
83 static HRESULT
WAVEParser_Sample(LPVOID iface
, IMediaSample
* pSample
, DWORD_PTR cookie
)
85 WAVEParserImpl
*This
= iface
;
86 LPBYTE pbSrcStream
= NULL
;
87 ULONG cbSrcStream
= 0;
88 REFERENCE_TIME tStart
, tStop
;
90 IMediaSample
*newsample
= NULL
;
91 Parser_OutputPin
*pOutputPin
;
92 PullPin
*pin
= This
->Parser
.pInputPin
;
94 IMediaSample_GetPointer(pSample
, &pbSrcStream
);
95 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
97 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
100 if (cbSrcStream
== 0)
102 TRACE(".. Why do I need you?\n");
106 pOutputPin
= unsafe_impl_Parser_OutputPin_from_IPin(This
->Parser
.ppPins
[1]);
109 hr
= IMemAllocator_GetBuffer(pin
->pAlloc
, &newsample
, NULL
, NULL
, 0);
113 LONGLONG rtSampleStart
= pin
->rtNext
;
114 /* Add 4 for the next header, which should hopefully work */
115 LONGLONG rtSampleStop
= rtSampleStart
+ MEDIATIME_FROM_BYTES(IMediaSample_GetSize(newsample
));
117 if (rtSampleStop
> pin
->rtStop
)
118 rtSampleStop
= MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin
->rtStop
), pin
->cbAlign
));
120 IMediaSample_SetTime(newsample
, &rtSampleStart
, &rtSampleStop
);
122 pin
->rtCurrent
= pin
->rtNext
;
123 pin
->rtNext
= rtSampleStop
;
125 IMediaSample_SetPreroll(newsample
, 0);
126 IMediaSample_SetDiscontinuity(newsample
, 0);
127 IMediaSample_SetSyncPoint(newsample
, 1);
129 hr
= IAsyncReader_Request(pin
->pReader
, newsample
, 0);
134 REFERENCE_TIME tAviStart
, tAviStop
;
136 IMediaSample_SetSyncPoint(pSample
, TRUE
);
137 pOutputPin
->dwSamplesProcessed
++;
139 tAviStart
= bytepos_to_duration(This
, tStart
);
140 tAviStop
= bytepos_to_duration(This
, tStart
+ MEDIATIME_FROM_BYTES(IMediaSample_GetActualDataLength(pSample
)));
142 IMediaSample_SetTime(pSample
, &tAviStart
, &tAviStop
);
144 hr
= BaseOutputPinImpl_Deliver(&pOutputPin
->pin
, pSample
);
145 if (hr
!= S_OK
&& hr
!= S_FALSE
&& hr
!= VFW_E_WRONG_STATE
)
146 ERR("Error sending sample (%x)\n", hr
);
148 /* Unset progression if denied! */
149 This
->Parser
.pInputPin
->rtCurrent
= tStart
;
152 if (tStop
>= This
->EndOfFile
|| (bytepos_to_duration(This
, tStop
) >= This
->Parser
.sourceSeeking
.llStop
) || hr
== VFW_E_NOT_CONNECTED
)
156 TRACE("End of file reached\n");
158 for (i
= 0; i
< This
->Parser
.cStreams
; i
++)
163 TRACE("Send End Of Stream to output pin %u\n", i
);
165 hr
= IPin_ConnectedTo(This
->Parser
.ppPins
[i
+1], &ppin
);
168 hr
= IPin_EndOfStream(ppin
);
178 /* Force the pullpin thread to stop */
185 static HRESULT
WAVEParser_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
187 if (!IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
))
189 if (IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_WAVE
))
191 if (IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_AU
) || IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_AIFF
))
192 FIXME("AU and AIFF files not supported yet!\n");
196 static HRESULT WINAPI
WAVEParserImpl_seek(IMediaSeeking
*iface
)
198 WAVEParserImpl
*This
= impl_from_IMediaSeeking(iface
);
199 PullPin
*pPin
= This
->Parser
.pInputPin
;
201 LONGLONG newpos
, curpos
, endpos
, bytepos
;
203 newpos
= This
->Parser
.sourceSeeking
.llCurrent
;
204 curpos
= bytepos_to_duration(This
, pPin
->rtCurrent
);
205 endpos
= bytepos_to_duration(This
, This
->EndOfFile
);
206 bytepos
= duration_to_bytepos(This
, newpos
);
210 WARN("Requesting position %x%08x beyond end of stream %x%08x\n", (DWORD
)(newpos
>>32), (DWORD
)newpos
, (DWORD
)(endpos
>>32), (DWORD
)endpos
);
214 if (curpos
/1000000 == newpos
/1000000)
216 TRACE("Requesting position %x%08x same as current position %x%08x\n", (DWORD
)(newpos
>>32), (DWORD
)newpos
, (DWORD
)(curpos
>>32), (DWORD
)curpos
);
220 TRACE("Moving sound to %08u bytes!\n", (DWORD
)BYTES_FROM_MEDIATIME(bytepos
));
222 EnterCriticalSection(&pPin
->thread_lock
);
223 IPin_BeginFlush(&pPin
->pin
.IPin_iface
);
225 /* Make sure this is done while stopped, BeginFlush takes care of this */
226 EnterCriticalSection(&This
->Parser
.filter
.csFilter
);
227 IPin_ConnectedTo(This
->Parser
.ppPins
[1], &victim
);
230 IPin_NewSegment(victim
, newpos
, endpos
, pPin
->dRate
);
231 IPin_Release(victim
);
234 pPin
->rtStart
= pPin
->rtCurrent
= bytepos
;
235 unsafe_impl_Parser_OutputPin_from_IPin(This
->Parser
.ppPins
[1])->dwSamplesProcessed
= 0;
236 LeaveCriticalSection(&This
->Parser
.filter
.csFilter
);
238 TRACE("Done flushing\n");
239 IPin_EndFlush(&pPin
->pin
.IPin_iface
);
240 LeaveCriticalSection(&pPin
->thread_lock
);
245 static HRESULT
WAVEParser_InputPin_PreConnect(IPin
* iface
, IPin
* pConnectPin
, ALLOCATOR_PROPERTIES
*props
)
247 PullPin
*This
= impl_PullPin_from_IPin(iface
);
251 LONGLONG pos
= 0; /* in bytes */
254 WAVEParserImpl
* pWAVEParser
= impl_from_IBaseFilter(This
->pin
.pinInfo
.pFilter
);
255 LONGLONG length
, avail
;
257 piOutput
.dir
= PINDIR_OUTPUT
;
258 piOutput
.pFilter
= &pWAVEParser
->Parser
.filter
.IBaseFilter_iface
;
259 lstrcpynW(piOutput
.achName
, wcsOutputPinName
, sizeof(piOutput
.achName
) / sizeof(piOutput
.achName
[0]));
261 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
264 if (list
.fcc
!= FOURCC_RIFF
)
266 ERR("Input stream not a RIFF file\n");
269 if (list
.cb
> 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */
271 ERR("Input stream violates RIFF spec\n");
274 if (list
.fccListType
!= mmioFOURCC('W','A','V','E'))
276 ERR("Input stream not an WAVE RIFF file\n");
280 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(chunk
), (BYTE
*)&chunk
);
281 pos
+= sizeof(chunk
);
282 if (chunk
.fcc
!= mmioFOURCC('f','m','t',' '))
284 ERR("Expected 'fmt ' chunk, but got %.04s\n", (LPSTR
)&chunk
.fcc
);
288 amt
.majortype
= MEDIATYPE_Audio
;
289 amt
.formattype
= FORMAT_WaveFormatEx
;
290 amt
.cbFormat
= chunk
.cb
;
291 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
293 IAsyncReader_SyncRead(This
->pReader
, pos
, amt
.cbFormat
, amt
.pbFormat
);
294 amt
.subtype
= MEDIATYPE_Audio
;
295 amt
.subtype
.Data1
= ((WAVEFORMATEX
*)amt
.pbFormat
)->wFormatTag
;
298 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(chunk
), (BYTE
*)&chunk
);
299 if (chunk
.fcc
== mmioFOURCC('f','a','c','t'))
301 FIXME("'fact' chunk not supported yet\n");
302 pos
+= sizeof(chunk
) + chunk
.cb
;
303 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(chunk
), (BYTE
*)&chunk
);
305 if (chunk
.fcc
!= mmioFOURCC('d','a','t','a'))
307 ERR("Expected 'data' chunk, but got %.04s\n", (LPSTR
)&chunk
.fcc
);
313 pWAVEParser
->StartOfFile
= MEDIATIME_FROM_BYTES(pos
+ sizeof(RIFFCHUNK
));
314 pWAVEParser
->EndOfFile
= MEDIATIME_FROM_BYTES(pos
+ chunk
.cb
+ sizeof(RIFFCHUNK
));
320 props
->cbAlign
= ((WAVEFORMATEX
*)amt
.pbFormat
)->nBlockAlign
;
322 props
->cbBuffer
= 4096;
324 pWAVEParser
->dwSampleSize
= ((WAVEFORMATEX
*)amt
.pbFormat
)->nBlockAlign
;
325 IAsyncReader_Length(This
->pReader
, &length
, &avail
);
326 pWAVEParser
->dwLength
= length
/ (ULONGLONG
)pWAVEParser
->dwSampleSize
;
327 pWAVEParser
->nSamplesPerSec
= ((WAVEFORMATEX
*)amt
.pbFormat
)->nSamplesPerSec
;
328 hr
= Parser_AddPin(&(pWAVEParser
->Parser
), &piOutput
, props
, &amt
);
329 CoTaskMemFree(amt
.pbFormat
);
331 pWAVEParser
->Parser
.sourceSeeking
.llCurrent
= 0;
332 pWAVEParser
->Parser
.sourceSeeking
.llStop
= pWAVEParser
->Parser
.sourceSeeking
.llDuration
= bytepos_to_duration(pWAVEParser
, pWAVEParser
->EndOfFile
);
333 TRACE("Duration: %u seconds\n", (DWORD
)(pWAVEParser
->Parser
.sourceSeeking
.llDuration
/ (LONGLONG
)10000000));
335 This
->rtStop
= pWAVEParser
->EndOfFile
;
336 This
->rtStart
= pWAVEParser
->StartOfFile
;
338 TRACE("WAVE File ok\n");
343 static HRESULT
WAVEParser_Cleanup(LPVOID iface
)
345 WAVEParserImpl
*This
= iface
;
347 TRACE("(%p)->()\n", This
);
352 static HRESULT
WAVEParser_first_request(LPVOID iface
)
354 WAVEParserImpl
*This
= iface
;
355 PullPin
*pin
= This
->Parser
.pInputPin
;
357 IMediaSample
*sample
;
359 if (pin
->rtCurrent
>= pin
->rtStop
)
361 /* Last sample has already been queued, request nothing more */
366 hr
= IMemAllocator_GetBuffer(pin
->pAlloc
, &sample
, NULL
, NULL
, 0);
368 pin
->rtNext
= pin
->rtCurrent
;
371 LONGLONG rtSampleStart
= pin
->rtNext
;
372 /* Add 4 for the next header, which should hopefully work */
373 LONGLONG rtSampleStop
= rtSampleStart
+ MEDIATIME_FROM_BYTES(IMediaSample_GetSize(sample
));
374 Parser_OutputPin
*outpin
= unsafe_impl_Parser_OutputPin_from_IPin(This
->Parser
.ppPins
[1]);
376 if (rtSampleStop
> pin
->rtStop
)
377 rtSampleStop
= MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin
->rtStop
), pin
->cbAlign
));
379 IMediaSample_SetTime(sample
, &rtSampleStart
, &rtSampleStop
);
381 pin
->rtCurrent
= pin
->rtNext
;
382 pin
->rtNext
= rtSampleStop
;
384 IMediaSample_SetPreroll(sample
, FALSE
);
385 if (!outpin
->dwSamplesProcessed
++)
386 IMediaSample_SetDiscontinuity(sample
, TRUE
);
388 IMediaSample_SetDiscontinuity(sample
, FALSE
);
390 hr
= IAsyncReader_Request(pin
->pReader
, sample
, 0);
393 ERR("Horsemen of the apocalypse came to bring error 0x%08x %p\n", hr
, sample
);
398 static HRESULT
WAVEParser_disconnect(LPVOID iface
)
400 /* TODO: Find and plug memory leaks */
404 static const IBaseFilterVtbl WAVEParser_Vtbl
=
406 Parser_QueryInterface
,
414 Parser_SetSyncSource
,
415 Parser_GetSyncSource
,
418 Parser_QueryFilterInfo
,
419 Parser_JoinFilterGraph
,
420 Parser_QueryVendorInfo
423 HRESULT
WAVEParser_create(IUnknown
* pUnkOuter
, LPVOID
* ppv
)
426 WAVEParserImpl
* This
;
428 TRACE("(%p, %p)\n", pUnkOuter
, ppv
);
433 return CLASS_E_NOAGGREGATION
;
435 /* Note: This memory is managed by the transform filter once created */
436 This
= CoTaskMemAlloc(sizeof(WAVEParserImpl
));
438 hr
= Parser_Create(&(This
->Parser
), &WAVEParser_Vtbl
, &CLSID_WAVEParser
, WAVEParser_Sample
, WAVEParser_QueryAccept
, WAVEParser_InputPin_PreConnect
, WAVEParser_Cleanup
, WAVEParser_disconnect
, WAVEParser_first_request
, NULL
, NULL
, WAVEParserImpl_seek
, NULL
);