Chris Robinson | b3fab14 | 2007-04-24 05:28:14 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * MPEG Splitter Filter |
| 3 | * |
| 4 | * Copyright 2003 Robert Shearman |
| 5 | * Copyright 2004-2005 Christian Costa |
| 6 | * Copyright 2007 Chris Robinson |
| 7 | * |
| 8 | * This library is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU Lesser General Public |
| 10 | * License as published by the Free Software Foundation; either |
| 11 | * version 2.1 of the License, or (at your option) any later version. |
| 12 | * |
| 13 | * This library is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 16 | * Lesser General Public License for more details. |
| 17 | * |
| 18 | * You should have received a copy of the GNU Lesser General Public |
| 19 | * License along with this library; if not, write to the Free Software |
| 20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| 21 | */ |
| 22 | |
| 23 | #include <assert.h> |
| 24 | #include <math.h> |
| 25 | |
| 26 | #include "quartz_private.h" |
| 27 | #include "control_private.h" |
| 28 | #include "pin.h" |
| 29 | |
| 30 | #include "uuids.h" |
| 31 | #include "mmreg.h" |
| 32 | #include "vfwmsgs.h" |
| 33 | #include "mmsystem.h" |
| 34 | |
| 35 | #include "winternl.h" |
| 36 | |
| 37 | #include "wine/unicode.h" |
| 38 | #include "wine/debug.h" |
| 39 | |
| 40 | #include "parser.h" |
| 41 | |
| 42 | WINE_DEFAULT_DEBUG_CHANNEL(quartz); |
| 43 | |
| 44 | #define SEQUENCE_HEADER_CODE 0xB3 |
| 45 | #define PACK_START_CODE 0xBA |
| 46 | |
| 47 | #define SYSTEM_START_CODE 0xBB |
| 48 | #define AUDIO_ELEMENTARY_STREAM 0xC0 |
| 49 | #define VIDEO_ELEMENTARY_STREAM 0xE0 |
| 50 | |
| 51 | #define MPEG_SYSTEM_HEADER 3 |
| 52 | #define MPEG_VIDEO_HEADER 2 |
| 53 | #define MPEG_AUDIO_HEADER 1 |
| 54 | #define MPEG_NO_HEADER 0 |
| 55 | |
| 56 | |
| 57 | typedef struct MPEGSplitterImpl |
| 58 | { |
| 59 | ParserImpl Parser; |
| 60 | IMediaSample *pCurrentSample; |
| 61 | LONGLONG EndOfFile; |
| 62 | } MPEGSplitterImpl; |
| 63 | |
| 64 | static int MPEGSplitter_head_check(const BYTE *header) |
| 65 | { |
| 66 | /* If this is a possible start code, check for a system or video header */ |
| 67 | if (header[0] == 0 && header[1] == 0 && header[2] == 1) |
| 68 | { |
| 69 | /* Check if we got a system or elementary stream start code */ |
| 70 | if (header[3] == PACK_START_CODE || |
| 71 | header[3] == VIDEO_ELEMENTARY_STREAM || |
| 72 | header[3] == AUDIO_ELEMENTARY_STREAM) |
| 73 | return MPEG_SYSTEM_HEADER; |
| 74 | |
| 75 | /* Check for a MPEG video sequence start code */ |
| 76 | if (header[3] == SEQUENCE_HEADER_CODE) |
| 77 | return MPEG_VIDEO_HEADER; |
| 78 | } |
| 79 | |
| 80 | /* This should give a good guess if we have an MPEG audio header */ |
| 81 | if(header[0] == 0xff && ((header[1]>>5)&0x7) == 0x7 && |
| 82 | ((header[1]>>1)&0x3) != 0 && ((header[2]>>4)&0xf) != 0xf && |
| 83 | ((header[2]>>2)&0x3) != 0x3) |
| 84 | return MPEG_AUDIO_HEADER; |
| 85 | |
| 86 | /* Nothing yet.. */ |
| 87 | return MPEG_NO_HEADER; |
| 88 | } |
| 89 | |
| 90 | |
| 91 | static HRESULT MPEGSplitter_process_sample(LPVOID iface, IMediaSample * pSample) |
| 92 | { |
| 93 | MPEGSplitterImpl *This = (MPEGSplitterImpl*)iface; |
| 94 | LPBYTE pbSrcStream = NULL; |
| 95 | DWORD cbSrcStream = 0; |
| 96 | DWORD used_bytes = 0; |
| 97 | REFERENCE_TIME tStart, tStop; |
| 98 | HRESULT hr; |
| 99 | BYTE *pbDstStream; |
| 100 | DWORD cbDstStream; |
| 101 | long remaining_bytes = 0; |
| 102 | Parser_OutputPin * pOutputPin; |
| 103 | DWORD bytes_written = 0; |
| 104 | |
| 105 | pOutputPin = (Parser_OutputPin*)This->Parser.ppPins[1]; |
| 106 | |
| 107 | hr = IMediaSample_GetTime(pSample, &tStart, &tStop); |
| 108 | if (SUCCEEDED(hr)) |
| 109 | { |
| 110 | cbSrcStream = IMediaSample_GetActualDataLength(pSample); |
| 111 | hr = IMediaSample_GetPointer(pSample, &pbSrcStream); |
| 112 | } |
| 113 | |
| 114 | /* trace removed for performance reasons */ |
| 115 | /* TRACE("(%p), %llu -> %llu\n", pSample, tStart, tStop); */ |
| 116 | |
| 117 | if (This->pCurrentSample) |
| 118 | bytes_written = IMediaSample_GetActualDataLength(This->pCurrentSample); |
| 119 | |
| 120 | while (hr == S_OK && used_bytes < cbSrcStream) |
| 121 | { |
| 122 | remaining_bytes = (long)(This->EndOfFile - BYTES_FROM_MEDIATIME(tStart) - used_bytes); |
| 123 | if (remaining_bytes <= 0) |
| 124 | break; |
| 125 | |
| 126 | if (!This->pCurrentSample) |
| 127 | { |
| 128 | /* cache media sample until it is ready to be despatched |
| 129 | * (i.e. we reach the end of the chunk) */ |
| 130 | hr = OutputPin_GetDeliveryBuffer(&pOutputPin->pin, &This->pCurrentSample, NULL, NULL, 0); |
| 131 | if (FAILED(hr)) |
| 132 | { |
| 133 | TRACE("Skipping sending sample due to error (%x)\n", hr); |
| 134 | This->pCurrentSample = NULL; |
| 135 | break; |
| 136 | } |
| 137 | |
| 138 | IMediaSample_SetTime(This->pCurrentSample, NULL, NULL); |
| 139 | hr = IMediaSample_SetActualDataLength(This->pCurrentSample, 0); |
| 140 | assert(hr == S_OK); |
| 141 | bytes_written = 0; |
| 142 | } |
| 143 | |
| 144 | hr = IMediaSample_GetPointer(This->pCurrentSample, &pbDstStream); |
| 145 | if (SUCCEEDED(hr)) |
| 146 | { |
| 147 | cbDstStream = IMediaSample_GetSize(This->pCurrentSample); |
| 148 | remaining_bytes = min(remaining_bytes, (long)(cbDstStream - bytes_written)); |
| 149 | |
| 150 | assert(remaining_bytes >= 0); |
| 151 | |
| 152 | /* trace removed for performance reasons */ |
| 153 | /* TRACE("remaining_bytes: %d, cbSrcStream: 0x%x\n", remaining_bytes, cbSrcStream); */ |
| 154 | } |
| 155 | |
| 156 | if (remaining_bytes <= (long)(cbSrcStream-used_bytes)) |
| 157 | { |
| 158 | if (SUCCEEDED(hr)) |
| 159 | { |
| 160 | memcpy(pbDstStream + bytes_written, pbSrcStream + used_bytes, remaining_bytes); |
| 161 | bytes_written += remaining_bytes; |
| 162 | |
| 163 | hr = IMediaSample_SetActualDataLength(This->pCurrentSample, bytes_written); |
| 164 | assert(hr == S_OK); |
| 165 | |
| 166 | used_bytes += remaining_bytes; |
| 167 | } |
| 168 | |
| 169 | if (SUCCEEDED(hr)) |
| 170 | { |
| 171 | REFERENCE_TIME tMPEGStart, tMPEGStop; |
| 172 | |
| 173 | pOutputPin->dwSamplesProcessed = (BYTES_FROM_MEDIATIME(tStart)+used_bytes) / pOutputPin->dwSampleSize; |
| 174 | |
| 175 | tMPEGStart = (tStart + MEDIATIME_FROM_BYTES(used_bytes-bytes_written)) / |
| 176 | (pOutputPin->fSamplesPerSec*pOutputPin->dwSampleSize); |
| 177 | tMPEGStop = (tStart + MEDIATIME_FROM_BYTES(used_bytes)) / |
| 178 | (pOutputPin->fSamplesPerSec*pOutputPin->dwSampleSize); |
| 179 | |
| 180 | /* If the start of the sample has a valid MPEG header, it's a |
| 181 | * sync point */ |
| 182 | if (MPEGSplitter_head_check(pbDstStream) == MPEG_AUDIO_HEADER) |
| 183 | IMediaSample_SetSyncPoint(This->pCurrentSample, TRUE); |
| 184 | else |
| 185 | IMediaSample_SetSyncPoint(This->pCurrentSample, FALSE); |
| 186 | IMediaSample_SetTime(This->pCurrentSample, &tMPEGStart, &tMPEGStop); |
| 187 | |
| 188 | hr = OutputPin_SendSample(&pOutputPin->pin, This->pCurrentSample); |
| 189 | if (FAILED(hr)) |
| 190 | WARN("Error sending sample (%x)\n", hr); |
| 191 | } |
| 192 | |
| 193 | if (This->pCurrentSample) |
| 194 | IMediaSample_Release(This->pCurrentSample); |
| 195 | This->pCurrentSample = NULL; |
| 196 | } |
| 197 | else |
| 198 | { |
| 199 | if (SUCCEEDED(hr)) |
| 200 | { |
| 201 | memcpy(pbDstStream + bytes_written, pbSrcStream + used_bytes, cbSrcStream - used_bytes); |
| 202 | bytes_written += cbSrcStream - used_bytes; |
| 203 | IMediaSample_SetActualDataLength(This->pCurrentSample, bytes_written); |
| 204 | |
| 205 | used_bytes += cbSrcStream - used_bytes; |
| 206 | } |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | if (BYTES_FROM_MEDIATIME(tStop) >= This->EndOfFile) |
| 211 | { |
| 212 | int i; |
| 213 | |
| 214 | TRACE("End of file reached (%d out of %d bytes used)\n", used_bytes, cbSrcStream); |
| 215 | |
| 216 | if (This->pCurrentSample) |
| 217 | { |
| 218 | /* Make sure the last bit of data, if any, is sent */ |
| 219 | if (IMediaSample_GetActualDataLength(This->pCurrentSample) > 0) |
| 220 | { |
| 221 | REFERENCE_TIME tMPEGStart, tMPEGStop; |
| 222 | |
| 223 | pOutputPin->dwSamplesProcessed = (BYTES_FROM_MEDIATIME(tStart)+used_bytes) / pOutputPin->dwSampleSize; |
| 224 | |
| 225 | tMPEGStart = (tStart + MEDIATIME_FROM_BYTES(used_bytes-bytes_written)) / |
| 226 | (pOutputPin->fSamplesPerSec*pOutputPin->dwSampleSize); |
| 227 | tMPEGStop = (tStart + MEDIATIME_FROM_BYTES(used_bytes)) / |
| 228 | (pOutputPin->fSamplesPerSec*pOutputPin->dwSampleSize); |
| 229 | |
| 230 | if (MPEGSplitter_head_check(pbDstStream) == MPEG_AUDIO_HEADER) |
| 231 | IMediaSample_SetSyncPoint(This->pCurrentSample, TRUE); |
| 232 | else |
| 233 | IMediaSample_SetSyncPoint(This->pCurrentSample, FALSE); |
| 234 | IMediaSample_SetTime(This->pCurrentSample, &tMPEGStart, &tMPEGStop); |
| 235 | |
| 236 | hr = OutputPin_SendSample(&pOutputPin->pin, This->pCurrentSample); |
| 237 | if (FAILED(hr)) |
| 238 | WARN("Error sending sample (%x)\n", hr); |
| 239 | } |
| 240 | IMediaSample_Release(This->pCurrentSample); |
| 241 | } |
| 242 | This->pCurrentSample = NULL; |
| 243 | |
| 244 | for (i = 0; i < This->Parser.cStreams; i++) |
| 245 | { |
| 246 | IPin* ppin; |
| 247 | HRESULT hr; |
| 248 | |
| 249 | TRACE("Send End Of Stream to output pin %d\n", i); |
| 250 | |
| 251 | hr = IPin_ConnectedTo(This->Parser.ppPins[i+1], &ppin); |
| 252 | if (SUCCEEDED(hr)) |
| 253 | { |
| 254 | hr = IPin_EndOfStream(ppin); |
| 255 | IPin_Release(ppin); |
| 256 | } |
| 257 | if (FAILED(hr)) |
| 258 | WARN("Error sending EndOfStream to pin %d (%x)\n", i, hr); |
| 259 | } |
| 260 | |
| 261 | /* Force the pullpin thread to stop */ |
| 262 | hr = S_FALSE; |
| 263 | } |
| 264 | |
| 265 | return hr; |
| 266 | } |
| 267 | |
| 268 | |
| 269 | static HRESULT MPEGSplitter_query_accept(LPVOID iface, const AM_MEDIA_TYPE *pmt) |
| 270 | { |
| 271 | if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream)) |
| 272 | return S_FALSE; |
| 273 | |
| 274 | if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1Audio)) |
| 275 | return S_OK; |
| 276 | |
| 277 | if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1Video)) |
| 278 | FIXME("MPEG-1 video streams not yet supported.\n"); |
| 279 | else if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1System)) |
| 280 | FIXME("MPEG-1 system streams not yet supported.\n"); |
| 281 | else if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1VideoCD)) |
| 282 | FIXME("MPEG-1 VideoCD streams not yet supported.\n"); |
| 283 | |
| 284 | return S_FALSE; |
| 285 | } |
| 286 | |
| 287 | |
| 288 | static const WCHAR wszAudioStream[] = {'A','u','d','i','o',0}; |
| 289 | static const WCHAR wszVideoStream[] = {'V','i','d','e','o',0}; |
| 290 | |
| 291 | static HRESULT MPEGSplitter_init_audio(MPEGSplitterImpl *This, const BYTE *header, PIN_INFO *ppiOutput, AM_MEDIA_TYPE *pamt) |
| 292 | { |
| 293 | static const DWORD freqs[10] = { 44100, 48000, 32000, 22050, 24000, |
| 294 | 16000, 11025, 12000, 8000, 0 }; |
| 295 | static const DWORD tabsel_123[2][3][16] = { |
| 296 | { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,}, |
| 297 | {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,}, |
| 298 | {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} }, |
| 299 | |
| 300 | { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,}, |
| 301 | {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,}, |
| 302 | {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,} } |
| 303 | }; |
| 304 | |
| 305 | WAVEFORMATEX *format; |
| 306 | int bitrate_index; |
| 307 | int freq_index; |
| 308 | int mode_ext; |
| 309 | int emphasis; |
| 310 | int padding; |
| 311 | int lsf = 1; |
| 312 | int mpeg1; |
| 313 | int layer; |
| 314 | int mode; |
| 315 | |
| 316 | ZeroMemory(pamt, sizeof(*pamt)); |
| 317 | ppiOutput->dir = PINDIR_OUTPUT; |
| 318 | ppiOutput->pFilter = (IBaseFilter*)This; |
| 319 | wsprintfW(ppiOutput->achName, wszAudioStream); |
| 320 | |
| 321 | memcpy(&pamt->formattype, &FORMAT_WaveFormatEx, sizeof(GUID)); |
| 322 | memcpy(&pamt->majortype, &MEDIATYPE_Audio, sizeof(GUID)); |
| 323 | memcpy(&pamt->subtype, &MEDIASUBTYPE_MPEG1AudioPayload, sizeof(GUID)); |
| 324 | |
| 325 | pamt->lSampleSize = 0; |
| 326 | pamt->bFixedSizeSamples = TRUE; |
| 327 | pamt->bTemporalCompression = 0; |
| 328 | |
| 329 | mpeg1 = (header[1]>>4)&0x1; |
| 330 | if (mpeg1) |
| 331 | lsf = ((header[1]>>3)&0x1)^1; |
| 332 | |
| 333 | layer = 4-((header[1]>>1)&0x3); |
| 334 | bitrate_index = ((header[2]>>4)&0xf); |
| 335 | freq_index = ((header[2]>>2)&0x3) + (mpeg1?(lsf*3):6); |
| 336 | padding = ((header[2]>>1)&0x1); |
| 337 | mode = ((header[3]>>6)&0x3); |
| 338 | mode_ext = ((header[3]>>4)&0x3); |
| 339 | emphasis = ((header[3]>>0)&0x3); |
| 340 | |
| 341 | |
| 342 | pamt->cbFormat = ((layer==3)? sizeof(MPEGLAYER3WAVEFORMAT) : |
| 343 | sizeof(MPEG1WAVEFORMAT)); |
| 344 | pamt->pbFormat = CoTaskMemAlloc(pamt->cbFormat); |
| 345 | if (!pamt->pbFormat) |
| 346 | return E_OUTOFMEMORY; |
| 347 | ZeroMemory(pamt->pbFormat, pamt->cbFormat); |
| 348 | format = (WAVEFORMATEX*)pamt->pbFormat; |
| 349 | |
| 350 | format->wFormatTag = ((layer == 3) ? WAVE_FORMAT_MPEGLAYER3 : |
| 351 | WAVE_FORMAT_MPEG); |
| 352 | format->nChannels = ((mode == 3) ? 1 : 2); |
| 353 | format->nSamplesPerSec = freqs[freq_index]; |
| 354 | format->nAvgBytesPerSec = tabsel_123[lsf][layer-1][bitrate_index] * 1000 / 8; |
| 355 | if (format->nAvgBytesPerSec == 0) |
| 356 | { |
| 357 | WARN("Variable-bitrate audio is not supported.\n"); |
| 358 | return E_FAIL; |
| 359 | } |
| 360 | |
| 361 | if (layer == 3) |
| 362 | format->nBlockAlign = format->nAvgBytesPerSec * 8 * 144 / |
| 363 | (format->nSamplesPerSec<<lsf) + |
| 364 | (padding - 4); |
| 365 | else if (layer == 2) |
| 366 | format->nBlockAlign = format->nAvgBytesPerSec * 8 * 144 / |
| 367 | format->nSamplesPerSec + (padding - 4); |
| 368 | else |
| 369 | format->nBlockAlign = ((format->nAvgBytesPerSec * 8 * 12 / |
| 370 | format->nSamplesPerSec + padding) << 2) - 4; |
| 371 | |
| 372 | format->wBitsPerSample = 0; |
| 373 | |
| 374 | if (layer == 3) |
| 375 | { |
| 376 | MPEGLAYER3WAVEFORMAT *mp3format = (MPEGLAYER3WAVEFORMAT*)format; |
| 377 | |
| 378 | format->cbSize = MPEGLAYER3_WFX_EXTRA_BYTES; |
| 379 | |
| 380 | mp3format->wID = MPEGLAYER3_ID_MPEG; |
| 381 | mp3format->fdwFlags = (padding ? |
| 382 | MPEGLAYER3_FLAG_PADDING_OFF : |
| 383 | MPEGLAYER3_FLAG_PADDING_ON); |
| 384 | mp3format->nBlockSize = format->nBlockAlign; |
| 385 | mp3format->nFramesPerBlock = 1; |
| 386 | |
| 387 | /* Beware the evil magic numbers. This struct is apparently horribly |
| 388 | * under-documented, and the only references I could find had it being |
| 389 | * set to this with no real explanation. It works fine though, so I'm |
| 390 | * not complaining (yet). |
| 391 | */ |
| 392 | mp3format->nCodecDelay = 1393; |
| 393 | } |
| 394 | else |
| 395 | { |
| 396 | MPEG1WAVEFORMAT *mpgformat = (MPEG1WAVEFORMAT*)format; |
| 397 | |
| 398 | format->cbSize = 22; |
| 399 | |
| 400 | mpgformat->fwHeadLayer = ((layer == 1) ? ACM_MPEG_LAYER1 : |
| 401 | ((layer == 2) ? ACM_MPEG_LAYER2 : |
| 402 | ACM_MPEG_LAYER3)); |
| 403 | mpgformat->dwHeadBitrate = format->nAvgBytesPerSec * 8; |
| 404 | mpgformat->fwHeadMode = ((mode == 3) ? ACM_MPEG_SINGLECHANNEL : |
| 405 | ((mode == 2) ? ACM_MPEG_DUALCHANNEL : |
| 406 | ((mode == 1) ? ACM_MPEG_JOINTSTEREO : |
| 407 | ACM_MPEG_STEREO))); |
| 408 | mpgformat->fwHeadModeExt = ((mode == 1) ? 0x0F : (1<<mode_ext)); |
| 409 | mpgformat->wHeadEmphasis = emphasis + 1; |
| 410 | mpgformat->fwHeadFlags = ACM_MPEG_ID_MPEG1; |
| 411 | } |
| 412 | pamt->subtype.Data1 = format->wFormatTag; |
| 413 | |
| 414 | TRACE("MPEG audio stream detected:\n" |
| 415 | "\tLayer %d (%#x)\n" |
| 416 | "\tFrequency: %d\n" |
| 417 | "\tChannels: %d (%d)\n" |
| 418 | "\tBytesPerSec: %d\n", |
| 419 | layer, format->wFormatTag, format->nSamplesPerSec, |
| 420 | format->nChannels, mode, format->nAvgBytesPerSec); |
| 421 | |
| 422 | dump_AM_MEDIA_TYPE(pamt); |
| 423 | |
| 424 | return S_OK; |
| 425 | } |
| 426 | |
| 427 | |
| 428 | static HRESULT MPEGSplitter_pre_connect(IPin *iface, IPin *pConnectPin) |
| 429 | { |
| 430 | PullPin *pPin = (PullPin *)iface; |
| 431 | MPEGSplitterImpl *This = (MPEGSplitterImpl*)pPin->pin.pinInfo.pFilter; |
| 432 | ALLOCATOR_PROPERTIES props; |
| 433 | HRESULT hr; |
| 434 | LONGLONG pos = 0; /* in bytes */ |
| 435 | BYTE header[4]; |
| 436 | int streamtype = 0; |
| 437 | LONGLONG total, avail; |
| 438 | AM_MEDIA_TYPE amt; |
| 439 | PIN_INFO piOutput; |
| 440 | |
| 441 | IAsyncReader_Length(pPin->pReader, &total, &avail); |
| 442 | This->EndOfFile = total; |
| 443 | |
| 444 | hr = IAsyncReader_SyncRead(pPin->pReader, pos, 4, (LPVOID)&header[0]); |
| 445 | if (SUCCEEDED(hr)) |
| 446 | pos += 4; |
| 447 | |
| 448 | while(SUCCEEDED(hr) && !(streamtype=MPEGSplitter_head_check(header))) |
| 449 | { |
| 450 | /* No valid header yet; shift by a byte and check again */ |
| 451 | memcpy(header, header+1, 3); |
| 452 | hr = IAsyncReader_SyncRead(pPin->pReader, pos++, 1, (LPVOID)&header[3]); |
| 453 | } |
| 454 | if (FAILED(hr)) |
| 455 | return hr; |
| 456 | |
| 457 | switch(streamtype) |
| 458 | { |
| 459 | case MPEG_AUDIO_HEADER: |
| 460 | hr = MPEGSplitter_init_audio(This, header, &piOutput, &amt); |
| 461 | if (SUCCEEDED(hr)) |
| 462 | { |
| 463 | WAVEFORMATEX *format = (WAVEFORMATEX*)amt.pbFormat; |
| 464 | |
| 465 | props.cbAlign = 1; |
| 466 | props.cbPrefix = 0; |
| 467 | /* Make the output buffer a multiple of the frame size */ |
| 468 | props.cbBuffer = 0x4000 / format->nBlockAlign * |
| 469 | format->nBlockAlign; |
| 470 | props.cBuffers = 1; |
| 471 | |
| 472 | hr = Parser_AddPin(&(This->Parser), &piOutput, &props, &amt, |
| 473 | (float)format->nAvgBytesPerSec / |
| 474 | (float)format->nBlockAlign, |
| 475 | format->nBlockAlign, |
| 476 | total); |
| 477 | } |
| 478 | |
| 479 | if (FAILED(hr)) |
| 480 | { |
| 481 | if (amt.pbFormat) |
| 482 | CoTaskMemFree(amt.pbFormat); |
| 483 | ERR("Could not create pin for MPEG audio stream (%x)\n", hr); |
| 484 | } |
| 485 | break; |
| 486 | case MPEG_VIDEO_HEADER: |
| 487 | FIXME("MPEG video processing not yet supported!\n"); |
| 488 | hr = E_FAIL; |
| 489 | break; |
| 490 | case MPEG_SYSTEM_HEADER: |
| 491 | FIXME("MPEG system streams not yet supported!\n"); |
| 492 | hr = E_FAIL; |
| 493 | break; |
| 494 | |
| 495 | default: |
| 496 | break; |
| 497 | } |
| 498 | |
| 499 | return hr; |
| 500 | } |
| 501 | |
| 502 | static HRESULT MPEGSplitter_cleanup(LPVOID iface) |
| 503 | { |
| 504 | MPEGSplitterImpl *This = (MPEGSplitterImpl*)iface; |
| 505 | |
| 506 | TRACE("(%p)->()\n", This); |
| 507 | |
| 508 | if (This->pCurrentSample) |
| 509 | IMediaSample_Release(This->pCurrentSample); |
| 510 | This->pCurrentSample = NULL; |
| 511 | |
| 512 | return S_OK; |
| 513 | } |
| 514 | |
| 515 | HRESULT MPEGSplitter_create(IUnknown * pUnkOuter, LPVOID * ppv) |
| 516 | { |
| 517 | MPEGSplitterImpl *This; |
| 518 | HRESULT hr = E_FAIL; |
| 519 | |
| 520 | TRACE("(%p, %p)\n", pUnkOuter, ppv); |
| 521 | |
| 522 | *ppv = NULL; |
| 523 | |
| 524 | if (pUnkOuter) |
| 525 | return CLASS_E_NOAGGREGATION; |
| 526 | |
| 527 | This = CoTaskMemAlloc(sizeof(MPEGSplitterImpl)); |
| 528 | if (!This) |
| 529 | return E_OUTOFMEMORY; |
| 530 | |
| 531 | ZeroMemory(This, sizeof(MPEGSplitterImpl)); |
| 532 | hr = Parser_Create(&(This->Parser), &CLSID_MPEG1Splitter, MPEGSplitter_process_sample, MPEGSplitter_query_accept, MPEGSplitter_pre_connect, MPEGSplitter_cleanup); |
| 533 | if (FAILED(hr)) |
| 534 | { |
| 535 | CoTaskMemFree(This); |
| 536 | return hr; |
| 537 | } |
| 538 | |
| 539 | /* Note: This memory is managed by the parser filter once created */ |
| 540 | *ppv = (LPVOID)This; |
| 541 | |
| 542 | return hr; |
| 543 | } |