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