advapi32: Create MachineGuid value if it doesn't exist.
[wine] / dlls / quartz / waveparser.c
1 /*
2  * WAVE Parser Filter
3  *
4  * Copyright 2005 Christian Costa
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "quartz_private.h"
22 #include "control_private.h"
23 #include "pin.h"
24
25 #include "uuids.h"
26 #include "aviriff.h"
27 #include "vfwmsgs.h"
28 #include "mmsystem.h"
29
30 #include "fourcc.h"
31
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
34
35 #include <math.h>
36 #include <assert.h>
37
38 #include "parser.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
41
42 static const WCHAR wcsOutputPinName[] = {'o','u','t','p','u','t',' ','p','i','n',0};
43
44 typedef struct WAVEParserImpl
45 {
46     ParserImpl Parser;
47     IMediaSample * pCurrentSample;
48     LONGLONG StartOfFile; /* in media time */
49     LONGLONG EndOfFile;
50     DWORD dwSampleSize;
51     FLOAT fSamplesPerSec;
52     DWORD dwLength;
53 } WAVEParserImpl;
54
55 static HRESULT WAVEParser_Sample(LPVOID iface, IMediaSample * pSample)
56 {
57     WAVEParserImpl *This = (WAVEParserImpl *)iface;
58     LPBYTE pbSrcStream = NULL;
59     long cbSrcStream = 0;
60     REFERENCE_TIME tStart, tStop;
61     HRESULT hr;
62     BOOL bMoreData = TRUE;
63     Parser_OutputPin * pOutputPin;
64     BYTE * pbDstStream;
65     long cbDstStream;
66     long chunk_remaining_bytes = 0;
67     long offset_src = 0;
68  
69     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
70
71     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
72
73     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
74
75     assert(BYTES_FROM_MEDIATIME(tStop - tStart) == cbSrcStream);
76
77     pOutputPin = (Parser_OutputPin *)This->Parser.ppPins[1];
78
79     if (tStop < This->StartOfFile)
80         return S_OK;
81
82     if (tStart < This->StartOfFile)
83         offset_src = BYTES_FROM_MEDIATIME(This->StartOfFile - tStart);
84
85     while (bMoreData)
86     {
87         if (!This->pCurrentSample)
88         {
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);
92
93             if (SUCCEEDED(hr))
94             {
95                 hr = IMediaSample_SetActualDataLength(This->pCurrentSample, 0);
96                 assert(hr == S_OK);
97             }
98             else
99             {
100                 TRACE("Skipping sending sample due to error (%x)\n", hr);
101                 This->pCurrentSample = NULL;
102                 break;
103             }
104         }
105
106         hr = IMediaSample_GetPointer(This->pCurrentSample, &pbDstStream);
107
108         if (SUCCEEDED(hr))
109         {
110             cbDstStream = IMediaSample_GetSize(This->pCurrentSample);
111
112             chunk_remaining_bytes = cbDstStream - IMediaSample_GetActualDataLength(This->pCurrentSample);
113         
114             assert(chunk_remaining_bytes >= 0);
115             assert(chunk_remaining_bytes <= cbDstStream - IMediaSample_GetActualDataLength(This->pCurrentSample));
116         }
117
118         if (chunk_remaining_bytes <= cbSrcStream - offset_src)
119         {
120             if (SUCCEEDED(hr))
121             {
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));
124                 assert(hr == S_OK);
125             }
126
127             if (SUCCEEDED(hr))
128             {
129                 REFERENCE_TIME tAviStart, tAviStop;
130
131                 /* FIXME: hack */
132                 if (pOutputPin->dwSamplesProcessed == 0) {
133                     IMediaSample_SetDiscontinuity(This->pCurrentSample, TRUE);
134                     IMediaSample_SetSyncPoint(This->pCurrentSample, TRUE);
135                 }
136
137                 pOutputPin->dwSamplesProcessed++;
138
139                 if (This->dwSampleSize)
140                     tAviStart = (LONGLONG)ceil(10000000.0 * (float)(pOutputPin->dwSamplesProcessed - 1) * (float)IMediaSample_GetActualDataLength(This->pCurrentSample) / ((float)This->dwSampleSize * This->fSamplesPerSec));
141                 else
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));
145                 else
146                     tAviStop = (LONGLONG)ceil(10000000.0 * (float)pOutputPin->dwSamplesProcessed / (float)This->fSamplesPerSec);
147
148                 IMediaSample_SetTime(This->pCurrentSample, &tAviStart, &tAviStop);
149
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);
153             }
154
155             if (This->pCurrentSample)
156                 IMediaSample_Release(This->pCurrentSample);
157                     
158             This->pCurrentSample = NULL;
159         }
160         else
161         {
162             if (SUCCEEDED(hr))
163             {
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));
166             }
167             bMoreData = FALSE;
168         }
169         offset_src += chunk_remaining_bytes;
170     }
171
172     if (tStop >= This->EndOfFile)
173     {
174         int i;
175
176         TRACE("End of file reached\n");
177
178         for (i = 0; i < This->Parser.cStreams; i++)
179         {
180             IPin* ppin;
181             HRESULT hr;
182
183             TRACE("Send End Of Stream to output pin %d\n", i);
184
185             hr = IPin_ConnectedTo(This->Parser.ppPins[i+1], &ppin);
186             if (SUCCEEDED(hr))
187             {
188                 hr = IPin_EndOfStream(ppin);
189                 IPin_Release(ppin);
190             }
191             if (FAILED(hr))
192             {
193                 ERR("%x\n", hr);
194                 break;
195             }
196         }
197
198         /* Force the pullpin thread to stop */
199         hr = S_FALSE;
200     }
201
202     return hr;
203 }
204
205 static HRESULT WAVEParser_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
206 {
207     if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
208         return S_FALSE;
209     if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_WAVE))
210         return S_OK;
211     if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_AU) || IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_AIFF))
212         FIXME("AU and AIFF files not supported yet!\n");
213     return S_FALSE;
214 }
215
216 static HRESULT WAVEParser_InputPin_PreConnect(IPin * iface, IPin * pConnectPin)
217 {
218     PullPin *This = (PullPin *)iface;
219     HRESULT hr;
220     RIFFLIST list;
221     RIFFCHUNK chunk;
222     LONGLONG pos = 0; /* in bytes */
223     PIN_INFO piOutput;
224     ALLOCATOR_PROPERTIES props;
225     AM_MEDIA_TYPE amt;
226     WAVEParserImpl * pWAVEParser = (WAVEParserImpl *)This->pin.pinInfo.pFilter;
227     LONGLONG length, avail;
228
229     piOutput.dir = PINDIR_OUTPUT;
230     piOutput.pFilter = (IBaseFilter *)This;
231     lstrcpynW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
232     
233     hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
234     pos += sizeof(list);
235
236     if (list.fcc != ckidRIFF)
237     {
238         ERR("Input stream not a RIFF file\n");
239         return E_FAIL;
240     }
241     if (list.cb > 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */
242     {
243         ERR("Input stream violates RIFF spec\n");
244         return E_FAIL;
245     }
246     if (list.fccListType != mmioFOURCC('W','A','V','E'))
247     {
248         ERR("Input stream not an WAVE RIFF file\n");
249         return E_FAIL;
250     }
251
252     hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(chunk), (BYTE *)&chunk);
253     pos += sizeof(chunk);
254     if (chunk.fcc != mmioFOURCC('f','m','t',' '))
255     {
256         ERR("Expected 'fmt ' chunk, but got %.04s\n", (LPSTR)&chunk.fcc);
257         return E_FAIL;
258     }
259
260     amt.majortype = MEDIATYPE_Audio;
261     amt.formattype = FORMAT_WaveFormatEx;
262     amt.cbFormat = chunk.cb;
263     amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
264     amt.pUnk = NULL;
265     hr = IAsyncReader_SyncRead(This->pReader, pos, amt.cbFormat, amt.pbFormat);
266     amt.subtype = MEDIATYPE_Audio;
267     amt.subtype.Data1 = ((WAVEFORMATEX*)amt.pbFormat)->wFormatTag;
268
269     pos += chunk.cb;
270     hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(chunk), (BYTE *)&chunk);
271     if (chunk.fcc == mmioFOURCC('f','a','c','t'))
272     {
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);
276     }
277     if (chunk.fcc != mmioFOURCC('d','a','t','a'))
278     {
279         ERR("Expected 'data' chunk, but got %.04s\n", (LPSTR)&chunk.fcc);
280         return E_FAIL;
281     }
282
283     if (hr == S_OK)
284     {
285         pWAVEParser->StartOfFile = MEDIATIME_FROM_BYTES(pos + sizeof(RIFFCHUNK));
286         pWAVEParser->EndOfFile = MEDIATIME_FROM_BYTES(pos + chunk.cb + sizeof(RIFFCHUNK));
287     }
288
289     if (hr != S_OK)
290         return E_FAIL;
291
292     props.cbAlign = ((WAVEFORMATEX*)amt.pbFormat)->nBlockAlign;
293     props.cbPrefix = 0;
294     props.cbBuffer = 4096;
295     props.cBuffers = 2;
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);
301     
302     TRACE("WAVE File ok\n");
303
304     return hr;
305 }
306
307 static HRESULT WAVEParser_Cleanup(LPVOID iface)
308 {
309     WAVEParserImpl *This = (WAVEParserImpl*)iface;
310
311     TRACE("(%p)->()\n", This);
312
313     if (This->pCurrentSample)
314         IMediaSample_Release(This->pCurrentSample);
315     This->pCurrentSample = NULL;
316
317     return S_OK;
318 }
319
320 HRESULT WAVEParser_create(IUnknown * pUnkOuter, LPVOID * ppv)
321 {
322     HRESULT hr;
323     WAVEParserImpl * This;
324
325     TRACE("(%p, %p)\n", pUnkOuter, ppv);
326
327     *ppv = NULL;
328
329     if (pUnkOuter)
330         return CLASS_E_NOAGGREGATION;
331
332     /* Note: This memory is managed by the transform filter once created */
333     This = CoTaskMemAlloc(sizeof(WAVEParserImpl));
334
335     This->pCurrentSample = NULL;
336
337     hr = Parser_Create(&(This->Parser), &CLSID_WAVEParser, WAVEParser_Sample, WAVEParser_QueryAccept, WAVEParser_InputPin_PreConnect, WAVEParser_Cleanup);
338
339     if (FAILED(hr))
340         return hr;
341
342     *ppv = (LPVOID)This;
343
344     return hr;
345 }