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"
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
42 static const WCHAR wcsOutputPinName
[] = {'o','u','t','p','u','t',' ','p','i','n',0};
44 typedef struct WAVEParserImpl
47 IMediaSample
* pCurrentSample
;
48 LONGLONG StartOfFile
; /* in media time */
55 static HRESULT
WAVEParser_Sample(LPVOID iface
, IMediaSample
* pSample
)
57 WAVEParserImpl
*This
= (WAVEParserImpl
*)iface
;
58 LPBYTE pbSrcStream
= NULL
;
60 REFERENCE_TIME tStart
, tStop
;
62 BOOL bMoreData
= TRUE
;
63 Parser_OutputPin
* pOutputPin
;
66 long chunk_remaining_bytes
= 0;
69 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
71 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
73 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
75 assert(BYTES_FROM_MEDIATIME(tStop
- tStart
) == cbSrcStream
);
77 pOutputPin
= (Parser_OutputPin
*)This
->Parser
.ppPins
[1];
79 if (tStop
< This
->StartOfFile
)
82 if (tStart
< This
->StartOfFile
)
83 offset_src
= BYTES_FROM_MEDIATIME(This
->StartOfFile
- tStart
);
87 if (!This
->pCurrentSample
)
89 /* cache media sample until it is ready to be despatched
90 * (i.e. we reach the end of the chunk) */
91 hr
= OutputPin_GetDeliveryBuffer(&pOutputPin
->pin
, &This
->pCurrentSample
, NULL
, NULL
, 0);
95 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, 0);
100 TRACE("Skipping sending sample due to error (%x)\n", hr
);
101 This
->pCurrentSample
= NULL
;
106 hr
= IMediaSample_GetPointer(This
->pCurrentSample
, &pbDstStream
);
110 cbDstStream
= IMediaSample_GetSize(This
->pCurrentSample
);
112 chunk_remaining_bytes
= cbDstStream
- IMediaSample_GetActualDataLength(This
->pCurrentSample
);
114 assert(chunk_remaining_bytes
>= 0);
115 assert(chunk_remaining_bytes
<= cbDstStream
- IMediaSample_GetActualDataLength(This
->pCurrentSample
));
118 if (chunk_remaining_bytes
<= cbSrcStream
- offset_src
)
122 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, chunk_remaining_bytes
);
123 hr
= IMediaSample_SetActualDataLength(This
->pCurrentSample
, chunk_remaining_bytes
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
129 REFERENCE_TIME tAviStart
, tAviStop
;
132 if (pOutputPin
->dwSamplesProcessed
== 0) {
133 IMediaSample_SetDiscontinuity(This
->pCurrentSample
, TRUE
);
134 IMediaSample_SetSyncPoint(This
->pCurrentSample
, TRUE
);
137 pOutputPin
->dwSamplesProcessed
++;
139 if (This
->dwSampleSize
)
140 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) * (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)This
->dwSampleSize
* This
->fSamplesPerSec
));
142 tAviStart
= (LONGLONG
)ceil(10000000.0 * (float)(pOutputPin
->dwSamplesProcessed
- 1) / (float)This
->fSamplesPerSec
);
143 if (This
->dwSampleSize
)
144 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
* (float)IMediaSample_GetActualDataLength(This
->pCurrentSample
) / ((float)This
->dwSampleSize
* This
->fSamplesPerSec
));
146 tAviStop
= (LONGLONG
)ceil(10000000.0 * (float)pOutputPin
->dwSamplesProcessed
/ (float)This
->fSamplesPerSec
);
148 IMediaSample_SetTime(This
->pCurrentSample
, &tAviStart
, &tAviStop
);
150 hr
= OutputPin_SendSample(&pOutputPin
->pin
, This
->pCurrentSample
);
151 if (hr
!= S_OK
&& hr
!= VFW_E_NOT_CONNECTED
)
152 ERR("Error sending sample (%x)\n", hr
);
155 if (This
->pCurrentSample
)
156 IMediaSample_Release(This
->pCurrentSample
);
158 This
->pCurrentSample
= NULL
;
164 memcpy(pbDstStream
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
), pbSrcStream
+ offset_src
, cbSrcStream
- offset_src
);
165 IMediaSample_SetActualDataLength(This
->pCurrentSample
, cbSrcStream
- offset_src
+ IMediaSample_GetActualDataLength(This
->pCurrentSample
));
169 offset_src
+= chunk_remaining_bytes
;
172 if (tStop
>= This
->EndOfFile
)
176 TRACE("End of file reached\n");
178 for (i
= 0; i
< This
->Parser
.cStreams
; i
++)
183 TRACE("Send End Of Stream to output pin %d\n", i
);
185 hr
= IPin_ConnectedTo(This
->Parser
.ppPins
[i
+1], &ppin
);
188 hr
= IPin_EndOfStream(ppin
);
198 /* Force the pullpin thread to stop */
205 static HRESULT
WAVEParser_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
207 if (!IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
))
209 if (IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_WAVE
))
211 if (IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_AU
) || IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_AIFF
))
212 FIXME("AU and AIFF files not supported yet!\n");
216 static HRESULT
WAVEParser_InputPin_PreConnect(IPin
* iface
, IPin
* pConnectPin
)
218 PullPin
*This
= (PullPin
*)iface
;
222 LONGLONG pos
= 0; /* in bytes */
224 ALLOCATOR_PROPERTIES props
;
226 WAVEParserImpl
* pWAVEParser
= (WAVEParserImpl
*)This
->pin
.pinInfo
.pFilter
;
227 LONGLONG length
, avail
;
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 amt
.majortype
= MEDIATYPE_Audio
;
261 amt
.formattype
= FORMAT_WaveFormatEx
;
262 amt
.cbFormat
= chunk
.cb
;
263 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
265 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, amt
.cbFormat
, amt
.pbFormat
);
266 amt
.subtype
= MEDIATYPE_Audio
;
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;
296 pWAVEParser
->dwSampleSize
= ((WAVEFORMATEX
*)amt
.pbFormat
)->nBlockAlign
;
297 IAsyncReader_Length(This
->pReader
, &length
, &avail
);
298 pWAVEParser
->dwLength
= length
/ (ULONGLONG
)pWAVEParser
->dwSampleSize
;
299 pWAVEParser
->fSamplesPerSec
= ((WAVEFORMATEX
*)amt
.pbFormat
)->nAvgBytesPerSec
/ ((WAVEFORMATEX
*)amt
.pbFormat
)->nBlockAlign
;
300 hr
= Parser_AddPin(&(pWAVEParser
->Parser
), &piOutput
, &props
, &amt
);
302 TRACE("WAVE File ok\n");
307 static HRESULT
WAVEParser_Cleanup(LPVOID iface
)
309 WAVEParserImpl
*This
= (WAVEParserImpl
*)iface
;
311 TRACE("(%p)->()\n", This
);
313 if (This
->pCurrentSample
)
314 IMediaSample_Release(This
->pCurrentSample
);
315 This
->pCurrentSample
= NULL
;
320 HRESULT
WAVEParser_create(IUnknown
* pUnkOuter
, LPVOID
* ppv
)
323 WAVEParserImpl
* This
;
325 TRACE("(%p, %p)\n", pUnkOuter
, ppv
);
330 return CLASS_E_NOAGGREGATION
;
332 /* Note: This memory is managed by the transform filter once created */
333 This
= CoTaskMemAlloc(sizeof(WAVEParserImpl
));
335 This
->pCurrentSample
= NULL
;
337 hr
= Parser_Create(&(This
->Parser
), &CLSID_WAVEParser
, WAVEParser_Sample
, WAVEParser_QueryAccept
, WAVEParser_InputPin_PreConnect
, WAVEParser_Cleanup
, NULL
, NULL
, NULL
);