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"
33 #include "wine/unicode.h"
34 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
43 static const WCHAR wcsOutputPinName
[] = {'o','u','t','p','u','t',' ','p','i','n',0};
45 typedef struct WAVEParserImpl
48 IMediaSample
* pCurrentSample
;
49 LONGLONG StartOfFile
; /* in media time */
53 static HRESULT
WAVEParser_Sample(LPVOID iface
, IMediaSample
* pSample
)
55 WAVEParserImpl
*This
= (WAVEParserImpl
*)iface
;
56 LPBYTE pbSrcStream
= NULL
;
58 REFERENCE_TIME tStart
, tStop
;
60 BOOL bMoreData
= TRUE
;
61 Parser_OutputPin
* pOutputPin
;
64 long chunk_remaining_bytes
= 0;
67 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
69 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
71 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
73 assert(BYTES_FROM_MEDIATIME(tStop
- tStart
) == cbSrcStream
);
75 pOutputPin
= (Parser_OutputPin
*)This
->Parser
.ppPins
[1];
77 if (tStop
< This
->StartOfFile
)
80 if (tStart
< This
->StartOfFile
)
81 offset_src
= BYTES_FROM_MEDIATIME(This
->StartOfFile
- tStart
);
85 if (!This
->pCurrentSample
)
87 /* cache media sample until it is ready to be despatched
88 * (i.e. we reach the end of the chunk) */
89 hr
= OutputPin_GetDeliveryBuffer(&pOutputPin
->pin
, &This
->pCurrentSample
, NULL
, NULL
, 0);
93 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, 0);
98 TRACE("Skipping sending sample due to error (%x)\n", hr
);
99 This
->pCurrentSample
= NULL
;
104 hr
= IMediaSample_GetPointer(This
->pCurrentSample
, &pbDstStream
);
108 cbDstStream
= IMediaSample_GetSize(This
->pCurrentSample
);
110 chunk_remaining_bytes
= cbDstStream
- IMediaSample_GetActualDataLength(This
->pCurrentSample
);
112 assert(chunk_remaining_bytes
>= 0);
113 assert(chunk_remaining_bytes
<= cbDstStream
- IMediaSample_GetActualDataLength(This
->pCurrentSample
));
116 if (chunk_remaining_bytes
<= cbSrcStream
- offset_src
)
120 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, chunk_remaining_bytes
);
121 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, chunk_remaining_bytes
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
127 REFERENCE_TIME tAviStart
, tAviStop
;
130 if (pOutputPin
->dwSamplesProcessed
== 0) {
131 IMediaSample_SetDiscontinuity(This
->pCurrentSample
, TRUE
);
132 IMediaSample_SetSyncPoint(This
->pCurrentSample
, TRUE
);
135 pOutputPin
->dwSamplesProcessed
++;
137 if (pOutputPin
->dwSampleSize
)
138 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) * (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)pOutputPin
->dwSampleSize
* pOutputPin
->fSamplesPerSec
));
140 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) / (float)pOutputPin
->fSamplesPerSec
);
141 if (pOutputPin
->dwSampleSize
)
142 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
* (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)pOutputPin
->dwSampleSize
* pOutputPin
->fSamplesPerSec
));
144 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
/ (float)pOutputPin
->fSamplesPerSec
);
146 IMediaSample_SetTime(This
->pCurrentSample
, &tAviStart
, &tAviStop
);
148 hr
= OutputPin_SendSample(&pOutputPin
->pin
, This
->pCurrentSample
);
149 if (hr
!= S_OK
&& hr
!= VFW_E_NOT_CONNECTED
)
150 ERR("Error sending sample (%x)\n", hr
);
153 if (This
->pCurrentSample
)
154 IMediaSample_Release(This
->pCurrentSample
);
156 This
->pCurrentSample
= NULL
;
162 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, cbSrcStream
- offset_src
);
163 IMediaSample_SetActualDataLength(This
->pCurrentSample
, cbSrcStream
- offset_src
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
167 offset_src
+= chunk_remaining_bytes
;
170 if (tStop
>= This
->EndOfFile
)
174 TRACE("End of file reached\n");
176 for (i
= 0; i
< This
->Parser
.cStreams
; i
++)
181 TRACE("Send End Of Stream to output pin %d\n", i
);
183 hr
= IPin_ConnectedTo(This
->Parser
.ppPins
[i
+1], &ppin
);
186 hr
= IPin_EndOfStream(ppin
);
196 /* Force the pullpin thread to stop */
203 static HRESULT
WAVEParser_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
205 if (!IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
))
207 if (IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_WAVE
))
209 if (IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_AU
) || IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_AIFF
))
210 FIXME("AU and AIFF files not supported yet!\n");
214 static HRESULT
WAVEParser_InputPin_PreConnect(IPin
* iface
, IPin
* pConnectPin
)
216 PullPin
*This
= (PullPin
*)iface
;
220 LONGLONG pos
= 0; /* in bytes */
222 ALLOCATOR_PROPERTIES props
;
224 float fSamplesPerSec
= 0.0f
;
225 DWORD dwSampleSize
= 0;
227 WAVEParserImpl
* pWAVEParser
= (WAVEParserImpl
*)This
->pin
.pinInfo
.pFilter
;
229 piOutput
.dir
= PINDIR_OUTPUT
;
230 piOutput
.pFilter
= (IBaseFilter
*)This
;
231 lstrcpynW(piOutput
.achName
, wcsOutputPinName
, sizeof(piOutput
.achName
) / sizeof(piOutput
.achName
[0]));
233 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
236 if (list
.fcc
!= ckidRIFF
)
238 ERR("Input stream not a RIFF file\n");
241 if (list
.cb
> 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */
243 ERR("Input stream violates RIFF spec\n");
246 if (list
.fccListType
!= mmioFOURCC('W','A','V','E'))
248 ERR("Input stream not an WAVE RIFF file\n");
252 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(chunk
), (BYTE
*)&chunk
);
253 pos
+= sizeof(chunk
);
254 if (chunk
.fcc
!= mmioFOURCC('f','m','t',' '))
256 ERR("Expected 'fmt ' chunk, but got %.04s\n", (LPSTR
)&chunk
.fcc
);
260 memcpy(&amt
.majortype
, &MEDIATYPE_Audio
, sizeof(GUID
));
261 memcpy(&amt
.formattype
, &FORMAT_WaveFormatEx
, sizeof(GUID
));
262 amt
.cbFormat
= chunk
.cb
;
263 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
265 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, amt
.cbFormat
, amt
.pbFormat
);
266 memcpy(&amt
.subtype
, &MEDIATYPE_Audio
, sizeof(GUID
));
267 amt
.subtype
.Data1
= ((WAVEFORMATEX
*)amt
.pbFormat
)->wFormatTag
;
270 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(chunk
), (BYTE
*)&chunk
);
271 if (chunk
.fcc
== mmioFOURCC('f','a','c','t'))
273 FIXME("'fact' chunk not supported yet\n");
274 pos
+= sizeof(chunk
) + chunk
.cb
;
275 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(chunk
), (BYTE
*)&chunk
);
277 if (chunk
.fcc
!= mmioFOURCC('d','a','t','a'))
279 ERR("Expected 'data' chunk, but got %.04s\n", (LPSTR
)&chunk
.fcc
);
285 pWAVEParser
->StartOfFile
= MEDIATIME_FROM_BYTES(pos
+ sizeof(RIFFCHUNK
));
286 pWAVEParser
->EndOfFile
= MEDIATIME_FROM_BYTES(pos
+ chunk
.cb
+ sizeof(RIFFCHUNK
));
292 props
.cbAlign
= ((WAVEFORMATEX
*)amt
.pbFormat
)->nBlockAlign
;
294 props
.cbBuffer
= 4096;
297 hr
= Parser_AddPin(&(pWAVEParser
->Parser
), &piOutput
, &props
, &amt
, fSamplesPerSec
, dwSampleSize
, dwLength
);
299 TRACE("WAVE File ok\n");
304 static HRESULT
WAVEParser_Cleanup(LPVOID iface
)
306 WAVEParserImpl
*This
= (WAVEParserImpl
*)iface
;
308 TRACE("(%p)->()\n", This
);
310 if (This
->pCurrentSample
)
311 IMediaSample_Release(This
->pCurrentSample
);
312 This
->pCurrentSample
= NULL
;
317 HRESULT
WAVEParser_create(IUnknown
* pUnkOuter
, LPVOID
* ppv
)
320 WAVEParserImpl
* This
;
322 TRACE("(%p, %p)\n", pUnkOuter
, ppv
);
327 return CLASS_E_NOAGGREGATION
;
329 /* Note: This memory is managed by the transform filter once created */
330 This
= CoTaskMemAlloc(sizeof(WAVEParserImpl
));
332 This
->pCurrentSample
= NULL
;
334 hr
= Parser_Create(&(This
->Parser
), &CLSID_WAVEParser
, WAVEParser_Sample
, WAVEParser_QueryAccept
, WAVEParser_InputPin_PreConnect
, WAVEParser_Cleanup
);