push 6fe5edf8439c19d3885814583531c2f2b1495177
[wine/hacks.git] / dlls / quartz / waveparser.c
blob5aef9ef2cedb83e4b124ed7801289638c9870fa9
1 /*
2 * WAVE Parser Filter
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"
23 #include "pin.h"
25 #include "uuids.h"
26 #include "aviriff.h"
27 #include "vfwmsgs.h"
28 #include "mmsystem.h"
30 #include "wine/unicode.h"
31 #include "wine/debug.h"
33 #include <math.h>
34 #include <assert.h>
36 #include "parser.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
44 ParserImpl Parser;
45 IMediaSample * pCurrentSample;
46 LONGLONG StartOfFile; /* in media time */
47 LONGLONG EndOfFile;
48 DWORD dwSampleSize;
49 DWORD nSamplesPerSec;
50 DWORD dwLength;
51 } WAVEParserImpl;
53 static LONGLONG bytepos_to_duration(WAVEParserImpl *This, LONGLONG bytepos)
55 LONGLONG duration = BYTES_FROM_MEDIATIME(bytepos - This->StartOfFile);
56 duration *= 10000000;
57 duration /= (This->dwSampleSize * This->nSamplesPerSec);
59 return duration;
62 static LONGLONG duration_to_bytepos(WAVEParserImpl *This, LONGLONG duration)
64 LONGLONG bytepos;
66 bytepos = (This->dwSampleSize * This->nSamplesPerSec);
67 bytepos *= duration;
68 bytepos /= 10000000;
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;
79 long cbSrcStream = 0;
80 REFERENCE_TIME tStart, tStop;
81 HRESULT hr;
82 BOOL bMoreData = TRUE;
83 Parser_OutputPin * pOutputPin;
84 BYTE * pbDstStream;
85 long cbDstStream;
86 long chunk_remaining_bytes = 0;
87 long offset_src = 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)))
102 HRESULT hr;
104 /* Unset advancement */
105 This->Parser.pInputPin->rtCurrent -= MEDIATIME_FROM_BYTES(cbSrcStream);
107 hr = OutputPin_SendSample(&pOutputPin->pin, This->pCurrentSample);
109 if (hr != S_OK)
110 return hr;
112 IMediaSample_Release(This->pCurrentSample);
113 This->pCurrentSample = NULL;
115 This->Parser.pInputPin->rtCurrent += MEDIATIME_FROM_BYTES(cbSrcStream);
118 if (tStop < This->StartOfFile)
119 return S_OK;
121 if (tStart < This->StartOfFile)
122 offset_src = BYTES_FROM_MEDIATIME(This->StartOfFile - tStart);
124 while (bMoreData)
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);
132 if (SUCCEEDED(hr))
134 hr = IMediaSample_SetActualDataLength(This->pCurrentSample, 0);
135 assert(hr == S_OK);
137 else
139 TRACE("Skipping sending sample due to error (%x)\n", hr);
140 This->pCurrentSample = NULL;
141 break;
145 hr = IMediaSample_GetPointer(This->pCurrentSample, &pbDstStream);
147 if (SUCCEEDED(hr))
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)
159 if (SUCCEEDED(hr))
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));
163 assert(hr == S_OK);
166 if (SUCCEEDED(hr))
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;
193 if (hr == S_FALSE)
195 /* Break out */
196 This->Parser.pInputPin->rtCurrent -= MEDIATIME_FROM_BYTES(cbSrcStream - offset_src - chunk_remaining_bytes);
197 hr = S_OK;
198 break;
202 else
204 if (SUCCEEDED(hr))
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));
209 bMoreData = FALSE;
211 offset_src += chunk_remaining_bytes;
214 if (tStop >= This->EndOfFile || (bytepos_to_duration(This, tStop) >= This->Parser.mediaSeeking.llStop))
216 int i;
218 TRACE("End of file reached\n");
220 for (i = 0; i < This->Parser.cStreams; i++)
222 IPin* ppin;
223 HRESULT hr;
225 TRACE("Send End Of Stream to output pin %d\n", i);
227 hr = IPin_ConnectedTo(This->Parser.ppPins[i+1], &ppin);
228 if (SUCCEEDED(hr))
230 hr = IPin_EndOfStream(ppin);
231 IPin_Release(ppin);
233 if (FAILED(hr))
235 ERR("%x\n", hr);
236 break;
240 /* Force the pullpin thread to stop */
241 hr = S_FALSE;
244 return hr;
247 static HRESULT WAVEParser_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
249 if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
250 return S_FALSE;
251 if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_WAVE))
252 return S_OK;
253 if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_AU) || IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_AIFF))
254 FIXME("AU and AIFF files not supported yet!\n");
255 return S_FALSE;
258 static HRESULT WAVEParserImpl_seek(IBaseFilter *iface)
260 WAVEParserImpl *This = (WAVEParserImpl *)iface;
261 PullPin *pPin = This->Parser.pInputPin;
262 IPin *victim = NULL;
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);
270 if (newpos > endpos)
272 WARN("Requesting position %x%08x beyond end of stream %x%08x\n", (DWORD)(newpos>>32), (DWORD)newpos, (DWORD)(endpos>>32), (DWORD)endpos);
273 return E_INVALIDARG;
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);
279 return S_OK;
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);
290 if (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);
304 return S_OK;
307 static HRESULT WAVEParser_InputPin_PreConnect(IPin * iface, IPin * pConnectPin)
309 PullPin *This = (PullPin *)iface;
310 HRESULT hr;
311 RIFFLIST list;
312 RIFFCHUNK chunk;
313 LONGLONG pos = 0; /* in bytes */
314 PIN_INFO piOutput;
315 ALLOCATOR_PROPERTIES props;
316 AM_MEDIA_TYPE amt;
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);
325 pos += sizeof(list);
327 if (list.fcc != FOURCC_RIFF)
329 ERR("Input stream not a RIFF file\n");
330 return E_FAIL;
332 if (list.cb > 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */
334 ERR("Input stream violates RIFF spec\n");
335 return E_FAIL;
337 if (list.fccListType != mmioFOURCC('W','A','V','E'))
339 ERR("Input stream not an WAVE RIFF file\n");
340 return E_FAIL;
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);
348 return E_FAIL;
351 amt.majortype = MEDIATYPE_Audio;
352 amt.formattype = FORMAT_WaveFormatEx;
353 amt.cbFormat = chunk.cb;
354 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
355 amt.pUnk = NULL;
356 hr = IAsyncReader_SyncRead(This->pReader, pos, amt.cbFormat, amt.pbFormat);
357 amt.subtype = MEDIATYPE_Audio;
358 amt.subtype.Data1 = ((WAVEFORMATEX*)amt.pbFormat)->wFormatTag;
360 pos += chunk.cb;
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);
371 return E_FAIL;
374 if (hr == S_OK)
376 pWAVEParser->StartOfFile = MEDIATIME_FROM_BYTES(pos + sizeof(RIFFCHUNK));
377 pWAVEParser->EndOfFile = MEDIATIME_FROM_BYTES(pos + chunk.cb + sizeof(RIFFCHUNK));
380 if (hr != S_OK)
381 return E_FAIL;
383 props.cbAlign = ((WAVEFORMATEX*)amt.pbFormat)->nBlockAlign;
384 props.cbPrefix = 0;
385 props.cbBuffer = 4096;
386 props.cBuffers = 2;
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");
403 return hr;
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;
416 return S_OK;
419 static HRESULT WAVEParser_disconnect(LPVOID iface)
421 /* TODO: Find and plug memory leaks */
422 return S_OK;
425 HRESULT WAVEParser_create(IUnknown * pUnkOuter, LPVOID * ppv)
427 HRESULT hr;
428 WAVEParserImpl * This;
430 TRACE("(%p, %p)\n", pUnkOuter, ppv);
432 *ppv = NULL;
434 if (pUnkOuter)
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);
444 if (FAILED(hr))
445 return hr;
447 *ppv = (LPVOID)This;
449 return hr;