1 /* 2 * MPEG Splitter Filter 3 * 4 * Copyright 2003 Robert Shearman 5 * Copyright 2004-2005 Christian Costa 6 * Copyright 2007 Chris Robinson 7 * Copyright 2008 Maarten Lankhorst 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 22 */ 23 24 #include <assert.h> 25 #include <math.h> 26 27 #include "quartz_private.h" 28 #include "pin.h" 29 30 #include "uuids.h" 31 #include "mmreg.h" 32 #include "mmsystem.h" 33 34 #include "wine/winternl.h" 35 36 #include "wine/unicode.h" 37 #include "wine/debug.h" 38 39 #include "parser.h" 40 41 WINE_DEFAULT_DEBUG_CHANNEL(quartz); 42 43 #define SEQUENCE_HEADER_CODE 0xB3 44 #define PACK_START_CODE 0xBA 45 46 #define SYSTEM_START_CODE 0xBB 47 #define AUDIO_ELEMENTARY_STREAM 0xC0 48 #define VIDEO_ELEMENTARY_STREAM 0xE0 49 50 #define MPEG_SYSTEM_HEADER 3 51 #define MPEG_VIDEO_HEADER 2 52 #define MPEG_AUDIO_HEADER 1 53 #define MPEG_NO_HEADER 0 54 55 typedef struct MPEGSplitterImpl 56 { 57 ParserImpl Parser; 58 IAMStreamSelect IAMStreamSelect_iface; 59 LONGLONG EndOfFile; 60 LONGLONG position; 61 DWORD begin_offset; 62 BYTE header[4]; 63 64 /* Whether we just seeked (or started playing) */ 65 BOOL seek; 66 } MPEGSplitterImpl; 67 68 static inline MPEGSplitterImpl *impl_from_IBaseFilter( IBaseFilter *iface ) 69 { 70 return CONTAINING_RECORD(iface, MPEGSplitterImpl, Parser.filter.IBaseFilter_iface); 71 } 72 73 static inline MPEGSplitterImpl *impl_from_IMediaSeeking( IMediaSeeking *iface ) 74 { 75 return CONTAINING_RECORD(iface, MPEGSplitterImpl, Parser.sourceSeeking.IMediaSeeking_iface); 76 } 77 78 static inline MPEGSplitterImpl *impl_from_IAMStreamSelect( IAMStreamSelect *iface ) 79 { 80 return CONTAINING_RECORD(iface, MPEGSplitterImpl, IAMStreamSelect_iface); 81 } 82 83 static int MPEGSplitter_head_check(const BYTE *header) 84 { 85 /* If this is a possible start code, check for a system or video header */ 86 if (header[0] == 0 && header[1] == 0 && header[2] == 1) 87 { 88 /* Check if we got a system or elementary stream start code */ 89 if (header[3] == PACK_START_CODE || 90 header[3] == VIDEO_ELEMENTARY_STREAM || 91 header[3] == AUDIO_ELEMENTARY_STREAM) 92 return MPEG_SYSTEM_HEADER; 93 94 /* Check for a MPEG video sequence start code */ 95 if (header[3] == SEQUENCE_HEADER_CODE) 96 return MPEG_VIDEO_HEADER; 97 } 98 99 /* This should give a good guess if we have an MPEG audio header */ 100 if(header[0] == 0xff && ((header[1]>>5)&0x7) == 0x7 && 101 ((header[1]>>1)&0x3) != 0 && ((header[2]>>4)&0xf) != 0xf && 102 ((header[2]>>2)&0x3) != 0x3) 103 return MPEG_AUDIO_HEADER; 104 105 /* Nothing yet.. */ 106 return MPEG_NO_HEADER; 107 } 108 109 static const WCHAR wszAudioStream[] = {'A','u','d','i','o',0}; 110 111 static const DWORD freqs[10] = { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000, 0 }; 112 113 static const DWORD tabsel_123[2][3][16] = { 114 { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,}, 115 {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,}, 116 {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} }, 117 118 { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,}, 119 {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,}, 120 {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,} } 121 }; 122 123 static HRESULT parse_header(BYTE *header, LONGLONG *plen, LONGLONG *pduration) 124 { 125 int bitrate_index, freq_index, lsf = 1, mpeg1, layer, padding, bitrate, length; 126 LONGLONG duration; 127 128 if (MPEGSplitter_head_check(header) != MPEG_AUDIO_HEADER) 129 { 130 FIXME("Not a valid header: %02x:%02x:%02x:%02x\n", header[0], header[1], header[2], header[3]); 131 return E_INVALIDARG; 132 } 133 134 mpeg1 = (header[1]>>4)&0x1; 135 if (mpeg1) 136 lsf = ((header[1]>>3)&0x1)^1; 137 138 layer = 4-((header[1]>>1)&0x3); 139 bitrate_index = ((header[2]>>4)&0xf); 140 freq_index = ((header[2]>>2)&0x3) + (mpeg1?(lsf*3):6); 141 padding = ((header[2]>>1)&0x1); 142 143 bitrate = tabsel_123[lsf][layer-1][bitrate_index] * 1000; 144 if (!bitrate) 145 { 146 FIXME("Not a valid header: %02x:%02x:%02x:%02x\n", header[0], header[1], header[2], header[3]); 147 return E_INVALIDARG; 148 } 149 150 if (layer == 1) 151 length = 4 * (12 * bitrate / freqs[freq_index] + padding); 152 else if (layer == 2) 153 length = 144 * bitrate / freqs[freq_index] + padding; 154 else if (layer == 3) 155 length = 144 * bitrate / (freqs[freq_index]<<lsf) + padding; 156 else 157 { 158 ERR("Impossible layer %d\n", layer); 159 return E_INVALIDARG; 160 } 161 162 duration = (ULONGLONG)10000000 * (ULONGLONG)(length) / (ULONGLONG)(bitrate/8); 163 *plen = length; 164 if (pduration) 165 *pduration += duration; 166 return S_OK; 167 } 168 169 static HRESULT FillBuffer(MPEGSplitterImpl *This, IMediaSample *pCurrentSample) 170 { 171 Parser_OutputPin * pOutputPin = unsafe_impl_Parser_OutputPin_from_IPin(This->Parser.ppPins[1]); 172 LONGLONG length = 0; 173 LONGLONG pos = BYTES_FROM_MEDIATIME(This->Parser.pInputPin->rtNext); 174 LONGLONG time = This->position, rtstop, rtstart; 175 HRESULT hr; 176 BYTE *fbuf = NULL; 177 DWORD len = IMediaSample_GetActualDataLength(pCurrentSample); 178 179 TRACE("Source length: %u\n", len); 180 IMediaSample_GetPointer(pCurrentSample, &fbuf); 181 182 /* Find the next valid header.. it <SHOULD> be right here */ 183 hr = parse_header(fbuf, &length, &This->position); 184 assert(hr == S_OK); 185 IMediaSample_SetActualDataLength(pCurrentSample, length); 186 187 /* Queue the next sample */ 188 if (length + 4 == len) 189 { 190 PullPin *pin = This->Parser.pInputPin; 191 LONGLONG stop = BYTES_FROM_MEDIATIME(pin->rtStop); 192 193 hr = S_OK; 194 memcpy(This->header, fbuf + length, 4); 195 while (FAILED(hr = parse_header(This->header, &length, NULL))) 196 { 197 memmove(This->header, This->header+1, 3); 198 if (pos + 4 >= stop) 199 break; 200 IAsyncReader_SyncRead(pin->pReader, ++pos, 1, This->header + 3); 201 } 202 pin->rtNext = MEDIATIME_FROM_BYTES(pos); 203 204 if (SUCCEEDED(hr)) 205 { 206 /* Remove 4 for the last header, which should hopefully work */ 207 IMediaSample *sample = NULL; 208 LONGLONG rtSampleStart = pin->rtNext - MEDIATIME_FROM_BYTES(4); 209 LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(length + 4); 210 211 if (rtSampleStop > pin->rtStop) 212 rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign)); 213 214 hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0); 215 if (SUCCEEDED(hr)) 216 { 217 IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop); 218 IMediaSample_SetPreroll(sample, FALSE); 219 IMediaSample_SetDiscontinuity(sample, FALSE); 220 IMediaSample_SetSyncPoint(sample, TRUE); 221 hr = IAsyncReader_Request(pin->pReader, sample, 0); 222 if (SUCCEEDED(hr)) 223 { 224 pin->rtCurrent = rtSampleStart; 225 pin->rtNext = rtSampleStop; 226 } 227 else 228 IMediaSample_Release(sample); 229 } 230 if (FAILED(hr)) 231 FIXME("o_Ox%08x\n", hr); 232 } 233 } 234 /* If not, we're presumably at the end of file */ 235 236 TRACE("Media time : %u.%03u\n", (DWORD)(This->position/10000000), (DWORD)((This->position/10000)%1000)); 237 238 if (IMediaSample_IsDiscontinuity(pCurrentSample) == S_OK) { 239 IPin *victim; 240 EnterCriticalSection(&This->Parser.filter.csFilter); 241 pOutputPin->pin.pin.tStart = time; 242 pOutputPin->pin.pin.dRate = This->Parser.sourceSeeking.dRate; 243 hr = IPin_ConnectedTo(&pOutputPin->pin.pin.IPin_iface, &victim); 244 if (hr == S_OK) 245 { 246 hr = IPin_NewSegment(victim, time, This->Parser.sourceSeeking.llStop, 247 This->Parser.sourceSeeking.dRate); 248 if (hr != S_OK) 249 FIXME("NewSegment returns %08x\n", hr); 250 IPin_Release(victim); 251 } 252 LeaveCriticalSection(&This->Parser.filter.csFilter); 253 if (hr != S_OK) 254 return hr; 255 } 256 rtstart = (double)(time - pOutputPin->pin.pin.tStart) / pOutputPin->pin.pin.dRate; 257 rtstop = (double)(This->position - pOutputPin->pin.pin.tStart) / pOutputPin->pin.pin.dRate; 258 IMediaSample_SetTime(pCurrentSample, &rtstart, &rtstop); 259 IMediaSample_SetMediaTime(pCurrentSample, &time, &This->position); 260 261 hr = BaseOutputPinImpl_Deliver(&pOutputPin->pin, pCurrentSample); 262 263 if (hr != S_OK) 264 { 265 if (hr != S_FALSE) 266 TRACE("Error sending sample (%x)\n", hr); 267 else 268 TRACE("S_FALSE (%d), holding\n", IMediaSample_GetActualDataLength(pCurrentSample)); 269 } 270 271 return hr; 272 } 273 274 275 static HRESULT MPEGSplitter_process_sample(LPVOID iface, IMediaSample * pSample, DWORD_PTR cookie) 276 { 277 MPEGSplitterImpl *This = iface; 278 BYTE *pbSrcStream; 279 DWORD cbSrcStream = 0; 280 REFERENCE_TIME tStart, tStop, tAviStart = This->position; 281 HRESULT hr; 282 283 hr = IMediaSample_GetTime(pSample, &tStart, &tStop); 284 if (SUCCEEDED(hr)) 285 { 286 cbSrcStream = IMediaSample_GetActualDataLength(pSample); 287 hr = IMediaSample_GetPointer(pSample, &pbSrcStream); 288 } 289 290 /* Flush occurring */ 291 if (cbSrcStream == 0) 292 { 293 FIXME(".. Why do I need you?\n"); 294 return S_OK; 295 } 296 297 /* trace removed for performance reasons */ 298 /* TRACE("(%p), %llu -> %llu\n", pSample, tStart, tStop); */ 299 300 /* Now, try to find a new header */ 301 hr = FillBuffer(This, pSample); 302 if (hr != S_OK) 303 { 304 WARN("Failed with hres: %08x!\n", hr); 305 306 /* Unset progression if denied! */ 307 if (hr == VFW_E_WRONG_STATE || hr == S_FALSE) 308 { 309 memcpy(This->header, pbSrcStream, 4); 310 This->Parser.pInputPin->rtCurrent = tStart; 311 This->position = tAviStart; 312 } 313 } 314 315 if (BYTES_FROM_MEDIATIME(tStop) >= This->EndOfFile || This->position >= This->Parser.sourceSeeking.llStop) 316 { 317 unsigned int i; 318 319 TRACE("End of file reached\n"); 320 321 for (i = 0; i < This->Parser.cStreams; i++) 322 { 323 IPin* ppin; 324 325 hr = IPin_ConnectedTo(This->Parser.ppPins[i+1], &ppin); 326 if (SUCCEEDED(hr)) 327 { 328 hr = IPin_EndOfStream(ppin); 329 IPin_Release(ppin); 330 } 331 if (FAILED(hr)) 332 WARN("Error sending EndOfStream to pin %u (%x)\n", i, hr); 333 } 334 335 /* Force the pullpin thread to stop */ 336 hr = S_FALSE; 337 } 338 339 return hr; 340 } 341 342 343 static HRESULT MPEGSplitter_query_accept(LPVOID iface, const AM_MEDIA_TYPE *pmt) 344 { 345 if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream)) 346 return S_FALSE; 347 348 if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1Audio)) 349 return S_OK; 350 351 if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1Video)) 352 FIXME("MPEG-1 video streams not yet supported.\n"); 353 else if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1System)) 354 FIXME("MPEG-1 system streams not yet supported.\n"); 355 else if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1VideoCD)) 356 FIXME("MPEG-1 VideoCD streams not yet supported.\n"); 357 else FIXME("%s\n", debugstr_guid(&pmt->subtype)); 358 359 return S_FALSE; 360 } 361 362 363 static HRESULT MPEGSplitter_init_audio(MPEGSplitterImpl *This, const BYTE *header, PIN_INFO *ppiOutput, AM_MEDIA_TYPE *pamt) 364 { 365 WAVEFORMATEX *format; 366 int bitrate_index; 367 int freq_index; 368 int mode_ext; 369 int emphasis; 370 int padding; 371 int lsf = 1; 372 int mpeg1; 373 int layer; 374 int mode; 375 376 ZeroMemory(pamt, sizeof(*pamt)); 377 ppiOutput->dir = PINDIR_OUTPUT; 378 ppiOutput->pFilter = &This->Parser.filter.IBaseFilter_iface; 379 wsprintfW(ppiOutput->achName, wszAudioStream); 380 381 pamt->formattype = FORMAT_WaveFormatEx; 382 pamt->majortype = MEDIATYPE_Audio; 383 pamt->subtype = MEDIASUBTYPE_MPEG1AudioPayload; 384 385 pamt->lSampleSize = 0; 386 pamt->bFixedSizeSamples = FALSE; 387 pamt->bTemporalCompression = 0; 388 389 mpeg1 = (header[1]>>4)&0x1; 390 if (mpeg1) 391 lsf = ((header[1]>>3)&0x1)^1; 392 393 layer = 4-((header[1]>>1)&0x3); 394 bitrate_index = ((header[2]>>4)&0xf); 395 padding = ((header[2]>>1)&0x1); 396 freq_index = ((header[2]>>2)&0x3) + (mpeg1?(lsf*3):6); 397 mode = ((header[3]>>6)&0x3); 398 mode_ext = ((header[3]>>4)&0x3); 399 emphasis = ((header[3]>>0)&0x3); 400 401 if (!bitrate_index) 402 { 403 /* Set to highest bitrate so samples will fit in for sure */ 404 FIXME("Variable-bitrate audio not fully supported.\n"); 405 bitrate_index = 15; 406 } 407 408 pamt->cbFormat = ((layer==3)? sizeof(MPEGLAYER3WAVEFORMAT) : 409 sizeof(MPEG1WAVEFORMAT)); 410 pamt->pbFormat = CoTaskMemAlloc(pamt->cbFormat); 411 if (!pamt->pbFormat) 412 return E_OUTOFMEMORY; 413 ZeroMemory(pamt->pbFormat, pamt->cbFormat); 414 format = (WAVEFORMATEX*)pamt->pbFormat; 415 416 format->wFormatTag = ((layer == 3) ? WAVE_FORMAT_MPEGLAYER3 : 417 WAVE_FORMAT_MPEG); 418 format->nChannels = ((mode == 3) ? 1 : 2); 419 format->nSamplesPerSec = freqs[freq_index]; 420 format->nAvgBytesPerSec = tabsel_123[lsf][layer-1][bitrate_index] * 1000 / 8; 421 422 if (layer == 3) 423 format->nBlockAlign = format->nAvgBytesPerSec * 8 * 144 / 424 (format->nSamplesPerSec<<lsf) + padding; 425 else if (layer == 2) 426 format->nBlockAlign = format->nAvgBytesPerSec * 8 * 144 / 427 format->nSamplesPerSec + padding; 428 else 429 format->nBlockAlign = 4 * (format->nAvgBytesPerSec * 8 * 12 / format->nSamplesPerSec + padding); 430 431 format->wBitsPerSample = 0; 432 433 if (layer == 3) 434 { 435 MPEGLAYER3WAVEFORMAT *mp3format = (MPEGLAYER3WAVEFORMAT*)format; 436 437 format->cbSize = MPEGLAYER3_WFX_EXTRA_BYTES; 438 439 mp3format->wID = MPEGLAYER3_ID_MPEG; 440 mp3format->fdwFlags = MPEGLAYER3_FLAG_PADDING_ON; 441 mp3format->nBlockSize = format->nBlockAlign; 442 mp3format->nFramesPerBlock = 1; 443 444 /* Beware the evil magic numbers. This struct is apparently horribly 445 * under-documented, and the only references I could find had it being 446 * set to this with no real explanation. It works fine though, so I'm 447 * not complaining (yet). 448 */ 449 mp3format->nCodecDelay = 1393; 450 } 451 else 452 { 453 MPEG1WAVEFORMAT *mpgformat = (MPEG1WAVEFORMAT*)format; 454 455 format->cbSize = 22; 456 457 mpgformat->fwHeadLayer = ((layer == 1) ? ACM_MPEG_LAYER1 : 458 ((layer == 2) ? ACM_MPEG_LAYER2 : 459 ACM_MPEG_LAYER3)); 460 mpgformat->dwHeadBitrate = format->nAvgBytesPerSec * 8; 461 mpgformat->fwHeadMode = ((mode == 3) ? ACM_MPEG_SINGLECHANNEL : 462 ((mode == 2) ? ACM_MPEG_DUALCHANNEL : 463 ((mode == 1) ? ACM_MPEG_JOINTSTEREO : 464 ACM_MPEG_STEREO))); 465 mpgformat->fwHeadModeExt = ((mode == 1) ? 0x0F : (1<<mode_ext)); 466 mpgformat->wHeadEmphasis = emphasis + 1; 467 mpgformat->fwHeadFlags = ACM_MPEG_ID_MPEG1; 468 } 469 pamt->subtype.Data1 = format->wFormatTag; 470 471 TRACE("MPEG audio stream detected:\n" 472 "\tLayer %d (%#x)\n" 473 "\tFrequency: %d\n" 474 "\tChannels: %d (%d)\n" 475 "\tBytesPerSec: %d\n", 476 layer, format->wFormatTag, format->nSamplesPerSec, 477 format->nChannels, mode, format->nAvgBytesPerSec); 478 479 dump_AM_MEDIA_TYPE(pamt); 480 481 return S_OK; 482 } 483 484 485 static HRESULT MPEGSplitter_pre_connect(IPin *iface, IPin *pConnectPin, ALLOCATOR_PROPERTIES *props) 486 { 487 PullPin *pPin = impl_PullPin_from_IPin(iface); 488 MPEGSplitterImpl *This = (MPEGSplitterImpl*)pPin->pin.pinInfo.pFilter; 489 HRESULT hr; 490 LONGLONG pos = 0; /* in bytes */ 491 BYTE header[10]; 492 int streamtype; 493 LONGLONG total, avail; 494 AM_MEDIA_TYPE amt; 495 PIN_INFO piOutput; 496 497 IAsyncReader_Length(pPin->pReader, &total, &avail); 498 This->EndOfFile = total; 499 500 hr = IAsyncReader_SyncRead(pPin->pReader, pos, 4, header); 501 if (SUCCEEDED(hr)) 502 pos += 4; 503 504 /* Skip ID3 v2 tag, if any */ 505 if (SUCCEEDED(hr) && !memcmp("ID3", header, 3)) 506 do { 507 UINT length = 0; 508 hr = IAsyncReader_SyncRead(pPin->pReader, pos, 6, header + 4); 509 if (FAILED(hr)) 510 break; 511 pos += 6; 512 TRACE("Found ID3 v2.%d.%d\n", header[3], header[4]); 513 if(header[3] <= 4 && header[4] != 0xff && 514 (header[5]&0x0f) == 0 && (header[6]&0x80) == 0 && 515 (header[7]&0x80) == 0 && (header[8]&0x80) == 0 && 516 (header[9]&0x80) == 0) 517 { 518 length = (header[6]<<21) | (header[7]<<14) | 519 (header[8]<< 7) | (header[9] ); 520 if((header[5]&0x10)) 521 length += 10; 522 TRACE("Length: %u\n", length); 523 } 524 pos += length; 525 526 /* Read the real header for the mpeg splitter */ 527 hr = IAsyncReader_SyncRead(pPin->pReader, pos, 4, header); 528 if (SUCCEEDED(hr)) 529 pos += 4; 530 } while (0); 531 532 while(SUCCEEDED(hr)) 533 { 534 TRACE("Testing header %x:%x:%x:%x\n", header[0], header[1], header[2], header[3]); 535 536 streamtype = MPEGSplitter_head_check(header); 537 if (streamtype == MPEG_AUDIO_HEADER) 538 { 539 LONGLONG length; 540 if (parse_header(header, &length, NULL) == S_OK) 541 { 542 BYTE next_header[4]; 543 /* Ensure we have a valid header by seeking for the next frame, some bad 544 * encoded ID3v2 may have an incorrect length and we end up finding bytes 545 * like FF FE 00 28 which are nothing more than a Unicode BOM followed by 546 * ')' character from inside a ID3v2 tag. Unfortunately that sequence 547 * matches like a valid mpeg audio header. 548 */ 549 hr = IAsyncReader_SyncRead(pPin->pReader, pos + length - 4, 4, next_header); 550 if (FAILED(hr)) 551 break; 552 if (parse_header(next_header, &length, NULL) == S_OK) 553 break; 554 TRACE("%x:%x:%x:%x is a fake audio header, looking for next...\n", 555 header[0], header[1], header[2], header[3]); 556 } 557 } 558 else if (streamtype) /* Video or System stream */ 559 break; 560 561 /* No valid header yet; shift by a byte and check again */ 562 memmove(header, header+1, 3); 563 hr = IAsyncReader_SyncRead(pPin->pReader, pos++, 1, header + 3); 564 } 565 if (FAILED(hr)) 566 return hr; 567 pos -= 4; 568 This->begin_offset = pos; 569 memcpy(This->header, header, 4); 570 571 switch(streamtype) 572 { 573 case MPEG_AUDIO_HEADER: 574 { 575 LONGLONG duration = 0; 576 WAVEFORMATEX *format; 577 578 hr = MPEGSplitter_init_audio(This, header, &piOutput, &amt); 579 if (SUCCEEDED(hr)) 580 { 581 format = (WAVEFORMATEX*)amt.pbFormat; 582 583 props->cbAlign = 1; 584 props->cbPrefix = 0; 585 /* Make the output buffer a multiple of the frame size */ 586 props->cbBuffer = 0x4000 / format->nBlockAlign * 587 format->nBlockAlign; 588 props->cBuffers = 3; 589 hr = Parser_AddPin(&(This->Parser), &piOutput, props, &amt); 590 } 591 592 if (FAILED(hr)) 593 { 594 CoTaskMemFree(amt.pbFormat); 595 ERR("Could not create pin for MPEG audio stream (%x)\n", hr); 596 break; 597 } 598 599 /* Check for idv1 tag, and remove it from stream if found */ 600 hr = IAsyncReader_SyncRead(pPin->pReader, This->EndOfFile-128, 3, header); 601 if (FAILED(hr)) 602 break; 603 if (!strncmp((char*)header, "TAG", 3)) 604 This->EndOfFile -= 128; 605 This->Parser.pInputPin->rtStop = MEDIATIME_FROM_BYTES(This->EndOfFile); 606 This->Parser.pInputPin->rtStart = This->Parser.pInputPin->rtCurrent = MEDIATIME_FROM_BYTES(This->begin_offset); 607 608 duration = (This->EndOfFile-This->begin_offset) * 10000000 / format->nAvgBytesPerSec; 609 TRACE("Duration: %d seconds\n", (DWORD)(duration / 10000000)); 610 611 This->Parser.sourceSeeking.llCurrent = 0; 612 This->Parser.sourceSeeking.llDuration = duration; 613 This->Parser.sourceSeeking.llStop = duration; 614 break; 615 } 616 case MPEG_VIDEO_HEADER: 617 FIXME("MPEG video processing not yet supported!\n"); 618 hr = E_FAIL; 619 break; 620 case MPEG_SYSTEM_HEADER: 621 FIXME("MPEG system streams not yet supported!\n"); 622 hr = E_FAIL; 623 break; 624 625 default: 626 break; 627 } 628 This->position = 0; 629 630 return hr; 631 } 632 633 static HRESULT MPEGSplitter_cleanup(LPVOID iface) 634 { 635 MPEGSplitterImpl *This = iface; 636 637 TRACE("(%p)\n", This); 638 639 return S_OK; 640 } 641 642 static HRESULT WINAPI MPEGSplitter_seek(IMediaSeeking *iface) 643 { 644 MPEGSplitterImpl *This = impl_from_IMediaSeeking(iface); 645 PullPin *pPin = This->Parser.pInputPin; 646 LONGLONG newpos, timepos, bytepos; 647 HRESULT hr = E_INVALIDARG; 648 BYTE header[4]; 649 650 newpos = This->Parser.sourceSeeking.llCurrent; 651 if (This->position/1000000 == newpos/1000000) 652 { 653 TRACE("Requesting position %x%08x same as current position %x%08x\n", (DWORD)(newpos>>32), (DWORD)newpos, (DWORD)(This->position>>32), (DWORD)This->position); 654 return S_OK; 655 } 656 657 bytepos = This->begin_offset; 658 timepos = 0; 659 /* http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm has a whole read up on audio headers */ 660 while (bytepos + 3 < This->EndOfFile) 661 { 662 LONGLONG duration = timepos; 663 LONGLONG length = 0; 664 hr = IAsyncReader_SyncRead(pPin->pReader, bytepos, 4, header); 665 if (hr != S_OK) 666 break; 667 while ((hr=parse_header(header, &length, &duration)) != S_OK && 668 bytepos + 4 < This->EndOfFile) 669 { 670 /* No valid header yet; shift by a byte and check again */ 671 memmove(header, header+1, 3); 672 hr = IAsyncReader_SyncRead(pPin->pReader, ++bytepos + 3, 1, header + 3); 673 if (hr != S_OK) 674 break; 675 } 676 if (hr != S_OK || duration > newpos) 677 break; 678 bytepos += length; 679 timepos = duration; 680 } 681 682 if (SUCCEEDED(hr)) 683 { 684 PullPin *pin = This->Parser.pInputPin; 685 686 TRACE("Moving sound to %08u bytes!\n", (DWORD)bytepos); 687 688 EnterCriticalSection(&pin->thread_lock); 689 IPin_BeginFlush(&pin->pin.IPin_iface); 690 691 /* Make sure this is done while stopped, BeginFlush takes care of this */ 692 EnterCriticalSection(&This->Parser.filter.csFilter); 693 memcpy(This->header, header, 4); 694 695 pin->rtStart = pin->rtCurrent = MEDIATIME_FROM_BYTES(bytepos); 696 pin->rtStop = MEDIATIME_FROM_BYTES((REFERENCE_TIME)This->EndOfFile); 697 This->seek = TRUE; 698 This->position = newpos; 699 LeaveCriticalSection(&This->Parser.filter.csFilter); 700 701 TRACE("Done flushing\n"); 702 IPin_EndFlush(&pin->pin.IPin_iface); 703 LeaveCriticalSection(&pin->thread_lock); 704 } 705 return hr; 706 } 707 708 static HRESULT MPEGSplitter_disconnect(LPVOID iface) 709 { 710 /* TODO: Find memory leaks etc */ 711 return S_OK; 712 } 713 714 static HRESULT MPEGSplitter_first_request(LPVOID iface) 715 { 716 MPEGSplitterImpl *This = iface; 717 PullPin *pin = This->Parser.pInputPin; 718 HRESULT hr; 719 LONGLONG length; 720 IMediaSample *sample; 721 722 TRACE("Seeking? %d\n", This->seek); 723 724 hr = parse_header(This->header, &length, NULL); 725 assert(hr == S_OK); 726 727 if (pin->rtCurrent >= pin->rtStop) 728 { 729 /* Last sample has already been queued, request nothing more */ 730 FIXME("Done!\n"); 731 return S_OK; 732 } 733 734 hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0); 735 736 pin->rtNext = pin->rtCurrent; 737 if (SUCCEEDED(hr)) 738 { 739 LONGLONG rtSampleStart = pin->rtNext; 740 /* Add 4 for the next header, which should hopefully work */ 741 LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(length + 4); 742 743 if (rtSampleStop > pin->rtStop) 744 rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign)); 745 746 IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop); 747 IMediaSample_SetPreroll(sample, FALSE); 748 IMediaSample_SetDiscontinuity(sample, TRUE); 749 IMediaSample_SetSyncPoint(sample, 1); 750 This->seek = FALSE; 751 752 hr = IAsyncReader_Request(pin->pReader, sample, 0); 753 if (SUCCEEDED(hr)) 754 { 755 pin->rtCurrent = pin->rtNext; 756 pin->rtNext = rtSampleStop; 757 } 758 else 759 IMediaSample_Release(sample); 760 } 761 if (FAILED(hr)) 762 ERR("Horsemen of the apocalypse came to bring error 0x%08x\n", hr); 763 764 return hr; 765 } 766 767 static HRESULT WINAPI MPEGSplitter_QueryInterface(IBaseFilter *iface, REFIID riid, void **ppv) 768 { 769 MPEGSplitterImpl *This = impl_from_IBaseFilter(iface); 770 TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv); 771 772 *ppv = NULL; 773 774 if ( IsEqualIID(riid, &IID_IUnknown) 775 || IsEqualIID(riid, &IID_IPersist) 776 || IsEqualIID(riid, &IID_IMediaFilter) 777 || IsEqualIID(riid, &IID_IBaseFilter) ) 778 *ppv = iface; 779 else if ( IsEqualIID(riid, &IID_IAMStreamSelect) ) 780 *ppv = &This->IAMStreamSelect_iface; 781 782 if (*ppv) 783 { 784 IBaseFilter_AddRef(iface); 785 return S_OK; 786 } 787 788 if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow)) 789 FIXME("No interface for %s!\n", qzdebugstr_guid(riid)); 790 791 return E_NOINTERFACE; 792 } 793 794 static const IBaseFilterVtbl MPEGSplitter_Vtbl = 795 { 796 MPEGSplitter_QueryInterface, 797 Parser_AddRef, 798 Parser_Release, 799 Parser_GetClassID, 800 Parser_Stop, 801 Parser_Pause, 802 Parser_Run, 803 Parser_GetState, 804 Parser_SetSyncSource, 805 Parser_GetSyncSource, 806 Parser_EnumPins, 807 Parser_FindPin, 808 Parser_QueryFilterInfo, 809 Parser_JoinFilterGraph, 810 Parser_QueryVendorInfo 811 }; 812 813 static HRESULT WINAPI AMStreamSelect_QueryInterface(IAMStreamSelect *iface, REFIID riid, void **ppv) 814 { 815 MPEGSplitterImpl *This = impl_from_IAMStreamSelect(iface); 816 817 return IBaseFilter_QueryInterface(&This->Parser.filter.IBaseFilter_iface, riid, ppv); 818 } 819 820 static ULONG WINAPI AMStreamSelect_AddRef(IAMStreamSelect *iface) 821 { 822 MPEGSplitterImpl *This = impl_from_IAMStreamSelect(iface); 823 824 return IBaseFilter_AddRef(&This->Parser.filter.IBaseFilter_iface); 825 } 826 827 static ULONG WINAPI AMStreamSelect_Release(IAMStreamSelect *iface) 828 { 829 MPEGSplitterImpl *This = impl_from_IAMStreamSelect(iface); 830 831 return IBaseFilter_Release(&This->Parser.filter.IBaseFilter_iface); 832 } 833 834 static HRESULT WINAPI AMStreamSelect_Count(IAMStreamSelect *iface, DWORD *streams) 835 { 836 MPEGSplitterImpl *This = impl_from_IAMStreamSelect(iface); 837 838 FIXME("(%p/%p)->(%p) stub!\n", This, iface, streams); 839 840 return E_NOTIMPL; 841 } 842 843 static HRESULT WINAPI AMStreamSelect_Info(IAMStreamSelect *iface, LONG index, AM_MEDIA_TYPE **media_type, DWORD *flags, LCID *lcid, DWORD *group, WCHAR **name, IUnknown **object, IUnknown **unknown) 844 { 845 MPEGSplitterImpl *This = impl_from_IAMStreamSelect(iface); 846 847 FIXME("(%p/%p)->(%d,%p,%p,%p,%p,%p,%p,%p) stub!\n", This, iface, index, media_type, flags, lcid, group, name, object, unknown); 848 849 return E_NOTIMPL; 850 } 851 852 static HRESULT WINAPI AMStreamSelect_Enable(IAMStreamSelect *iface, LONG index, DWORD flags) 853 { 854 MPEGSplitterImpl *This = impl_from_IAMStreamSelect(iface); 855 856 FIXME("(%p/%p)->(%d,%x) stub!\n", This, iface, index, flags); 857 858 return E_NOTIMPL; 859 } 860 861 static const IAMStreamSelectVtbl AMStreamSelectVtbl = 862 { 863 AMStreamSelect_QueryInterface, 864 AMStreamSelect_AddRef, 865 AMStreamSelect_Release, 866 AMStreamSelect_Count, 867 AMStreamSelect_Info, 868 AMStreamSelect_Enable 869 }; 870 871 HRESULT MPEGSplitter_create(IUnknown * pUnkOuter, LPVOID * ppv) 872 { 873 MPEGSplitterImpl *This; 874 HRESULT hr = E_FAIL; 875 876 TRACE("(%p, %p)\n", pUnkOuter, ppv); 877 878 *ppv = NULL; 879 880 if (pUnkOuter) 881 return CLASS_E_NOAGGREGATION; 882 883 This = CoTaskMemAlloc(sizeof(MPEGSplitterImpl)); 884 if (!This) 885 return E_OUTOFMEMORY; 886 887 ZeroMemory(This, sizeof(MPEGSplitterImpl)); 888 hr = Parser_Create(&(This->Parser), &MPEGSplitter_Vtbl, &CLSID_MPEG1Splitter, MPEGSplitter_process_sample, MPEGSplitter_query_accept, MPEGSplitter_pre_connect, MPEGSplitter_cleanup, MPEGSplitter_disconnect, MPEGSplitter_first_request, NULL, NULL, MPEGSplitter_seek, NULL); 889 if (FAILED(hr)) 890 { 891 CoTaskMemFree(This); 892 return hr; 893 } 894 This->IAMStreamSelect_iface.lpVtbl = &AMStreamSelectVtbl; 895 This->seek = TRUE; 896 897 /* Note: This memory is managed by the parser filter once created */ 898 *ppv = &This->Parser.filter.IBaseFilter_iface; 899 900 return hr; 901 } 902