2 * Implements ACM Wrapper(CLSID_ACMWrapper).
7 * Copyright (C) 2002 Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
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.
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.
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
39 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
42 #include "quartz_private.h"
47 static const WCHAR ACMWrapper_FilterName[] =
48 {'A','C','M',' ','W','r','a','p','p','e','r',0};
51 typedef struct CACMWrapperImpl
55 AM_MEDIA_TYPE* pmtOuts;
58 DWORD cbConvBlockSize;
60 DWORD cbConvAllocated;
65 void ACMWrapper_CleanupMTypes( CACMWrapperImpl* This )
69 if ( This->pmtOuts == NULL ) return;
70 for ( n = 0; n < This->cOuts; n++ )
72 QUARTZ_MediaType_Free( &This->pmtOuts[n] );
74 QUARTZ_FreeMem( This->pmtOuts );
80 void ACMWrapper_CleanupConvBuf( CACMWrapperImpl* This )
82 if ( This->pConvBuf != NULL )
84 QUARTZ_FreeMem( This->pConvBuf );
85 This->pConvBuf = NULL;
87 This->cbConvBlockSize = 0;
88 This->cbConvCached = 0;
89 This->cbConvAllocated = 0;
93 const WAVEFORMATEX* ACMWrapper_GetAudioFmt( const AM_MEDIA_TYPE* pmt )
95 const WAVEFORMATEX* pwfx;
97 if ( !IsEqualGUID( &pmt->majortype, &MEDIATYPE_Audio ) )
99 if ( !IsEqualGUID( &pmt->subtype, &MEDIASUBTYPE_NULL ) &&
100 !QUARTZ_MediaSubType_IsFourCC( &pmt->subtype ) )
102 if ( !IsEqualGUID( &pmt->formattype, &FORMAT_WaveFormatEx ) )
104 if ( pmt->pbFormat == NULL ||
105 pmt->cbFormat < (sizeof(WAVEFORMATEX)-sizeof(WORD)) )
108 pwfx = (const WAVEFORMATEX*)pmt->pbFormat;
110 if ( pwfx->wFormatTag != 1 && pmt->cbFormat < sizeof(WAVEFORMATEX) )
117 HRESULT ACMWrapper_SetupAudioFmt(
119 DWORD cbFormat, WORD wFormatTag, DWORD dwBlockAlign )
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) );
129 pmt->cbFormat = cbFormat;
130 pmt->pbFormat = (BYTE*)CoTaskMemAlloc( cbFormat );
131 if ( pmt->pbFormat == NULL )
132 return E_OUTOFMEMORY;
138 void ACMWrapper_FillFmtPCM(
139 WAVEFORMATEX* pwfxOut,
140 const WAVEFORMATEX* pwfxIn,
141 WORD wBitsPerSampOut )
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;
153 BOOL ACMWrapper_IsSupported(
154 WAVEFORMATEX* pwfxOut,
155 WAVEFORMATEX* pwfxIn )
160 NULL,(HACMDRIVER)NULL,
162 0,0,ACM_STREAMOPENF_QUERY);
163 if ( mr == ACMERR_NOTPOSSIBLE )
165 NULL,(HACMDRIVER)NULL,
167 0,0,ACM_STREAMOPENF_NONREALTIME|ACM_STREAMOPENF_QUERY);
168 return !!(mr == MMSYSERR_NOERROR);
172 HRESULT ACMWrapper_StreamOpen(
174 WAVEFORMATEX* pwfxOut,
175 WAVEFORMATEX* pwfxIn )
177 HACMSTREAM has = (HACMSTREAM)NULL;
181 &has,(HACMDRIVER)NULL,
184 if ( mr == ACMERR_NOTPOSSIBLE )
186 &has,(HACMDRIVER)NULL,
188 0,0,ACM_STREAMOPENF_NONREALTIME);
189 if ( mr != MMSYSERR_NOERROR )
191 if ( mr == MMSYSERR_NOMEM )
192 return E_OUTOFMEMORY;
200 /***************************************************************************
202 * CACMWrapperImpl methods
206 static void ACMWrapper_Close( CACMWrapperImpl* This )
208 if ( This->has != (HACMSTREAM)NULL )
210 acmStreamReset( This->has, 0 );
211 acmStreamClose( This->has, 0 );
212 This->has = (HACMSTREAM)NULL;
216 static HRESULT ACMWrapper_Init( CTransformBaseImpl* pImpl )
218 CACMWrapperImpl* This = pImpl->m_pUserData;
220 TRACE("(%p)\n",This);
225 This = (CACMWrapperImpl*)QUARTZ_AllocMem( sizeof(CACMWrapperImpl) );
227 return E_OUTOFMEMORY;
228 ZeroMemory( This, sizeof(CACMWrapperImpl) );
229 pImpl->m_pUserData = This;
232 This->has = (HACMSTREAM)NULL;
234 This->pmtOuts = NULL;
236 This->pConvBuf = NULL;
241 static HRESULT ACMWrapper_Cleanup( CTransformBaseImpl* pImpl )
243 CACMWrapperImpl* This = pImpl->m_pUserData;
245 TRACE("(%p)\n",This);
251 ACMWrapper_Close( This );
252 QUARTZ_FreeMem( This->pwfxIn );
253 ACMWrapper_CleanupMTypes( This );
254 ACMWrapper_CleanupConvBuf( This );
256 QUARTZ_FreeMem( This );
257 pImpl->m_pUserData = NULL;
262 static HRESULT ACMWrapper_CheckMediaType( CTransformBaseImpl* pImpl, const AM_MEDIA_TYPE* pmtIn, const AM_MEDIA_TYPE* pmtOut )
264 CACMWrapperImpl* This = pImpl->m_pUserData;
265 const WAVEFORMATEX* pwfxIn;
266 const WAVEFORMATEX* pwfxOut;
269 FIXME("(%p)\n",This);
273 pwfxIn = ACMWrapper_GetAudioFmt(pmtIn);
274 if ( pwfxIn == NULL ||
275 pwfxIn->wFormatTag == 0 ||
276 pwfxIn->wFormatTag == 1 )
278 TRACE("pwfxIn is not a compressed audio\n");
281 if ( pmtOut != NULL )
283 pwfxOut = ACMWrapper_GetAudioFmt(pmtOut);
284 if ( pwfxOut == NULL || pwfxOut->wFormatTag != 1 )
286 TRACE("pwfxOut is not a linear PCM\n");
289 if ( pwfxIn->nChannels != pwfxOut->nChannels ||
290 pwfxIn->nSamplesPerSec != pwfxOut->nSamplesPerSec )
292 TRACE("nChannels or nSamplesPerSec is not matched\n");
295 if ( !ACMWrapper_IsSupported((WAVEFORMATEX*)pwfxOut,(WAVEFORMATEX*)pwfxIn) )
297 TRACE("specified formats are not supported by ACM\n");
303 ACMWrapper_FillFmtPCM(&wfx,pwfxIn,8);
304 if ( ACMWrapper_IsSupported(&wfx,(WAVEFORMATEX*)pwfxIn) )
306 TRACE("compressed audio - can be decoded to 8bit\n");
309 ACMWrapper_FillFmtPCM(&wfx,pwfxIn,16);
310 if ( ACMWrapper_IsSupported(&wfx,(WAVEFORMATEX*)pwfxIn) )
312 TRACE("compressed audio - can be decoded to 16bit\n");
316 TRACE("unhandled audio %04x\n",(unsigned)pwfxIn->wFormatTag);
323 static HRESULT ACMWrapper_GetOutputTypes( CTransformBaseImpl* pImpl, const AM_MEDIA_TYPE* pmtIn, const AM_MEDIA_TYPE** ppmtAcceptTypes, ULONG* pcAcceptTypes )
325 CACMWrapperImpl* This = pImpl->m_pUserData;
327 const WAVEFORMATEX* pwfxIn;
328 AM_MEDIA_TYPE* pmtTry;
329 WAVEFORMATEX* pwfxTry;
331 FIXME("(%p)\n",This);
332 hr = ACMWrapper_CheckMediaType( pImpl, pmtIn, NULL );
335 pwfxIn = (const WAVEFORMATEX*)pmtIn->pbFormat;
337 ACMWrapper_CleanupMTypes( This );
338 This->pmtOuts = QUARTZ_AllocMem( sizeof(AM_MEDIA_TYPE) * 2 );
339 if ( This->pmtOuts == NULL )
340 return E_OUTOFMEMORY;
343 pmtTry = &This->pmtOuts[This->cOuts];
344 hr = ACMWrapper_SetupAudioFmt(
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 ) )
354 pmtTry = &This->pmtOuts[This->cOuts];
355 hr = ACMWrapper_SetupAudioFmt(
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 ) )
365 *ppmtAcceptTypes = This->pmtOuts;
366 *pcAcceptTypes = This->cOuts;
370 ACMWrapper_CleanupMTypes( This );
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 )
382 const WAVEFORMATEX* pwfxIn;
383 const WAVEFORMATEX* pwfxOut;
392 hr = ACMWrapper_CheckMediaType( pImpl, pmtIn, pmtOut );
395 pwfxIn = (const WAVEFORMATEX*)pmtIn->pbFormat;
396 pwfxOut = (const WAVEFORMATEX*)pmtOut->pbFormat;
398 hr = ACMWrapper_StreamOpen(
399 &has, (WAVEFORMATEX*)pwfxOut, (WAVEFORMATEX*)pwfxIn );
403 cbInput = (pwfxIn->nAvgBytesPerSec + pwfxIn->nBlockAlign - 1) / pwfxIn->nBlockAlign * pwfxIn->nBlockAlign;
406 mr = acmStreamSize( has, cbInput, &cbOutput, ACM_STREAMSIZEF_SOURCE );
407 acmStreamClose( has, 0 );
409 if ( mr != MMSYSERR_NOERROR || cbOutput == 0 )
411 TRACE("size %lu -> %lu\n", cbInput, cbOutput);
413 if ( pcbInput != NULL ) *pcbInput = cbInput;
414 if ( pcbOutput != NULL ) *pcbOutput = cbOutput;
420 static HRESULT ACMWrapper_GetAllocProp( CTransformBaseImpl* pImpl, const AM_MEDIA_TYPE* pmtIn, const AM_MEDIA_TYPE* pmtOut, ALLOCATOR_PROPERTIES* pProp, BOOL* pbTransInPlace, BOOL* pbTryToReuseSample )
422 CACMWrapperImpl* This = pImpl->m_pUserData;
426 TRACE("(%p)\n",This);
431 hr = ACMWrapper_GetConvBufSize(
432 pImpl, This, NULL, &cbOutput, pmtOut, pmtIn );
437 pProp->cbBuffer = cbOutput;
439 *pbTransInPlace = FALSE;
440 *pbTryToReuseSample = FALSE;
445 static HRESULT ACMWrapper_BeginTransform( CTransformBaseImpl* pImpl, const AM_MEDIA_TYPE* pmtIn, const AM_MEDIA_TYPE* pmtOut, BOOL bReuseSample )
447 CACMWrapperImpl* This = pImpl->m_pUserData;
449 const WAVEFORMATEX* pwfxIn;
450 const WAVEFORMATEX* pwfxOut;
453 FIXME("(%p,%p,%p,%d)\n",This,pmtIn,pmtOut,bReuseSample);
458 ACMWrapper_Close( This );
459 ACMWrapper_CleanupMTypes( This );
460 ACMWrapper_CleanupConvBuf( This );
462 hr = ACMWrapper_GetConvBufSize(
463 pImpl, This, &cbInput, NULL, pmtOut, pmtIn );
466 pwfxIn = (const WAVEFORMATEX*)pmtIn->pbFormat;
467 pwfxOut = (const WAVEFORMATEX*)pmtOut->pbFormat;
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;
476 hr = ACMWrapper_StreamOpen(
478 (WAVEFORMATEX*)pmtOut, (WAVEFORMATEX*)pmtIn );
485 static HRESULT ACMWrapper_Convert(
486 CTransformBaseImpl* pImpl,
487 CACMWrapperImpl* This,
488 BYTE* pbSrc, DWORD cbSrcLen,
489 DWORD dwConvertFlags )
494 DWORD dwConvCallFlags;
496 IMediaSample* pSampOut = NULL;
502 if ( This->pConvBuf == NULL )
505 dwConvCallFlags = ACM_STREAMCONVERTF_BLOCKALIGN;
506 if ( dwConvertFlags & ACM_STREAMCONVERTF_START )
508 dwConvCallFlags |= ACM_STREAMCONVERTF_START;
509 This->cbConvCached = 0;
514 cb = cbSrcLen + This->cbConvCached;
515 if ( cb > This->cbConvAllocated )
516 cb = This->cbConvAllocated;
517 cb -= This->cbConvCached;
520 memcpy( This->pConvBuf+This->cbConvCached,
524 This->cbConvCached += cb;
527 cb = This->cbConvCached / This->cbConvBlockSize * This->cbConvBlockSize;
530 if ( dwConvertFlags & ACM_STREAMCONVERTF_END )
532 dwConvCallFlags &= ~ACM_STREAMCONVERTF_BLOCKALIGN;
533 dwConvCallFlags |= ACM_STREAMCONVERTF_END;
534 cb = This->cbConvCached;
543 ZeroMemory( &ash, sizeof(ash) );
544 ash.cbStruct = sizeof(ash);
545 ash.pbSrc = This->pConvBuf;
546 ash.cbSrcLength = cb;
548 hr = IMemAllocator_GetBuffer(
549 pImpl->m_pOutPinAllocator,
550 &pSampOut, NULL, NULL, 0 );
553 hr = IMediaSample_SetSyncPoint( pSampOut, TRUE );
556 if ( dwConvCallFlags & ACM_STREAMCONVERTF_START )
558 hr = IMediaSample_SetDiscontinuity( pSampOut, TRUE );
562 hr = IMediaSample_GetPointer( pSampOut, &pOutBuf );
565 lOutBufLen = IMediaSample_GetSize( pSampOut );
566 if ( lOutBufLen <= 0 )
572 ash.cbDstLength = lOutBufLen;
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 )
588 if ( ash.cbDstLengthUsed > 0 )
590 hr = IMediaSample_SetActualDataLength( pSampOut, ash.cbDstLengthUsed );
594 hr = CPinBaseImpl_SendSample(
595 &pImpl->pOutPin->pin,
601 if ( This->cbConvCached == ash.cbSrcLengthUsed )
603 This->cbConvCached = 0;
607 This->cbConvCached -= ash.cbSrcLengthUsed;
608 memmove( This->pConvBuf,
609 This->pConvBuf + ash.cbSrcLengthUsed,
610 This->cbConvCached );
613 IMediaSample_Release( pSampOut ); pSampOut = NULL;
614 dwConvCallFlags &= ~ACM_STREAMCONVERTF_START;
617 if ( pSampOut != NULL )
618 IMediaSample_Release( pSampOut );
623 static HRESULT ACMWrapper_ProcessReceive( CTransformBaseImpl* pImpl, IMediaSample* pSampIn )
625 CACMWrapperImpl* This = pImpl->m_pUserData;
626 BYTE* pDataIn = NULL;
629 DWORD dwConvFlags = 0;
631 FIXME("(%p)\n",This);
633 if ( This == NULL || This->has == (HACMSTREAM)NULL )
636 hr = IMediaSample_GetPointer( pSampIn, &pDataIn );
639 lDataInLen = IMediaSample_GetActualDataLength( pSampIn );
640 if ( lDataInLen < 0 )
642 if ( IMediaSample_IsDiscontinuity( pSampIn ) != S_OK )
643 dwConvFlags |= ACM_STREAMCONVERTF_START;
645 return ACMWrapper_Convert(
646 pImpl, This, pDataIn, (DWORD)lDataInLen,
650 static HRESULT ACMWrapper_EndTransform( CTransformBaseImpl* pImpl )
652 CACMWrapperImpl* This = pImpl->m_pUserData;
654 DWORD dwConvFlags = ACM_STREAMCONVERTF_END;
656 TRACE("(%p)\n",This);
661 hr = ACMWrapper_Convert(
662 pImpl, This, NULL, 0,
665 ACMWrapper_Close( This );
666 ACMWrapper_CleanupMTypes( This );
667 ACMWrapper_CleanupConvBuf( This );
672 static const TransformBaseHandlers transhandlers =
676 ACMWrapper_CheckMediaType,
677 ACMWrapper_GetOutputTypes,
678 ACMWrapper_GetAllocProp,
679 ACMWrapper_BeginTransform,
680 ACMWrapper_ProcessReceive,
682 ACMWrapper_EndTransform,
685 HRESULT QUARTZ_CreateACMWrapper(IUnknown* punkOuter,void** ppobj)
687 return QUARTZ_CreateTransformBase(
690 ACMWrapper_FilterName,