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"
22 #include "control_private.h"
30 #include "wine/unicode.h"
31 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
40 static const WCHAR wcsOutputPinName
[] = {'o','u','t','p','u','t',' ','p','i','n',0};
42 typedef struct WAVEParserImpl
45 IMediaSample
* pCurrentSample
;
46 LONGLONG StartOfFile
; /* in media time */
53 static LONGLONG
bytepos_to_duration(WAVEParserImpl
*This
, LONGLONG bytepos
)
55 LONGLONG duration
= BYTES_FROM_MEDIATIME(bytepos
- This
->StartOfFile
);
57 duration
/= (This
->dwSampleSize
* This
->nSamplesPerSec
);
62 static LONGLONG
duration_to_bytepos(WAVEParserImpl
*This
, LONGLONG duration
)
66 bytepos
= (This
->dwSampleSize
* This
->nSamplesPerSec
);
69 bytepos
+= BYTES_FROM_MEDIATIME(This
->StartOfFile
);
70 bytepos
-= bytepos
% This
->dwSampleSize
;
72 return MEDIATIME_FROM_BYTES(bytepos
);
75 static HRESULT
WAVEParser_Sample(LPVOID iface
, IMediaSample
* pSample
)
77 WAVEParserImpl
*This
= (WAVEParserImpl
*)iface
;
78 LPBYTE pbSrcStream
= NULL
;
80 REFERENCE_TIME tStart
, tStop
;
82 BOOL bMoreData
= TRUE
;
83 Parser_OutputPin
* pOutputPin
;
86 long chunk_remaining_bytes
= 0;
89 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
91 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
93 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
95 assert(BYTES_FROM_MEDIATIME(tStop
- tStart
) == cbSrcStream
);
97 pOutputPin
= (Parser_OutputPin
*)This
->Parser
.ppPins
[1];
99 /* Try to get rid of the current sample in case we had a S_FALSE last time */
100 if (This
->pCurrentSample
&& (IMediaSample_GetActualDataLength(This
->pCurrentSample
) == IMediaSample_GetSize(This
->pCurrentSample
)))
104 /* Unset advancement */
105 This
->Parser
.pInputPin
->rtCurrent
-= MEDIATIME_FROM_BYTES(cbSrcStream
);
107 hr
= OutputPin_SendSample(&pOutputPin
->pin
, This
->pCurrentSample
);
112 IMediaSample_Release(This
->pCurrentSample
);
113 This
->pCurrentSample
= NULL
;
115 This
->Parser
.pInputPin
->rtCurrent
+= MEDIATIME_FROM_BYTES(cbSrcStream
);
118 if (tStop
< This
->StartOfFile
)
121 if (tStart
< This
->StartOfFile
)
122 offset_src
= BYTES_FROM_MEDIATIME(This
->StartOfFile
- tStart
);
126 if (!This
->pCurrentSample
)
128 /* cache media sample until it is ready to be dispatched
129 * (i.e. we reach the end of the chunk) */
130 hr
= OutputPin_GetDeliveryBuffer(&pOutputPin
->pin
, &This
->pCurrentSample
, NULL
, NULL
, 0);
134 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, 0);
139 TRACE("Skipping sending sample due to error (%x)\n", hr
);
140 This
->pCurrentSample
= NULL
;
145 hr
= IMediaSample_GetPointer(This
->pCurrentSample
, &pbDstStream
);
149 cbDstStream
= IMediaSample_GetSize(This
->pCurrentSample
);
151 chunk_remaining_bytes
= cbDstStream
- IMediaSample_GetActualDataLength(This
->pCurrentSample
);
153 assert(chunk_remaining_bytes
>= 0);
154 assert(chunk_remaining_bytes
<= cbDstStream
- IMediaSample_GetActualDataLength(This
->pCurrentSample
));
157 if (chunk_remaining_bytes
<= cbSrcStream
- offset_src
)
161 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, chunk_remaining_bytes
);
162 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, chunk_remaining_bytes
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
168 REFERENCE_TIME tAviStart
, tAviStop
, tOffset
;
170 IMediaSample_SetDiscontinuity(This
->pCurrentSample
, pOutputPin
->dwSamplesProcessed
== 0);
172 IMediaSample_SetSyncPoint(This
->pCurrentSample
, TRUE
);
173 pOutputPin
->dwSamplesProcessed
++;
175 tOffset
= MEDIATIME_FROM_BYTES(offset_src
+ chunk_remaining_bytes
- IMediaSample_GetActualDataLength(This
->pCurrentSample
));
176 tAviStart
= bytepos_to_duration(This
, tStart
+ tOffset
);
178 tOffset
+= MEDIATIME_FROM_BYTES(IMediaSample_GetActualDataLength(This
->pCurrentSample
));
179 tAviStop
= bytepos_to_duration(This
, tStart
+ tOffset
);
181 IMediaSample_SetTime(This
->pCurrentSample
, &tAviStart
, &tAviStop
);
183 hr
= OutputPin_SendSample(&pOutputPin
->pin
, This
->pCurrentSample
);
184 if (hr
!= S_OK
&& hr
!= VFW_E_NOT_CONNECTED
&& hr
!= S_FALSE
)
185 ERR("Error sending sample (%x)\n", hr
);
188 if (This
->pCurrentSample
&& hr
!= S_FALSE
)
190 IMediaSample_Release(This
->pCurrentSample
);
191 This
->pCurrentSample
= NULL
;
196 This
->Parser
.pInputPin
->rtCurrent
-= MEDIATIME_FROM_BYTES(cbSrcStream
- offset_src
- chunk_remaining_bytes
);
206 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, cbSrcStream
- offset_src
);
207 IMediaSample_SetActualDataLength(This
->pCurrentSample
, cbSrcStream
- offset_src
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
211 offset_src
+= chunk_remaining_bytes
;
214 if (tStop
>= This
->EndOfFile
|| (bytepos_to_duration(This
, tStop
) >= This
->Parser
.mediaSeeking
.llStop
))
218 TRACE("End of file reached\n");
220 for (i
= 0; i
< This
->Parser
.cStreams
; i
++)
225 TRACE("Send End Of Stream to output pin %d\n", i
);
227 hr
= IPin_ConnectedTo(This
->Parser
.ppPins
[i
+1], &ppin
);
230 hr
= IPin_EndOfStream(ppin
);
240 /* Force the pullpin thread to stop */
247 static HRESULT
WAVEParser_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
249 if (!IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
))
251 if (IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_WAVE
))
253 if (IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_AU
) || IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_AIFF
))
254 FIXME("AU and AIFF files not supported yet!\n");
258 static HRESULT
WAVEParserImpl_seek(IBaseFilter
*iface
)
260 WAVEParserImpl
*This
= (WAVEParserImpl
*)iface
;
261 PullPin
*pPin
= This
->Parser
.pInputPin
;
263 LONGLONG newpos
, curpos
, endpos
, bytepos
;
265 newpos
= This
->Parser
.mediaSeeking
.llCurrent
;
266 curpos
= bytepos_to_duration(This
, pPin
->rtCurrent
);
267 endpos
= bytepos_to_duration(This
, This
->EndOfFile
);
268 bytepos
= duration_to_bytepos(This
, newpos
);
272 WARN("Requesting position %x%08x beyond end of stream %x%08x\n", (DWORD
)(newpos
>>32), (DWORD
)newpos
, (DWORD
)(endpos
>>32), (DWORD
)endpos
);
276 if (curpos
/1000000 == newpos
/1000000)
278 TRACE("Requesting position %x%08x same as current position %x%08x\n", (DWORD
)(newpos
>>32), (DWORD
)newpos
, (DWORD
)(curpos
>>32), (DWORD
)curpos
);
282 TRACE("Moving sound to %08u bytes!\n", (DWORD
)BYTES_FROM_MEDIATIME(bytepos
));
284 EnterCriticalSection(&pPin
->thread_lock
);
285 IPin_BeginFlush((IPin
*)pPin
);
287 /* Make sure this is done while stopped, BeginFlush takes care of this */
288 EnterCriticalSection(&This
->Parser
.csFilter
);
289 IPin_ConnectedTo(This
->Parser
.ppPins
[1], &victim
);
292 IPin_NewSegment(victim
, newpos
, endpos
, pPin
->dRate
);
293 IPin_Release(victim
);
296 pPin
->rtStart
= pPin
->rtCurrent
= bytepos
;
297 ((Parser_OutputPin
*)This
->Parser
.ppPins
[1])->dwSamplesProcessed
= 0;
298 LeaveCriticalSection(&This
->Parser
.csFilter
);
300 TRACE("Done flushing\n");
301 IPin_EndFlush((IPin
*)pPin
);
302 LeaveCriticalSection(&pPin
->thread_lock
);
307 static HRESULT
WAVEParser_InputPin_PreConnect(IPin
* iface
, IPin
* pConnectPin
)
309 PullPin
*This
= (PullPin
*)iface
;
313 LONGLONG pos
= 0; /* in bytes */
315 ALLOCATOR_PROPERTIES props
;
317 WAVEParserImpl
* pWAVEParser
= (WAVEParserImpl
*)This
->pin
.pinInfo
.pFilter
;
318 LONGLONG length
, avail
;
320 piOutput
.dir
= PINDIR_OUTPUT
;
321 piOutput
.pFilter
= (IBaseFilter
*)This
;
322 lstrcpynW(piOutput
.achName
, wcsOutputPinName
, sizeof(piOutput
.achName
) / sizeof(piOutput
.achName
[0]));
324 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
327 if (list
.fcc
!= FOURCC_RIFF
)
329 ERR("Input stream not a RIFF file\n");
332 if (list
.cb
> 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */
334 ERR("Input stream violates RIFF spec\n");
337 if (list
.fccListType
!= mmioFOURCC('W','A','V','E'))
339 ERR("Input stream not an WAVE RIFF file\n");
343 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(chunk
), (BYTE
*)&chunk
);
344 pos
+= sizeof(chunk
);
345 if (chunk
.fcc
!= mmioFOURCC('f','m','t',' '))
347 ERR("Expected 'fmt ' chunk, but got %.04s\n", (LPSTR
)&chunk
.fcc
);
351 amt
.majortype
= MEDIATYPE_Audio
;
352 amt
.formattype
= FORMAT_WaveFormatEx
;
353 amt
.cbFormat
= chunk
.cb
;
354 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
356 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, amt
.cbFormat
, amt
.pbFormat
);
357 amt
.subtype
= MEDIATYPE_Audio
;
358 amt
.subtype
.Data1
= ((WAVEFORMATEX
*)amt
.pbFormat
)->wFormatTag
;
361 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(chunk
), (BYTE
*)&chunk
);
362 if (chunk
.fcc
== mmioFOURCC('f','a','c','t'))
364 FIXME("'fact' chunk not supported yet\n");
365 pos
+= sizeof(chunk
) + chunk
.cb
;
366 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(chunk
), (BYTE
*)&chunk
);
368 if (chunk
.fcc
!= mmioFOURCC('d','a','t','a'))
370 ERR("Expected 'data' chunk, but got %.04s\n", (LPSTR
)&chunk
.fcc
);
376 pWAVEParser
->StartOfFile
= MEDIATIME_FROM_BYTES(pos
+ sizeof(RIFFCHUNK
));
377 pWAVEParser
->EndOfFile
= MEDIATIME_FROM_BYTES(pos
+ chunk
.cb
+ sizeof(RIFFCHUNK
));
383 props
.cbAlign
= ((WAVEFORMATEX
*)amt
.pbFormat
)->nBlockAlign
;
385 props
.cbBuffer
= 4096;
387 pWAVEParser
->dwSampleSize
= ((WAVEFORMATEX
*)amt
.pbFormat
)->nBlockAlign
;
388 IAsyncReader_Length(This
->pReader
, &length
, &avail
);
389 pWAVEParser
->dwLength
= length
/ (ULONGLONG
)pWAVEParser
->dwSampleSize
;
390 pWAVEParser
->nSamplesPerSec
= ((WAVEFORMATEX
*)amt
.pbFormat
)->nSamplesPerSec
;
391 hr
= Parser_AddPin(&(pWAVEParser
->Parser
), &piOutput
, &props
, &amt
);
392 CoTaskMemFree(amt
.pbFormat
);
394 pWAVEParser
->Parser
.mediaSeeking
.llCurrent
= 0;
395 pWAVEParser
->Parser
.mediaSeeking
.llStop
= pWAVEParser
->Parser
.mediaSeeking
.llDuration
= bytepos_to_duration(pWAVEParser
, pWAVEParser
->EndOfFile
);
396 TRACE("Duration: %lld seconds\n", pWAVEParser
->Parser
.mediaSeeking
.llDuration
/ (LONGLONG
)10000000);
398 This
->rtStop
= pWAVEParser
->EndOfFile
;
399 This
->rtStart
= pWAVEParser
->StartOfFile
;
401 TRACE("WAVE File ok\n");
406 static HRESULT
WAVEParser_Cleanup(LPVOID iface
)
408 WAVEParserImpl
*This
= (WAVEParserImpl
*)iface
;
410 TRACE("(%p)->()\n", This
);
412 if (This
->pCurrentSample
)
413 IMediaSample_Release(This
->pCurrentSample
);
414 This
->pCurrentSample
= NULL
;
419 static HRESULT
WAVEParser_disconnect(LPVOID iface
)
421 /* TODO: Find and plug memory leaks */
425 HRESULT
WAVEParser_create(IUnknown
* pUnkOuter
, LPVOID
* ppv
)
428 WAVEParserImpl
* This
;
430 TRACE("(%p, %p)\n", pUnkOuter
, ppv
);
435 return CLASS_E_NOAGGREGATION
;
437 /* Note: This memory is managed by the transform filter once created */
438 This
= CoTaskMemAlloc(sizeof(WAVEParserImpl
));
440 This
->pCurrentSample
= NULL
;
442 hr
= Parser_Create(&(This
->Parser
), &CLSID_WAVEParser
, WAVEParser_Sample
, WAVEParser_QueryAccept
, WAVEParser_InputPin_PreConnect
, WAVEParser_Cleanup
, WAVEParser_disconnect
, NULL
, WAVEParserImpl_seek
, NULL
);