Implement ResetDC and PHYSICALOFFSET[X|Y] devcaps.
[wine] / dlls / quartz / acmwrap.c
1 /*
2  * Implements ACM Wrapper(CLSID_ACMWrapper).
3  *
4  *      FIXME - stub
5  *      FIXME - no encoding
6  *
7  * Copyright (C) 2002 Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24
25 #include "config.h"
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "winerror.h"
32 #include "msacm.h"
33 #include "strmif.h"
34 #include "control.h"
35 #include "amvideo.h"
36 #include "vfwmsgs.h"
37 #include "uuids.h"
38
39 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
41
42 #include "quartz_private.h"
43 #include "xform.h"
44 #include "mtype.h"
45
46
47 static const WCHAR ACMWrapper_FilterName[] =
48 {'A','C','M',' ','W','r','a','p','p','e','r',0};
49
50
51 typedef struct CACMWrapperImpl
52 {
53         HACMSTREAM      has;
54         WAVEFORMATEX*   pwfxIn;
55         AM_MEDIA_TYPE*  pmtOuts;
56         DWORD           cOuts;
57         BYTE*           pConvBuf;
58         DWORD           cbConvBlockSize;
59         DWORD           cbConvCached;
60         DWORD           cbConvAllocated;
61 } CACMWrapperImpl;
62
63
64 static
65 void ACMWrapper_CleanupMTypes( CACMWrapperImpl* This )
66 {
67         DWORD   n;
68
69         if ( This->pmtOuts == NULL ) return;
70         for ( n = 0; n < This->cOuts; n++ )
71         {
72                 QUARTZ_MediaType_Free( &This->pmtOuts[n] );
73         }
74         QUARTZ_FreeMem( This->pmtOuts );
75         This->pmtOuts = NULL;
76         This->cOuts = 0;
77 }
78
79 static
80 void ACMWrapper_CleanupConvBuf( CACMWrapperImpl* This )
81 {
82         if ( This->pConvBuf != NULL )
83         {
84                 QUARTZ_FreeMem( This->pConvBuf );
85                 This->pConvBuf = NULL;
86         }
87         This->cbConvBlockSize = 0;
88         This->cbConvCached = 0;
89         This->cbConvAllocated = 0;
90 }
91
92 static
93 const WAVEFORMATEX* ACMWrapper_GetAudioFmt( const AM_MEDIA_TYPE* pmt )
94 {
95         const WAVEFORMATEX*     pwfx;
96
97         if ( !IsEqualGUID( &pmt->majortype, &MEDIATYPE_Audio ) )
98                 return NULL;
99         if ( !IsEqualGUID( &pmt->subtype, &MEDIASUBTYPE_NULL ) &&
100                  !QUARTZ_MediaSubType_IsFourCC( &pmt->subtype ) )
101                 return NULL;
102         if ( !IsEqualGUID( &pmt->formattype, &FORMAT_WaveFormatEx ) )
103                 return NULL;
104         if ( pmt->pbFormat == NULL ||
105                  pmt->cbFormat < (sizeof(WAVEFORMATEX)-sizeof(WORD)) )
106                 return NULL;
107
108         pwfx = (const WAVEFORMATEX*)pmt->pbFormat;
109
110         if ( pwfx->wFormatTag != 1 && pmt->cbFormat < sizeof(WAVEFORMATEX) )
111                 return NULL;
112
113         return pwfx;
114 }
115
116 static
117 HRESULT ACMWrapper_SetupAudioFmt(
118         AM_MEDIA_TYPE* pmt,
119         DWORD cbFormat, WORD wFormatTag, DWORD dwBlockAlign )
120 {
121         ZeroMemory( pmt, sizeof(AM_MEDIA_TYPE) );
122         memcpy( &pmt->majortype, &MEDIATYPE_Audio, sizeof(GUID) );
123         QUARTZ_MediaSubType_FromFourCC( &pmt->subtype, (DWORD)wFormatTag );
124         pmt->bFixedSizeSamples = 1;
125         pmt->bTemporalCompression = 1;
126         pmt->lSampleSize = dwBlockAlign;
127         memcpy( &pmt->formattype, &FORMAT_WaveFormatEx, sizeof(GUID) );
128         pmt->pUnk = NULL;
129         pmt->cbFormat = cbFormat;
130         pmt->pbFormat = (BYTE*)CoTaskMemAlloc( cbFormat );
131         if ( pmt->pbFormat == NULL )
132                 return E_OUTOFMEMORY;
133
134         return S_OK;
135 }
136
137 static
138 void ACMWrapper_FillFmtPCM(
139         WAVEFORMATEX* pwfxOut,
140         const WAVEFORMATEX* pwfxIn,
141         WORD wBitsPerSampOut )
142 {
143         pwfxOut->wFormatTag = 1;
144         pwfxOut->nChannels = pwfxIn->nChannels;
145         pwfxOut->nSamplesPerSec = pwfxIn->nSamplesPerSec;
146         pwfxOut->nAvgBytesPerSec = ((DWORD)pwfxIn->nSamplesPerSec * (DWORD)pwfxIn->nChannels * (DWORD)wBitsPerSampOut) >> 3;
147         pwfxOut->nBlockAlign = (pwfxIn->nChannels * wBitsPerSampOut) >> 3;
148         pwfxOut->wBitsPerSample = wBitsPerSampOut;
149         pwfxOut->cbSize = 0;
150 }
151
152 static
153 BOOL ACMWrapper_IsSupported(
154         WAVEFORMATEX* pwfxOut,
155         WAVEFORMATEX* pwfxIn )
156 {
157         MMRESULT        mr;
158
159         mr = acmStreamOpen(
160                 NULL,(HACMDRIVER)NULL,
161                 pwfxIn,pwfxOut,NULL,
162                 0,0,ACM_STREAMOPENF_QUERY);
163         if ( mr == ACMERR_NOTPOSSIBLE )
164                 mr = acmStreamOpen(
165                         NULL,(HACMDRIVER)NULL,
166                         pwfxIn,pwfxOut,NULL,
167                         0,0,ACM_STREAMOPENF_NONREALTIME|ACM_STREAMOPENF_QUERY);
168         return !!(mr == MMSYSERR_NOERROR);
169 }
170
171 static
172 HRESULT ACMWrapper_StreamOpen(
173         HACMSTREAM* phas,
174         WAVEFORMATEX* pwfxOut,
175         WAVEFORMATEX* pwfxIn )
176 {
177         HACMSTREAM      has = (HACMSTREAM)NULL;
178         MMRESULT        mr;
179
180         mr = acmStreamOpen(
181                 &has,(HACMDRIVER)NULL,
182                 pwfxIn,pwfxOut,NULL,
183                 0,0,0);
184         if ( mr == ACMERR_NOTPOSSIBLE )
185                 mr = acmStreamOpen(
186                         &has,(HACMDRIVER)NULL,
187                         pwfxIn,pwfxOut,NULL,
188                         0,0,ACM_STREAMOPENF_NONREALTIME);
189         if ( mr != MMSYSERR_NOERROR )
190         {
191                 if ( mr == MMSYSERR_NOMEM )
192                         return E_OUTOFMEMORY;
193                 return E_FAIL;
194         }
195
196         *phas = has;
197         return S_OK;
198 }
199
200 /***************************************************************************
201  *
202  *      CACMWrapperImpl methods
203  *
204  */
205
206 static void ACMWrapper_Close( CACMWrapperImpl* This )
207 {
208         if ( This->has != (HACMSTREAM)NULL )
209         {
210                 acmStreamReset( This->has, 0 );
211                 acmStreamClose( This->has, 0 );
212                 This->has = (HACMSTREAM)NULL;
213         }
214 }
215
216 static HRESULT ACMWrapper_Init( CTransformBaseImpl* pImpl )
217 {
218         CACMWrapperImpl*        This = pImpl->m_pUserData;
219
220         TRACE("(%p)\n",This);
221
222         if ( This != NULL )
223                 return NOERROR;
224
225         This = (CACMWrapperImpl*)QUARTZ_AllocMem( sizeof(CACMWrapperImpl) );
226         if ( This == NULL )
227                 return E_OUTOFMEMORY;
228         ZeroMemory( This, sizeof(CACMWrapperImpl) );
229         pImpl->m_pUserData = This;
230
231         /* construct */
232         This->has = (HACMSTREAM)NULL;
233         This->pwfxIn = NULL;
234         This->pmtOuts = NULL;
235         This->cOuts = 0;
236         This->pConvBuf = NULL;
237
238         return S_OK;
239 }
240
241 static HRESULT ACMWrapper_Cleanup( CTransformBaseImpl* pImpl )
242 {
243         CACMWrapperImpl*        This = pImpl->m_pUserData;
244
245         TRACE("(%p)\n",This);
246
247         if ( This == NULL )
248                 return NOERROR;
249
250         /* destruct */
251         ACMWrapper_Close( This );
252         QUARTZ_FreeMem( This->pwfxIn );
253         ACMWrapper_CleanupMTypes( This );
254         ACMWrapper_CleanupConvBuf( This );
255
256         QUARTZ_FreeMem( This );
257         pImpl->m_pUserData = NULL;
258
259         return S_OK;
260 }
261
262 static HRESULT ACMWrapper_CheckMediaType( CTransformBaseImpl* pImpl, const AM_MEDIA_TYPE* pmtIn, const AM_MEDIA_TYPE* pmtOut )
263 {
264         CACMWrapperImpl*        This = pImpl->m_pUserData;
265         const WAVEFORMATEX*     pwfxIn;
266         const WAVEFORMATEX*     pwfxOut;
267         WAVEFORMATEX    wfx;
268
269         FIXME("(%p)\n",This);
270         if ( This == NULL )
271                 return E_UNEXPECTED;
272
273         pwfxIn = ACMWrapper_GetAudioFmt(pmtIn);
274         if ( pwfxIn == NULL ||
275              pwfxIn->wFormatTag == 0 ||
276              pwfxIn->wFormatTag == 1 )
277         {
278                 TRACE("pwfxIn is not a compressed audio\n");
279                 return E_FAIL;
280         }
281         if ( pmtOut != NULL )
282         {
283                 pwfxOut = ACMWrapper_GetAudioFmt(pmtOut);
284                 if ( pwfxOut == NULL || pwfxOut->wFormatTag != 1 )
285                 {
286                         TRACE("pwfxOut is not a linear PCM\n");
287                         return E_FAIL;
288                 }
289                 if ( pwfxIn->nChannels != pwfxOut->nChannels ||
290                      pwfxIn->nSamplesPerSec != pwfxOut->nSamplesPerSec )
291                 {
292                         TRACE("nChannels or nSamplesPerSec is not matched\n");
293                         return E_FAIL;
294                 }
295                 if ( !ACMWrapper_IsSupported((WAVEFORMATEX*)pwfxOut,(WAVEFORMATEX*)pwfxIn) )
296                 {
297                         TRACE("specified formats are not supported by ACM\n");
298                         return E_FAIL;
299                 }
300         }
301         else
302         {
303                 ACMWrapper_FillFmtPCM(&wfx,pwfxIn,8);
304                 if ( ACMWrapper_IsSupported(&wfx,(WAVEFORMATEX*)pwfxIn) )
305                 {
306                         TRACE("compressed audio - can be decoded to 8bit\n");
307                         return S_OK;
308                 }
309                 ACMWrapper_FillFmtPCM(&wfx,pwfxIn,16);
310                 if ( ACMWrapper_IsSupported(&wfx,(WAVEFORMATEX*)pwfxIn) )
311                 {
312                         TRACE("compressed audio - can be decoded to 16bit\n");
313                         return S_OK;
314                 }
315
316                 TRACE("unhandled audio %04x\n",(unsigned)pwfxIn->wFormatTag);
317                 return E_FAIL;
318         }
319
320         return S_OK;
321 }
322
323 static HRESULT ACMWrapper_GetOutputTypes( CTransformBaseImpl* pImpl, const AM_MEDIA_TYPE* pmtIn, const AM_MEDIA_TYPE** ppmtAcceptTypes, ULONG* pcAcceptTypes )
324 {
325         CACMWrapperImpl*        This = pImpl->m_pUserData;
326         HRESULT hr;
327         const WAVEFORMATEX*     pwfxIn;
328         AM_MEDIA_TYPE*          pmtTry;
329         WAVEFORMATEX*           pwfxTry;
330
331         FIXME("(%p)\n",This);
332         hr = ACMWrapper_CheckMediaType( pImpl, pmtIn, NULL );
333         if ( FAILED(hr) )
334                 return hr;
335         pwfxIn = (const WAVEFORMATEX*)pmtIn->pbFormat;
336
337         ACMWrapper_CleanupMTypes( This );
338         This->pmtOuts = QUARTZ_AllocMem( sizeof(AM_MEDIA_TYPE) * 2 );
339         if ( This->pmtOuts == NULL )
340                 return E_OUTOFMEMORY;
341         This->cOuts = 0;
342
343         pmtTry = &This->pmtOuts[This->cOuts];
344         hr = ACMWrapper_SetupAudioFmt(
345                 pmtTry,
346                 sizeof(WAVEFORMATEX), 1,
347                 (pwfxIn->nChannels * 8) >> 3 );
348         if ( FAILED(hr) ) goto err;
349         pwfxTry = (WAVEFORMATEX*)pmtTry->pbFormat;
350         ACMWrapper_FillFmtPCM( pwfxTry, pwfxIn, 8 );
351         if ( ACMWrapper_IsSupported( pwfxTry, (WAVEFORMATEX*)pwfxIn ) )
352                 This->cOuts ++;
353
354         pmtTry = &This->pmtOuts[This->cOuts];
355         hr = ACMWrapper_SetupAudioFmt(
356                 pmtTry,
357                 sizeof(WAVEFORMATEX), 1,
358                 (pwfxIn->nChannels * 16) >> 3 );
359         if ( FAILED(hr) ) goto err;
360         pwfxTry = (WAVEFORMATEX*)pmtTry->pbFormat;
361         ACMWrapper_FillFmtPCM( pwfxTry, pwfxIn, 16 );
362         if ( ACMWrapper_IsSupported( pwfxTry, (WAVEFORMATEX*)pwfxIn ) )
363                 This->cOuts ++;
364
365         *ppmtAcceptTypes = This->pmtOuts;
366         *pcAcceptTypes = This->cOuts;
367
368         return S_OK;
369 err:
370         ACMWrapper_CleanupMTypes( This );
371         return hr;
372 }
373
374 static HRESULT
375 ACMWrapper_GetConvBufSize(
376         CTransformBaseImpl* pImpl,
377         CACMWrapperImpl* This,
378         DWORD* pcbInput, DWORD* pcbOutput,
379         const AM_MEDIA_TYPE* pmtOut, const AM_MEDIA_TYPE* pmtIn )
380 {
381         HRESULT hr;
382         const WAVEFORMATEX* pwfxIn;
383         const WAVEFORMATEX* pwfxOut;
384         HACMSTREAM      has;
385         MMRESULT        mr;
386         DWORD   cbInput;
387         DWORD   cbOutput;
388
389         if ( This == NULL )
390                 return E_UNEXPECTED;
391
392         hr = ACMWrapper_CheckMediaType( pImpl, pmtIn, pmtOut );
393         if ( FAILED(hr) )
394                 return hr;
395         pwfxIn = (const WAVEFORMATEX*)pmtIn->pbFormat;
396         pwfxOut = (const WAVEFORMATEX*)pmtOut->pbFormat;
397
398         hr = ACMWrapper_StreamOpen(
399                 &has, (WAVEFORMATEX*)pwfxOut, (WAVEFORMATEX*)pwfxIn );
400         if ( FAILED(hr) )
401                 return hr;
402
403         cbInput = (pwfxIn->nAvgBytesPerSec + pwfxIn->nBlockAlign - 1) / pwfxIn->nBlockAlign * pwfxIn->nBlockAlign;
404         cbOutput = 0;
405
406         mr = acmStreamSize( has, cbInput, &cbOutput, ACM_STREAMSIZEF_SOURCE );
407         acmStreamClose( has, 0 );
408
409         if ( mr != MMSYSERR_NOERROR || cbOutput == 0 )
410                 return E_FAIL;
411         TRACE("size %lu -> %lu\n", cbInput, cbOutput);
412
413         if ( pcbInput != NULL ) *pcbInput = cbInput;
414         if ( pcbOutput != NULL ) *pcbOutput = cbOutput;
415
416         return S_OK;
417 }
418
419
420 static HRESULT ACMWrapper_GetAllocProp( CTransformBaseImpl* pImpl, const AM_MEDIA_TYPE* pmtIn, const AM_MEDIA_TYPE* pmtOut, ALLOCATOR_PROPERTIES* pProp, BOOL* pbTransInPlace, BOOL* pbTryToReuseSample )
421 {
422         CACMWrapperImpl*        This = pImpl->m_pUserData;
423         HRESULT hr;
424         DWORD   cbOutput;
425
426         TRACE("(%p)\n",This);
427
428         if ( This == NULL )
429                 return E_UNEXPECTED;
430
431         hr = ACMWrapper_GetConvBufSize(
432                 pImpl, This, NULL, &cbOutput, pmtOut, pmtIn );
433         if ( FAILED(hr) )
434                 return hr;
435
436         pProp->cBuffers = 1;
437         pProp->cbBuffer = cbOutput;
438
439         *pbTransInPlace = FALSE;
440         *pbTryToReuseSample = FALSE;
441
442         return S_OK;
443 }
444
445 static HRESULT ACMWrapper_BeginTransform( CTransformBaseImpl* pImpl, const AM_MEDIA_TYPE* pmtIn, const AM_MEDIA_TYPE* pmtOut, BOOL bReuseSample )
446 {
447         CACMWrapperImpl*        This = pImpl->m_pUserData;
448         HRESULT hr;
449         const WAVEFORMATEX*     pwfxIn;
450         const WAVEFORMATEX*     pwfxOut;
451         DWORD   cbInput;
452
453         FIXME("(%p,%p,%p,%d)\n",This,pmtIn,pmtOut,bReuseSample);
454
455         if ( This == NULL )
456                 return E_UNEXPECTED;
457
458         ACMWrapper_Close( This );
459         ACMWrapper_CleanupMTypes( This );
460         ACMWrapper_CleanupConvBuf( This );
461
462         hr = ACMWrapper_GetConvBufSize(
463                 pImpl, This, &cbInput, NULL, pmtOut, pmtIn );
464         if ( FAILED(hr) )
465                 return hr;
466         pwfxIn = (const WAVEFORMATEX*)pmtIn->pbFormat;
467         pwfxOut = (const WAVEFORMATEX*)pmtOut->pbFormat;
468
469         This->pConvBuf = (BYTE*)QUARTZ_AllocMem( cbInput );
470         if ( This->pConvBuf == NULL )
471                 return E_OUTOFMEMORY;
472         This->cbConvBlockSize = pwfxIn->nBlockAlign;
473         This->cbConvCached = 0;
474         This->cbConvAllocated = cbInput;
475
476         hr = ACMWrapper_StreamOpen(
477                 &This->has,
478                 (WAVEFORMATEX*)pmtOut, (WAVEFORMATEX*)pmtIn );
479         if ( FAILED(hr) )
480                 return E_FAIL;
481
482         return S_OK;
483 }
484
485 static HRESULT ACMWrapper_Convert(
486         CTransformBaseImpl* pImpl,
487         CACMWrapperImpl* This,
488         BYTE* pbSrc, DWORD cbSrcLen,
489         DWORD dwConvertFlags )
490 {
491         ACMSTREAMHEADER ash;
492         MMRESULT mr;
493         HRESULT hr = E_FAIL;
494         DWORD   dwConvCallFlags;
495         DWORD   cb;
496         IMediaSample*   pSampOut = NULL;
497         BYTE*   pOutBuf;
498         LONG    lOutBufLen;
499
500         TRACE("()\n");
501
502         if ( This->pConvBuf == NULL )
503                 return E_UNEXPECTED;
504
505         dwConvCallFlags = ACM_STREAMCONVERTF_BLOCKALIGN;
506         if ( dwConvertFlags & ACM_STREAMCONVERTF_START )
507         {
508                 dwConvCallFlags |= ACM_STREAMCONVERTF_START;
509                 This->cbConvCached = 0;
510         }
511
512         while ( 1 )
513         {
514                 cb = cbSrcLen + This->cbConvCached;
515                 if ( cb > This->cbConvAllocated )
516                         cb = This->cbConvAllocated;
517                 cb -= This->cbConvCached;
518                 if ( cb > 0 )
519                 {
520                         memcpy( This->pConvBuf+This->cbConvCached,
521                                 pbSrc, cb );
522                         pbSrc += cb;
523                         cbSrcLen -= cb;
524                         This->cbConvCached += cb;
525                 }
526
527                 cb = This->cbConvCached / This->cbConvBlockSize * This->cbConvBlockSize;
528                 if ( cb == 0 )
529                 {
530                         if ( dwConvertFlags & ACM_STREAMCONVERTF_END )
531                         {
532                                 dwConvCallFlags &= ~ACM_STREAMCONVERTF_BLOCKALIGN;
533                                 dwConvCallFlags |= ACM_STREAMCONVERTF_END;
534                                 cb = This->cbConvCached;
535                         }
536                         if ( cb == 0 )
537                         {
538                                 hr = S_OK;
539                                 break;
540                         }
541                 }
542
543                 ZeroMemory( &ash, sizeof(ash) );
544                 ash.cbStruct = sizeof(ash);
545                 ash.pbSrc = This->pConvBuf;
546                 ash.cbSrcLength = cb;
547
548                 hr = IMemAllocator_GetBuffer(
549                         pImpl->m_pOutPinAllocator,
550                         &pSampOut, NULL, NULL, 0 );
551                 if ( FAILED(hr) )
552                         break;
553                 hr = IMediaSample_SetSyncPoint( pSampOut, TRUE );
554                 if ( FAILED(hr) )
555                         break;
556                 if ( dwConvCallFlags & ACM_STREAMCONVERTF_START )
557                 {
558                         hr = IMediaSample_SetDiscontinuity( pSampOut, TRUE );
559                         if ( FAILED(hr) )
560                                 break;
561                 }
562                 hr = IMediaSample_GetPointer( pSampOut, &pOutBuf );
563                 if ( FAILED(hr) )
564                         break;
565                 lOutBufLen = IMediaSample_GetSize( pSampOut );
566                 if ( lOutBufLen <= 0 )
567                 {
568                         hr = E_FAIL;
569                         break;
570                 }
571                 ash.pbDst = pOutBuf;
572                 ash.cbDstLength = lOutBufLen;
573
574                 mr = acmStreamPrepareHeader(
575                         This->has, &ash, 0 );
576                 if ( mr == MMSYSERR_NOERROR )
577                         mr = acmStreamConvert(
578                                 This->has, &ash, dwConvCallFlags );
579                 if ( mr == MMSYSERR_NOERROR )
580                         mr = acmStreamUnprepareHeader(
581                                 This->has, &ash, 0 );
582                 if ( mr != MMSYSERR_NOERROR || ash.cbSrcLengthUsed == 0 )
583                 {
584                         hr = E_FAIL;
585                         break;
586                 }
587
588                 if ( ash.cbDstLengthUsed > 0 )
589                 {
590                         hr = IMediaSample_SetActualDataLength( pSampOut, ash.cbDstLengthUsed );
591                         if ( FAILED(hr) )
592                                 break;
593
594                         hr = CPinBaseImpl_SendSample(
595                                 &pImpl->pOutPin->pin,
596                                 pSampOut );
597                         if ( FAILED(hr) )
598                                 break;
599                 }
600
601                 if ( This->cbConvCached == ash.cbSrcLengthUsed )
602                 {
603                         This->cbConvCached = 0;
604                 }
605                 else
606                 {
607                         This->cbConvCached -= ash.cbSrcLengthUsed;
608                         memmove( This->pConvBuf,
609                                  This->pConvBuf + ash.cbSrcLengthUsed,
610                                  This->cbConvCached );
611                 }
612
613                 IMediaSample_Release( pSampOut ); pSampOut = NULL;
614                 dwConvCallFlags &= ~ACM_STREAMCONVERTF_START;
615         }
616
617         if ( pSampOut != NULL )
618                 IMediaSample_Release( pSampOut );
619
620         return hr;
621 }
622
623 static HRESULT ACMWrapper_ProcessReceive( CTransformBaseImpl* pImpl, IMediaSample* pSampIn )
624 {
625         CACMWrapperImpl*        This = pImpl->m_pUserData;
626         BYTE*   pDataIn = NULL;
627         LONG    lDataInLen;
628         HRESULT hr;
629         DWORD   dwConvFlags = 0;
630
631         FIXME("(%p)\n",This);
632
633         if ( This == NULL || This->has == (HACMSTREAM)NULL )
634                 return E_UNEXPECTED;
635
636         hr = IMediaSample_GetPointer( pSampIn, &pDataIn );
637         if ( FAILED(hr) )
638                 return hr;
639         lDataInLen = IMediaSample_GetActualDataLength( pSampIn );
640         if ( lDataInLen < 0 )
641                 return E_FAIL;
642         if ( IMediaSample_IsDiscontinuity( pSampIn ) != S_OK )
643                 dwConvFlags |= ACM_STREAMCONVERTF_START;
644
645         return ACMWrapper_Convert(
646                 pImpl, This, pDataIn, (DWORD)lDataInLen,
647                 dwConvFlags );
648 }
649
650 static HRESULT ACMWrapper_EndTransform( CTransformBaseImpl* pImpl )
651 {
652         CACMWrapperImpl*        This = pImpl->m_pUserData;
653         HRESULT hr;
654         DWORD   dwConvFlags = ACM_STREAMCONVERTF_END;
655
656         TRACE("(%p)\n",This);
657
658         if ( This == NULL )
659                 return E_UNEXPECTED;
660
661         hr = ACMWrapper_Convert(
662                 pImpl, This, NULL, 0,
663                 dwConvFlags );
664
665         ACMWrapper_Close( This );
666         ACMWrapper_CleanupMTypes( This );
667         ACMWrapper_CleanupConvBuf( This );
668
669         return hr;
670 }
671
672 static const TransformBaseHandlers transhandlers =
673 {
674         ACMWrapper_Init,
675         ACMWrapper_Cleanup,
676         ACMWrapper_CheckMediaType,
677         ACMWrapper_GetOutputTypes,
678         ACMWrapper_GetAllocProp,
679         ACMWrapper_BeginTransform,
680         ACMWrapper_ProcessReceive,
681         NULL,
682         ACMWrapper_EndTransform,
683 };
684
685 HRESULT QUARTZ_CreateACMWrapper(IUnknown* punkOuter,void** ppobj)
686 {
687         return QUARTZ_CreateTransformBase(
688                 punkOuter,ppobj,
689                 &CLSID_ACMWrapper,
690                 ACMWrapper_FilterName,
691                 NULL, NULL,
692                 &transhandlers );
693 }
694
695