mshtml: Added IDispatchEx support to text node.
[wine] / dlls / quartz / acmwrapper.c
1 /*
2  * ACM Wrapper
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 "config.h"
22
23 #include "quartz_private.h"
24 #include "pin.h"
25
26 #include "uuids.h"
27 #include "mmreg.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "dshow.h"
31 #include "strmif.h"
32 #include "vfwmsgs.h"
33 #include "msacm.h"
34
35 #include <assert.h>
36
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
39
40 #include "transform.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
43
44 typedef struct ACMWrapperImpl
45 {
46     TransformFilterImpl tf;
47     HACMSTREAM has;
48     LPWAVEFORMATEX pWfIn;
49     LPWAVEFORMATEX pWfOut;
50 } ACMWrapperImpl;
51
52 static HRESULT ACMWrapper_ProcessSampleData(TransformFilterImpl* pTransformFilter, IMediaSample *pSample)
53 {
54     ACMWrapperImpl* This = (ACMWrapperImpl*)pTransformFilter;
55     AM_MEDIA_TYPE amt;
56     IMediaSample* pOutSample = NULL;
57     DWORD cbDstStream, cbSrcStream;
58     LPBYTE pbDstStream;
59     LPBYTE pbSrcStream = NULL;
60     ACMSTREAMHEADER ash;
61     BOOL unprepare_header = FALSE, preroll;
62     MMRESULT res;
63     HRESULT hr;
64     LONGLONG tStart = -1, tStop = -1, tMed;
65
66     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
67     if (FAILED(hr))
68     {
69         ERR("Cannot get pointer to sample data (%x)\n", hr);
70         return hr;
71     }
72
73     preroll = (IMediaSample_IsPreroll(pSample) == S_OK);
74
75     IMediaSample_GetTime(pSample, &tStart, &tStop);
76     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
77
78     TRACE("Sample data ptr = %p, size = %ld\n", pbSrcStream, (long)cbSrcStream);
79
80     hr = IPin_ConnectionMediaType(This->tf.ppPins[0], &amt);
81     if (FAILED(hr)) {
82         ERR("Unable to retrieve media type\n");
83         return hr;
84     }
85
86     ash.pbSrc = pbSrcStream;
87     ash.cbSrcLength = cbSrcStream;
88
89     while(hr == S_OK && ash.cbSrcLength)
90     {
91         hr = OutputPin_GetDeliveryBuffer((OutputPin*)This->tf.ppPins[1], &pOutSample, NULL, NULL, 0);
92         if (FAILED(hr)) {
93             ERR("Unable to get delivery buffer (%x)\n", hr);
94             return hr;
95         }
96         IMediaSample_SetPreroll(pOutSample, preroll);
97
98         hr = IMediaSample_SetActualDataLength(pOutSample, 0);
99         assert(hr == S_OK);
100
101         hr = IMediaSample_GetPointer(pOutSample, &pbDstStream);
102         if (FAILED(hr)) {
103             ERR("Unable to get pointer to buffer (%x)\n", hr);
104             goto error;
105         }
106         cbDstStream = IMediaSample_GetSize(pOutSample);
107
108         ash.cbStruct = sizeof(ash);
109         ash.fdwStatus = 0;
110         ash.dwUser = 0;
111         ash.pbDst = pbDstStream;
112         ash.cbDstLength = cbDstStream;
113
114         if ((res = acmStreamPrepareHeader(This->has, &ash, 0))) {
115             ERR("Cannot prepare header %d\n", res);
116             goto error;
117         }
118         unprepare_header = TRUE;
119
120         if (IMediaSample_IsDiscontinuity(pSample) == S_OK)
121         {
122             res = acmStreamConvert(This->has, &ash, ACM_STREAMCONVERTF_START);
123             IMediaSample_SetDiscontinuity(pOutSample, TRUE);
124             /* One sample could be converted to multiple packets */
125             IMediaSample_SetDiscontinuity(pSample, FALSE);
126         }
127         else
128         {
129             res = acmStreamConvert(This->has, &ash, 0);
130             IMediaSample_SetDiscontinuity(pOutSample, FALSE);
131         }
132
133         if (res)
134         {
135             if(res != MMSYSERR_MOREDATA)
136                 ERR("Cannot convert data header %d\n", res);
137             goto error;
138         }
139
140         TRACE("used in %u/%u, used out %u/%u\n", ash.cbSrcLengthUsed, ash.cbSrcLength, ash.cbDstLengthUsed, ash.cbDstLength);
141
142         hr = IMediaSample_SetActualDataLength(pOutSample, ash.cbDstLengthUsed);
143         assert(hr == S_OK);
144
145         if (!ash.cbSrcLengthUsed)
146         {
147             WARN("Sample was skipped\n");
148             ash.cbSrcLength = 0;
149             goto error;
150         }
151
152         TRACE("Sample start time: %u.%03u\n", (DWORD)(tStart/10000000), (DWORD)((tStart/10000)%1000));
153         if (ash.cbSrcLengthUsed == cbSrcStream)
154         {
155             IMediaSample_SetTime(pOutSample, &tStart, &tStop);
156             tStart = tStop;
157         }
158         else if (tStop != tStart)
159         {
160             tMed = tStop - tStart;
161             tMed = tStart + tMed * ash.cbSrcLengthUsed / cbSrcStream;
162             IMediaSample_SetTime(pOutSample, &tStart, &tMed);
163             tStart = tMed;
164         }
165         else
166         {
167             ERR("No valid timestamp found\n");
168             IMediaSample_SetTime(pOutSample, NULL, NULL);
169         }
170         TRACE("Sample stop time: %u.%03u\n", (DWORD)(tStart/10000000), (DWORD)((tStart/10000)%1000));
171
172         hr = OutputPin_SendSample((OutputPin*)This->tf.ppPins[1], pOutSample);
173
174         if (hr != S_OK && hr != VFW_E_NOT_CONNECTED) {
175             if (FAILED(hr))
176                 ERR("Error sending sample (%x)\n", hr);
177             goto error;
178         }
179
180 error:
181         if (unprepare_header && (res = acmStreamUnprepareHeader(This->has, &ash, 0)))
182             ERR("Cannot unprepare header %d\n", res);
183         unprepare_header = FALSE;
184         ash.pbSrc += ash.cbSrcLengthUsed;
185         ash.cbSrcLength -= ash.cbSrcLengthUsed;
186
187         if (pOutSample)
188             IMediaSample_Release(pOutSample);
189         pOutSample = NULL;
190     }
191
192     return hr;
193 }
194
195 static HRESULT ACMWrapper_ConnectInput(TransformFilterImpl* pTransformFilter, const AM_MEDIA_TYPE * pmt)
196 {
197     ACMWrapperImpl* This = (ACMWrapperImpl*)pTransformFilter;
198     MMRESULT res;
199
200     TRACE("(%p)->(%p)\n", This, pmt);
201
202     /* Check root (GUID w/o FOURCC) */
203     if ((IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio)) &&
204         (!memcmp(((const char *)&pmt->subtype)+4, ((const char *)&MEDIATYPE_Audio)+4, sizeof(GUID)-4)) &&
205         (IsEqualIID(&pmt->formattype, &FORMAT_WaveFormatEx)))
206     {
207         HACMSTREAM drv;
208         AM_MEDIA_TYPE* outpmt = &((OutputPin*)This->tf.ppPins[1])->pin.mtCurrent;
209         This->pWfIn = (LPWAVEFORMATEX)pmt->pbFormat;
210
211         /* HACK */
212         /* TRACE("ALIGN = %d\n", pACMWrapper->pWfIn->nBlockAlign); */
213         /* pACMWrapper->pWfIn->nBlockAlign = 1; */
214
215         /* Set output audio data to PCM */
216         CopyMediaType(outpmt, pmt);
217         outpmt->subtype.Data1 = WAVE_FORMAT_PCM;
218         This->pWfOut = (WAVEFORMATEX*)outpmt->pbFormat;
219         This->pWfOut->wFormatTag = WAVE_FORMAT_PCM;
220         This->pWfOut->wBitsPerSample = 16;
221         This->pWfOut->nBlockAlign = 4;
222         This->pWfOut->cbSize = 0;
223         This->pWfOut->nAvgBytesPerSec = This->pWfOut->nChannels * This->pWfOut->nSamplesPerSec
224                                                 * (This->pWfOut->wBitsPerSample/8);
225
226         if (!(res = acmStreamOpen(&drv, NULL, This->pWfIn, This->pWfOut, NULL, 0, 0, 0)))
227         {
228             This->has = drv;
229
230             /* Update buffer size of media samples in output */
231             ((OutputPin*)This->tf.ppPins[1])->allocProps.cbBuffer = This->pWfOut->nAvgBytesPerSec / 2;
232             TRACE("Connection accepted\n");
233             return S_OK;
234         }
235         else
236             FIXME("acmStreamOpen returned %d\n", res);
237         FreeMediaType(outpmt);
238         TRACE("Unable to find a suitable ACM decompressor\n");
239     }
240
241     TRACE("Connection refused\n");
242     return VFW_E_TYPE_NOT_ACCEPTED;
243 }
244
245 static HRESULT ACMWrapper_Cleanup(TransformFilterImpl* pTransformFilter)
246 {
247     ACMWrapperImpl* This = (ACMWrapperImpl*)pTransformFilter;
248
249     TRACE("(%p)->()\n", This);
250     
251     if (This->has)
252         acmStreamClose(This->has, 0);
253
254     This->has = 0;
255     
256     return S_OK;
257 }
258
259 static const TransformFuncsTable ACMWrapper_FuncsTable = {
260     NULL,
261     ACMWrapper_ProcessSampleData,
262     NULL,
263     NULL,
264     ACMWrapper_ConnectInput,
265     ACMWrapper_Cleanup
266 };
267
268 HRESULT ACMWrapper_create(IUnknown * pUnkOuter, LPVOID * ppv)
269 {
270     HRESULT hr;
271     ACMWrapperImpl* This;
272
273     TRACE("(%p, %p)\n", pUnkOuter, ppv);
274
275     *ppv = NULL;
276
277     if (pUnkOuter)
278         return CLASS_E_NOAGGREGATION;
279
280     /* Note: This memory is managed by the transform filter once created */
281     This = CoTaskMemAlloc(sizeof(ACMWrapperImpl));
282     ZeroMemory(This, sizeof(ACMWrapperImpl));
283
284     hr = TransformFilter_Create(&(This->tf), &CLSID_ACMWrapper, &ACMWrapper_FuncsTable, NULL, NULL, NULL);
285
286     if (FAILED(hr))
287         return hr;
288
289     *ppv = (LPVOID)This;
290
291     return hr;
292 }