1 /* 2 * AVI Decompressor (VFW decompressors wrapper) 3 * 4 * Copyright 2004-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 "amvideo.h" 28 #include "windef.h" 29 #include "winbase.h" 30 #include "dshow.h" 31 #include "strmif.h" 32 #include "vfwmsgs.h" 33 #include "vfw.h" 34 #include "dvdmedia.h" 35 36 #include <assert.h> 37 38 #include "wine/unicode.h" 39 #include "wine/debug.h" 40 41 WINE_DEFAULT_DEBUG_CHANNEL(quartz); 42 43 typedef struct AVIDecImpl 44 { 45 TransformFilter tf; 46 47 HIC hvid; 48 BITMAPINFOHEADER* pBihIn; 49 BITMAPINFOHEADER* pBihOut; 50 REFERENCE_TIME late; 51 } AVIDecImpl; 52 53 static const IBaseFilterVtbl AVIDec_Vtbl; 54 55 static inline AVIDecImpl *impl_from_TransformFilter( TransformFilter *iface ) 56 { 57 return CONTAINING_RECORD(iface, AVIDecImpl, tf); 58 } 59 60 static HRESULT WINAPI AVIDec_StartStreaming(TransformFilter* pTransformFilter) 61 { 62 AVIDecImpl* This = impl_from_TransformFilter(pTransformFilter); 63 DWORD result; 64 65 TRACE("(%p)->()\n", This); 66 This->late = -1; 67 68 result = ICDecompressBegin(This->hvid, This->pBihIn, This->pBihOut); 69 if (result != ICERR_OK) 70 { 71 ERR("Cannot start processing (%d)\n", result); 72 return E_FAIL; 73 } 74 return S_OK; 75 } 76 77 static HRESULT WINAPI AVIDec_EndFlush(TransformFilter *pTransformFilter) { 78 AVIDecImpl* This = impl_from_TransformFilter(pTransformFilter); 79 This->late = -1; 80 return S_OK; 81 } 82 83 static HRESULT WINAPI AVIDec_NotifyDrop(TransformFilter *pTransformFilter, IBaseFilter *sender, Quality qm) { 84 AVIDecImpl *This = impl_from_TransformFilter(pTransformFilter); 85 86 EnterCriticalSection(&This->tf.filter.csFilter); 87 if (qm.Late > 0) 88 This->late = qm.Late + qm.TimeStamp; 89 else 90 This->late = -1; 91 LeaveCriticalSection(&This->tf.filter.csFilter); 92 return S_OK; 93 } 94 95 static int AVIDec_DropSample(AVIDecImpl *This, REFERENCE_TIME tStart) { 96 if (This->late < 0) 97 return 0; 98 99 if (tStart < This->late) { 100 TRACE("Dropping sample\n"); 101 return 1; 102 } 103 This->late = -1; 104 return 0; 105 } 106 107 static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample) 108 { 109 AVIDecImpl* This = impl_from_TransformFilter(tf); 110 AM_MEDIA_TYPE amt; 111 HRESULT hr; 112 DWORD res; 113 IMediaSample* pOutSample = NULL; 114 DWORD cbDstStream; 115 LPBYTE pbDstStream; 116 DWORD cbSrcStream; 117 LPBYTE pbSrcStream; 118 LONGLONG tStart, tStop; 119 DWORD flags = 0; 120 121 EnterCriticalSection(&This->tf.csReceive); 122 hr = IMediaSample_GetPointer(pSample, &pbSrcStream); 123 if (FAILED(hr)) 124 { 125 ERR("Cannot get pointer to sample data (%x)\n", hr); 126 goto error; 127 } 128 129 cbSrcStream = IMediaSample_GetActualDataLength(pSample); 130 131 TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream); 132 133 hr = IPin_ConnectionMediaType(This->tf.ppPins[0], &amt); 134 if (FAILED(hr)) { 135 ERR("Unable to retrieve media type\n"); 136 goto error; 137 } 138 139 /* Update input size to match sample size */ 140 This->pBihIn->biSizeImage = cbSrcStream; 141 142 hr = BaseOutputPinImpl_GetDeliveryBuffer((BaseOutputPin*)This->tf.ppPins[1], &pOutSample, NULL, NULL, 0); 143 if (FAILED(hr)) { 144 ERR("Unable to get delivery buffer (%x)\n", hr); 145 goto error; 146 } 147 148 hr = IMediaSample_SetActualDataLength(pOutSample, 0); 149 assert(hr == S_OK); 150 151 hr = IMediaSample_GetPointer(pOutSample, &pbDstStream); 152 if (FAILED(hr)) { 153 ERR("Unable to get pointer to buffer (%x)\n", hr); 154 goto error; 155 } 156 cbDstStream = IMediaSample_GetSize(pOutSample); 157 if (cbDstStream < This->pBihOut->biSizeImage) { 158 ERR("Sample size is too small %d < %d\n", cbDstStream, This->pBihOut->biSizeImage); 159 hr = E_FAIL; 160 goto error; 161 } 162 163 if (IMediaSample_IsPreroll(pSample) == S_OK) 164 flags |= ICDECOMPRESS_PREROLL; 165 if (IMediaSample_IsSyncPoint(pSample) != S_OK) 166 flags |= ICDECOMPRESS_NOTKEYFRAME; 167 hr = IMediaSample_GetTime(pSample, &tStart, &tStop); 168 if (hr == S_OK && AVIDec_DropSample(This, tStart)) 169 flags |= ICDECOMPRESS_HURRYUP; 170 171 res = ICDecompress(This->hvid, flags, This->pBihIn, pbSrcStream, This->pBihOut, pbDstStream); 172 if (res != ICERR_OK) 173 ERR("Error occurred during the decompression (%x)\n", res); 174 175 /* Drop sample if it's intended to be dropped */ 176 if (flags & ICDECOMPRESS_HURRYUP) { 177 hr = S_OK; 178 goto error; 179 } 180 181 IMediaSample_SetActualDataLength(pOutSample, This->pBihOut->biSizeImage); 182 183 IMediaSample_SetPreroll(pOutSample, (IMediaSample_IsPreroll(pSample) == S_OK)); 184 IMediaSample_SetDiscontinuity(pOutSample, (IMediaSample_IsDiscontinuity(pSample) == S_OK)); 185 IMediaSample_SetSyncPoint(pOutSample, (IMediaSample_IsSyncPoint(pSample) == S_OK)); 186 187 if (hr == S_OK) 188 IMediaSample_SetTime(pOutSample, &tStart, &tStop); 189 else if (hr == VFW_S_NO_STOP_TIME) 190 IMediaSample_SetTime(pOutSample, &tStart, NULL); 191 else 192 IMediaSample_SetTime(pOutSample, NULL, NULL); 193 194 if (IMediaSample_GetMediaTime(pSample, &tStart, &tStop) == S_OK) 195 IMediaSample_SetMediaTime(pOutSample, &tStart, &tStop); 196 else 197 IMediaSample_SetMediaTime(pOutSample, NULL, NULL); 198 199 LeaveCriticalSection(&This->tf.csReceive); 200 hr = BaseOutputPinImpl_Deliver((BaseOutputPin*)This->tf.ppPins[1], pOutSample); 201 EnterCriticalSection(&This->tf.csReceive); 202 if (hr != S_OK && hr != VFW_E_NOT_CONNECTED) 203 ERR("Error sending sample (%x)\n", hr); 204 205 error: 206 if (pOutSample) 207 IMediaSample_Release(pOutSample); 208 209 LeaveCriticalSection(&This->tf.csReceive); 210 return hr; 211 } 212 213 static HRESULT WINAPI AVIDec_StopStreaming(TransformFilter* pTransformFilter) 214 { 215 AVIDecImpl* This = impl_from_TransformFilter(pTransformFilter); 216 DWORD result; 217 218 TRACE("(%p)->()\n", This); 219 220 if (!This->hvid) 221 return S_OK; 222 223 result = ICDecompressEnd(This->hvid); 224 if (result != ICERR_OK) 225 { 226 ERR("Cannot stop processing (%d)\n", result); 227 return E_FAIL; 228 } 229 return S_OK; 230 } 231 232 static HRESULT WINAPI AVIDec_SetMediaType(TransformFilter *tf, PIN_DIRECTION dir, const AM_MEDIA_TYPE * pmt) 233 { 234 AVIDecImpl* This = impl_from_TransformFilter(tf); 235 HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED; 236 237 TRACE("(%p)->(%p)\n", This, pmt); 238 239 if (dir != PINDIR_INPUT) 240 return S_OK; 241 242 /* Check root (GUID w/o FOURCC) */ 243 if ((IsEqualIID(&pmt->majortype, &MEDIATYPE_Video)) && 244 (!memcmp(((const char *)&pmt->subtype)+4, ((const char *)&MEDIATYPE_Video)+4, sizeof(GUID)-4))) 245 { 246 VIDEOINFOHEADER *format1 = (VIDEOINFOHEADER *)pmt->pbFormat; 247 VIDEOINFOHEADER2 *format2 = (VIDEOINFOHEADER2 *)pmt->pbFormat; 248 BITMAPINFOHEADER *bmi; 249 250 if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)) 251 bmi = &format1->bmiHeader; 252 else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2)) 253 bmi = &format2->bmiHeader; 254 else 255 goto failed; 256 TRACE("Fourcc: %s\n", debugstr_an((const char *)&pmt->subtype.Data1, 4)); 257 258 This->hvid = ICLocate(pmt->majortype.Data1, pmt->subtype.Data1, bmi, NULL, ICMODE_DECOMPRESS); 259 if (This->hvid) 260 { 261 AM_MEDIA_TYPE* outpmt = &This->tf.pmt; 262 const CLSID* outsubtype; 263 DWORD bih_size; 264 DWORD output_depth = bmi->biBitCount; 265 DWORD result; 266 FreeMediaType(outpmt); 267 268 switch(bmi->biBitCount) 269 { 270 case 32: outsubtype = &MEDIASUBTYPE_RGB32; break; 271 case 24: outsubtype = &MEDIASUBTYPE_RGB24; break; 272 case 16: outsubtype = &MEDIASUBTYPE_RGB565; break; 273 case 8: outsubtype = &MEDIASUBTYPE_RGB8; break; 274 default: 275 WARN("Non standard input depth %d, forced output depth to 32\n", bmi->biBitCount); 276 outsubtype = &MEDIASUBTYPE_RGB32; 277 output_depth = 32; 278 break; 279 } 280 281 /* Copy bitmap header from media type to 1 for input and 1 for output */ 282 bih_size = bmi->biSize + bmi->biClrUsed * 4; 283 This->pBihIn = CoTaskMemAlloc(bih_size); 284 if (!This->pBihIn) 285 { 286 hr = E_OUTOFMEMORY; 287 goto failed; 288 } 289 This->pBihOut = CoTaskMemAlloc(bih_size); 290 if (!This->pBihOut) 291 { 292 hr = E_OUTOFMEMORY; 293 goto failed; 294 } 295 memcpy(This->pBihIn, bmi, bih_size); 296 memcpy(This->pBihOut, bmi, bih_size); 297 298 /* Update output format as non compressed bitmap */ 299 This->pBihOut->biCompression = 0; 300 This->pBihOut->biBitCount = output_depth; 301 This->pBihOut->biSizeImage = This->pBihOut->biWidth * This->pBihOut->biHeight * This->pBihOut->biBitCount / 8; 302 TRACE("Size: %u\n", This->pBihIn->biSize); 303 result = ICDecompressQuery(This->hvid, This->pBihIn, This->pBihOut); 304 if (result != ICERR_OK) 305 { 306 ERR("Unable to found a suitable output format (%d)\n", result); 307 goto failed; 308 } 309 310 /* Update output media type */ 311 CopyMediaType(outpmt, pmt); 312 outpmt->subtype = *outsubtype; 313 314 if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)) 315 memcpy(&(((VIDEOINFOHEADER *)outpmt->pbFormat)->bmiHeader), This->pBihOut, This->pBihOut->biSize); 316 else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2)) 317 memcpy(&(((VIDEOINFOHEADER2 *)outpmt->pbFormat)->bmiHeader), This->pBihOut, This->pBihOut->biSize); 318 else 319 assert(0); 320 321 TRACE("Connection accepted\n"); 322 return S_OK; 323 } 324 TRACE("Unable to find a suitable VFW decompressor\n"); 325 } 326 327 failed: 328 329 TRACE("Connection refused\n"); 330 return hr; 331 } 332 333 static HRESULT WINAPI AVIDec_CompleteConnect(TransformFilter *tf, PIN_DIRECTION dir, IPin *pin) 334 { 335 AVIDecImpl* This = impl_from_TransformFilter(tf); 336 337 TRACE("(%p)\n", This); 338 339 return S_OK; 340 } 341 342 static HRESULT WINAPI AVIDec_BreakConnect(TransformFilter *tf, PIN_DIRECTION dir) 343 { 344 AVIDecImpl *This = impl_from_TransformFilter(tf); 345 346 TRACE("(%p)->()\n", This); 347 348 if (dir == PINDIR_INPUT) 349 { 350 if (This->hvid) 351 ICClose(This->hvid); 352 CoTaskMemFree(This->pBihIn); 353 CoTaskMemFree(This->pBihOut); 354 This->hvid = NULL; 355 This->pBihIn = NULL; 356 This->pBihOut = NULL; 357 } 358 359 return S_OK; 360 } 361 362 static HRESULT WINAPI AVIDec_DecideBufferSize(TransformFilter *tf, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest) 363 { 364 AVIDecImpl *pAVI = impl_from_TransformFilter(tf); 365 ALLOCATOR_PROPERTIES actual; 366 367 if (!ppropInputRequest->cbAlign) 368 ppropInputRequest->cbAlign = 1; 369 370 if (ppropInputRequest->cbBuffer < pAVI->pBihOut->biSizeImage) 371 ppropInputRequest->cbBuffer = pAVI->pBihOut->biSizeImage; 372 373 if (!ppropInputRequest->cBuffers) 374 ppropInputRequest->cBuffers = 1; 375 376 return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual); 377 } 378 379 static const TransformFilterFuncTable AVIDec_FuncsTable = { 380 AVIDec_DecideBufferSize, 381 AVIDec_StartStreaming, 382 AVIDec_Receive, 383 AVIDec_StopStreaming, 384 NULL, 385 AVIDec_SetMediaType, 386 AVIDec_CompleteConnect, 387 AVIDec_BreakConnect, 388 NULL, 389 NULL, 390 AVIDec_EndFlush, 391 NULL, 392 AVIDec_NotifyDrop 393 }; 394 395 HRESULT AVIDec_create(IUnknown * pUnkOuter, LPVOID * ppv) 396 { 397 HRESULT hr; 398 AVIDecImpl * This; 399 400 TRACE("(%p, %p)\n", pUnkOuter, ppv); 401 402 *ppv = NULL; 403 404 if (pUnkOuter) 405 return CLASS_E_NOAGGREGATION; 406 407 hr = TransformFilter_Construct(&AVIDec_Vtbl, sizeof(AVIDecImpl), &CLSID_AVIDec, &AVIDec_FuncsTable, (IBaseFilter**)&This); 408 409 if (FAILED(hr)) 410 return hr; 411 412 This->hvid = NULL; 413 This->pBihIn = NULL; 414 This->pBihOut = NULL; 415 416 *ppv = &This->tf.filter.IBaseFilter_iface; 417 418 return hr; 419 } 420 421 static const IBaseFilterVtbl AVIDec_Vtbl = 422 { 423 TransformFilterImpl_QueryInterface, 424 BaseFilterImpl_AddRef, 425 TransformFilterImpl_Release, 426 BaseFilterImpl_GetClassID, 427 TransformFilterImpl_Stop, 428 TransformFilterImpl_Pause, 429 TransformFilterImpl_Run, 430 BaseFilterImpl_GetState, 431 BaseFilterImpl_SetSyncSource, 432 BaseFilterImpl_GetSyncSource, 433 BaseFilterImpl_EnumPins, 434 TransformFilterImpl_FindPin, 435 BaseFilterImpl_QueryFilterInfo, 436 BaseFilterImpl_JoinFilterGraph, 437 BaseFilterImpl_QueryVendorInfo 438 }; 439