Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenmpegsplit.c
Go to the documentation of this file.
00001 /* 00002 * MPEG Splitter Filter 00003 * 00004 * Copyright 2003 Robert Shearman 00005 * Copyright 2004-2005 Christian Costa 00006 * Copyright 2007 Chris Robinson 00007 * Copyright 2008 Maarten Lankhorst 00008 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 2.1 of the License, or (at your option) any later version. 00013 * 00014 * This library is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Lesser General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Lesser General Public 00020 * License along with this library; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00022 */ 00023 00024 #include <assert.h> 00025 #include <math.h> 00026 00027 #include "quartz_private.h" 00028 #include "control_private.h" 00029 #include "pin.h" 00030 00031 #include "uuids.h" 00032 #include "mmreg.h" 00033 #include "mmsystem.h" 00034 00035 #include "winternl.h" 00036 00037 #include "wine/unicode.h" 00038 #include "wine/debug.h" 00039 00040 #include "parser.h" 00041 00042 WINE_DEFAULT_DEBUG_CHANNEL(quartz); 00043 00044 #define SEQUENCE_HEADER_CODE 0xB3 00045 #define PACK_START_CODE 0xBA 00046 00047 #define SYSTEM_START_CODE 0xBB 00048 #define AUDIO_ELEMENTARY_STREAM 0xC0 00049 #define VIDEO_ELEMENTARY_STREAM 0xE0 00050 00051 #define MPEG_SYSTEM_HEADER 3 00052 #define MPEG_VIDEO_HEADER 2 00053 #define MPEG_AUDIO_HEADER 1 00054 #define MPEG_NO_HEADER 0 00055 00056 #define SEEK_INTERVAL (ULONGLONG)(10 * 10000000) /* Add an entry every 10 seconds */ 00057 00058 struct seek_entry { 00059 ULONGLONG bytepos; 00060 ULONGLONG timepos; 00061 }; 00062 00063 typedef struct MPEGSplitterImpl 00064 { 00065 ParserImpl Parser; 00066 LONGLONG EndOfFile; 00067 LONGLONG duration; 00068 LONGLONG position; 00069 DWORD begin_offset; 00070 BYTE header[4]; 00071 00072 /* Whether we just seeked (or started playing) */ 00073 BOOL seek; 00074 00075 /* Seeking cache */ 00076 ULONG seek_entries; 00077 struct seek_entry *seektable; 00078 } MPEGSplitterImpl; 00079 00080 static int MPEGSplitter_head_check(const BYTE *header) 00081 { 00082 /* If this is a possible start code, check for a system or video header */ 00083 if (header[0] == 0 && header[1] == 0 && header[2] == 1) 00084 { 00085 /* Check if we got a system or elementary stream start code */ 00086 if (header[3] == PACK_START_CODE || 00087 header[3] == VIDEO_ELEMENTARY_STREAM || 00088 header[3] == AUDIO_ELEMENTARY_STREAM) 00089 return MPEG_SYSTEM_HEADER; 00090 00091 /* Check for a MPEG video sequence start code */ 00092 if (header[3] == SEQUENCE_HEADER_CODE) 00093 return MPEG_VIDEO_HEADER; 00094 } 00095 00096 /* This should give a good guess if we have an MPEG audio header */ 00097 if(header[0] == 0xff && ((header[1]>>5)&0x7) == 0x7 && 00098 ((header[1]>>1)&0x3) != 0 && ((header[2]>>4)&0xf) != 0xf && 00099 ((header[2]>>2)&0x3) != 0x3) 00100 return MPEG_AUDIO_HEADER; 00101 00102 /* Nothing yet.. */ 00103 return MPEG_NO_HEADER; 00104 } 00105 00106 static const WCHAR wszAudioStream[] = {'A','u','d','i','o',0}; 00107 static const WCHAR wszVideoStream[] = {'V','i','d','e','o',0}; 00108 00109 static const DWORD freqs[10] = { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000, 0 }; 00110 00111 static const DWORD tabsel_123[2][3][16] = { 00112 { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,}, 00113 {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,}, 00114 {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} }, 00115 00116 { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,}, 00117 {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,}, 00118 {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,} } 00119 }; 00120 00121 static HRESULT parse_header(BYTE *header, LONGLONG *plen, LONGLONG *pduration) 00122 { 00123 LONGLONG duration; 00124 00125 int bitrate_index, freq_index, lsf = 1, mpeg1, layer, padding, bitrate, length; 00126 00127 if (!(header[0] == 0xff && ((header[1]>>5)&0x7) == 0x7 && 00128 ((header[1]>>1)&0x3) != 0 && ((header[2]>>4)&0xf) != 0xf && 00129 ((header[2]>>2)&0x3) != 0x3)) 00130 { 00131 FIXME("Not a valid header: %02x:%02x\n", header[0], header[1]); 00132 return E_INVALIDARG; 00133 } 00134 00135 mpeg1 = (header[1]>>4)&0x1; 00136 if (mpeg1) 00137 lsf = ((header[1]>>3)&0x1)^1; 00138 00139 layer = 4-((header[1]>>1)&0x3); 00140 bitrate_index = ((header[2]>>4)&0xf); 00141 freq_index = ((header[2]>>2)&0x3) + (mpeg1?(lsf*3):6); 00142 padding = ((header[2]>>1)&0x1); 00143 00144 bitrate = tabsel_123[lsf][layer-1][bitrate_index] * 1000; 00145 if (!bitrate || layer != 3) 00146 { 00147 FIXME("Not a valid header: %02x:%02x:%02x:%02x\n", header[0], header[1], header[2], header[3]); 00148 return E_INVALIDARG; 00149 } 00150 00151 00152 if (layer == 3 || layer == 2) 00153 length = 144 * bitrate / freqs[freq_index] + padding; 00154 else 00155 length = 4 * (12 * bitrate / freqs[freq_index] + padding); 00156 00157 duration = (ULONGLONG)10000000 * (ULONGLONG)(length) / (ULONGLONG)(bitrate/8); 00158 *plen = length; 00159 if (pduration) 00160 *pduration += duration; 00161 return S_OK; 00162 } 00163 00164 static HRESULT FillBuffer(MPEGSplitterImpl *This, IMediaSample *pCurrentSample) 00165 { 00166 Parser_OutputPin * pOutputPin = (Parser_OutputPin*)This->Parser.ppPins[1]; 00167 LONGLONG length = 0; 00168 LONGLONG pos = BYTES_FROM_MEDIATIME(This->Parser.pInputPin->rtNext); 00169 LONGLONG time = This->position; 00170 HRESULT hr; 00171 BYTE *fbuf = NULL; 00172 DWORD len = IMediaSample_GetActualDataLength(pCurrentSample); 00173 00174 TRACE("Source length: %u\n", len); 00175 IMediaSample_GetPointer(pCurrentSample, &fbuf); 00176 00177 /* Find the next valid header.. it <SHOULD> be right here */ 00178 assert(parse_header(fbuf, &length, &This->position) == S_OK); 00179 IMediaSample_SetActualDataLength(pCurrentSample, length); 00180 00181 /* Queue the next sample */ 00182 if (length + 4 == len) 00183 { 00184 PullPin *pin = This->Parser.pInputPin; 00185 LONGLONG stop = BYTES_FROM_MEDIATIME(pin->rtStop); 00186 00187 hr = S_OK; 00188 memcpy(This->header, fbuf + length, 4); 00189 while (FAILED(hr = parse_header(This->header, &length, NULL))) 00190 { 00191 memmove(This->header, This->header+1, 3); 00192 if (pos + 4 >= stop) 00193 break; 00194 IAsyncReader_SyncRead(pin->pReader, ++pos, 1, This->header + 3); 00195 } 00196 pin->rtNext = MEDIATIME_FROM_BYTES(pos); 00197 00198 if (SUCCEEDED(hr)) 00199 { 00200 /* Remove 4 for the last header, which should hopefully work */ 00201 IMediaSample *sample = NULL; 00202 LONGLONG rtSampleStart = pin->rtNext - MEDIATIME_FROM_BYTES(4); 00203 LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(length + 4); 00204 00205 if (rtSampleStop > pin->rtStop) 00206 rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign)); 00207 00208 hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0); 00209 if (SUCCEEDED(hr)) 00210 { 00211 IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop); 00212 IMediaSample_SetPreroll(sample, 0); 00213 IMediaSample_SetDiscontinuity(sample, 0); 00214 IMediaSample_SetSyncPoint(sample, 1); 00215 pin->rtCurrent = rtSampleStart; 00216 pin->rtNext = rtSampleStop; 00217 hr = IAsyncReader_Request(pin->pReader, sample, 0); 00218 } 00219 if (FAILED(hr)) 00220 FIXME("o_Ox%08x\n", hr); 00221 } 00222 } 00223 /* If not, we're presumably at the end of file */ 00224 00225 TRACE("Media time : %u.%03u\n", (DWORD)(This->position/10000000), (DWORD)((This->position/10000)%1000)); 00226 00227 IMediaSample_SetTime(pCurrentSample, &time, &This->position); 00228 00229 hr = OutputPin_SendSample(&pOutputPin->pin, pCurrentSample); 00230 00231 if (hr != S_OK) 00232 { 00233 if (hr != S_FALSE) 00234 TRACE("Error sending sample (%x)\n", hr); 00235 else 00236 TRACE("S_FALSE (%d), holding\n", IMediaSample_GetActualDataLength(pCurrentSample)); 00237 } 00238 00239 return hr; 00240 } 00241 00242 00243 static HRESULT MPEGSplitter_process_sample(LPVOID iface, IMediaSample * pSample, DWORD_PTR cookie) 00244 { 00245 MPEGSplitterImpl *This = iface; 00246 BYTE *pbSrcStream; 00247 DWORD cbSrcStream = 0; 00248 REFERENCE_TIME tStart, tStop, tAviStart = This->position; 00249 HRESULT hr; 00250 00251 hr = IMediaSample_GetTime(pSample, &tStart, &tStop); 00252 if (SUCCEEDED(hr)) 00253 { 00254 cbSrcStream = IMediaSample_GetActualDataLength(pSample); 00255 hr = IMediaSample_GetPointer(pSample, &pbSrcStream); 00256 } 00257 00258 /* Flush occurring */ 00259 if (cbSrcStream == 0) 00260 { 00261 FIXME(".. Why do I need you?\n"); 00262 return S_OK; 00263 } 00264 00265 /* trace removed for performance reasons */ 00266 /* TRACE("(%p), %llu -> %llu\n", pSample, tStart, tStop); */ 00267 00268 /* Now, try to find a new header */ 00269 hr = FillBuffer(This, pSample); 00270 if (hr != S_OK) 00271 { 00272 WARN("Failed with hres: %08x!\n", hr); 00273 00274 /* Unset progression if denied! */ 00275 if (hr == VFW_E_WRONG_STATE || hr == S_FALSE) 00276 { 00277 memcpy(This->header, pbSrcStream, 4); 00278 This->Parser.pInputPin->rtCurrent = tStart; 00279 This->position = tAviStart; 00280 } 00281 } 00282 00283 if (BYTES_FROM_MEDIATIME(tStop) >= This->EndOfFile || This->position >= This->Parser.mediaSeeking.llStop) 00284 { 00285 unsigned int i; 00286 00287 TRACE("End of file reached\n"); 00288 00289 for (i = 0; i < This->Parser.cStreams; i++) 00290 { 00291 IPin* ppin; 00292 00293 hr = IPin_ConnectedTo(This->Parser.ppPins[i+1], &ppin); 00294 if (SUCCEEDED(hr)) 00295 { 00296 hr = IPin_EndOfStream(ppin); 00297 IPin_Release(ppin); 00298 } 00299 if (FAILED(hr)) 00300 WARN("Error sending EndOfStream to pin %u (%x)\n", i, hr); 00301 } 00302 00303 /* Force the pullpin thread to stop */ 00304 hr = S_FALSE; 00305 } 00306 00307 return hr; 00308 } 00309 00310 00311 static HRESULT MPEGSplitter_query_accept(LPVOID iface, const AM_MEDIA_TYPE *pmt) 00312 { 00313 if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream)) 00314 return S_FALSE; 00315 00316 if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1Audio)) 00317 return S_OK; 00318 00319 if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1Video)) 00320 FIXME("MPEG-1 video streams not yet supported.\n"); 00321 else if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1System)) 00322 FIXME("MPEG-1 system streams not yet supported.\n"); 00323 else if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1VideoCD)) 00324 FIXME("MPEG-1 VideoCD streams not yet supported.\n"); 00325 00326 return S_FALSE; 00327 } 00328 00329 00330 static HRESULT MPEGSplitter_init_audio(MPEGSplitterImpl *This, const BYTE *header, PIN_INFO *ppiOutput, AM_MEDIA_TYPE *pamt) 00331 { 00332 WAVEFORMATEX *format; 00333 int bitrate_index; 00334 int freq_index; 00335 int mode_ext; 00336 int emphasis; 00337 int lsf = 1; 00338 int mpeg1; 00339 int layer; 00340 int mode; 00341 00342 ZeroMemory(pamt, sizeof(*pamt)); 00343 ppiOutput->dir = PINDIR_OUTPUT; 00344 ppiOutput->pFilter = (IBaseFilter*)This; 00345 wsprintfW(ppiOutput->achName, wszAudioStream); 00346 00347 pamt->formattype = FORMAT_WaveFormatEx; 00348 pamt->majortype = MEDIATYPE_Audio; 00349 pamt->subtype = MEDIASUBTYPE_MPEG1AudioPayload; 00350 00351 pamt->lSampleSize = 0; 00352 pamt->bFixedSizeSamples = FALSE; 00353 pamt->bTemporalCompression = 0; 00354 00355 mpeg1 = (header[1]>>4)&0x1; 00356 if (mpeg1) 00357 lsf = ((header[1]>>3)&0x1)^1; 00358 00359 layer = 4-((header[1]>>1)&0x3); 00360 bitrate_index = ((header[2]>>4)&0xf); 00361 freq_index = ((header[2]>>2)&0x3) + (mpeg1?(lsf*3):6); 00362 mode = ((header[3]>>6)&0x3); 00363 mode_ext = ((header[3]>>4)&0x3); 00364 emphasis = ((header[3]>>0)&0x3); 00365 00366 if (!bitrate_index) 00367 { 00368 /* Set to highest bitrate so samples will fit in for sure */ 00369 FIXME("Variable-bitrate audio not fully supported.\n"); 00370 bitrate_index = 15; 00371 } 00372 00373 pamt->cbFormat = ((layer==3)? sizeof(MPEGLAYER3WAVEFORMAT) : 00374 sizeof(MPEG1WAVEFORMAT)); 00375 pamt->pbFormat = CoTaskMemAlloc(pamt->cbFormat); 00376 if (!pamt->pbFormat) 00377 return E_OUTOFMEMORY; 00378 ZeroMemory(pamt->pbFormat, pamt->cbFormat); 00379 format = (WAVEFORMATEX*)pamt->pbFormat; 00380 00381 format->wFormatTag = ((layer == 3) ? WAVE_FORMAT_MPEGLAYER3 : 00382 WAVE_FORMAT_MPEG); 00383 format->nChannels = ((mode == 3) ? 1 : 2); 00384 format->nSamplesPerSec = freqs[freq_index]; 00385 format->nAvgBytesPerSec = tabsel_123[lsf][layer-1][bitrate_index] * 1000 / 8; 00386 00387 if (layer == 3) 00388 format->nBlockAlign = format->nAvgBytesPerSec * 8 * 144 / 00389 (format->nSamplesPerSec<<lsf) + 1; 00390 else if (layer == 2) 00391 format->nBlockAlign = format->nAvgBytesPerSec * 8 * 144 / 00392 format->nSamplesPerSec + 1; 00393 else 00394 format->nBlockAlign = 4 * (format->nAvgBytesPerSec * 8 * 12 / format->nSamplesPerSec + 1); 00395 00396 format->wBitsPerSample = 0; 00397 00398 if (layer == 3) 00399 { 00400 MPEGLAYER3WAVEFORMAT *mp3format = (MPEGLAYER3WAVEFORMAT*)format; 00401 00402 format->cbSize = MPEGLAYER3_WFX_EXTRA_BYTES; 00403 00404 mp3format->wID = MPEGLAYER3_ID_MPEG; 00405 mp3format->fdwFlags = MPEGLAYER3_FLAG_PADDING_ON; 00406 mp3format->nBlockSize = format->nBlockAlign; 00407 mp3format->nFramesPerBlock = 1; 00408 00409 /* Beware the evil magic numbers. This struct is apparently horribly 00410 * under-documented, and the only references I could find had it being 00411 * set to this with no real explanation. It works fine though, so I'm 00412 * not complaining (yet). 00413 */ 00414 mp3format->nCodecDelay = 1393; 00415 } 00416 else 00417 { 00418 MPEG1WAVEFORMAT *mpgformat = (MPEG1WAVEFORMAT*)format; 00419 00420 format->cbSize = 22; 00421 00422 mpgformat->fwHeadLayer = ((layer == 1) ? ACM_MPEG_LAYER1 : 00423 ((layer == 2) ? ACM_MPEG_LAYER2 : 00424 ACM_MPEG_LAYER3)); 00425 mpgformat->dwHeadBitrate = format->nAvgBytesPerSec * 8; 00426 mpgformat->fwHeadMode = ((mode == 3) ? ACM_MPEG_SINGLECHANNEL : 00427 ((mode == 2) ? ACM_MPEG_DUALCHANNEL : 00428 ((mode == 1) ? ACM_MPEG_JOINTSTEREO : 00429 ACM_MPEG_STEREO))); 00430 mpgformat->fwHeadModeExt = ((mode == 1) ? 0x0F : (1<<mode_ext)); 00431 mpgformat->wHeadEmphasis = emphasis + 1; 00432 mpgformat->fwHeadFlags = ACM_MPEG_ID_MPEG1; 00433 } 00434 pamt->subtype.Data1 = format->wFormatTag; 00435 00436 TRACE("MPEG audio stream detected:\n" 00437 "\tLayer %d (%#x)\n" 00438 "\tFrequency: %d\n" 00439 "\tChannels: %d (%d)\n" 00440 "\tBytesPerSec: %d\n", 00441 layer, format->wFormatTag, format->nSamplesPerSec, 00442 format->nChannels, mode, format->nAvgBytesPerSec); 00443 00444 dump_AM_MEDIA_TYPE(pamt); 00445 00446 return S_OK; 00447 } 00448 00449 00450 static HRESULT MPEGSplitter_pre_connect(IPin *iface, IPin *pConnectPin, ALLOCATOR_PROPERTIES *props) 00451 { 00452 PullPin *pPin = (PullPin *)iface; 00453 MPEGSplitterImpl *This = (MPEGSplitterImpl*)pPin->pin.pinInfo.pFilter; 00454 HRESULT hr; 00455 LONGLONG pos = 0; /* in bytes */ 00456 BYTE header[10]; 00457 int streamtype = 0; 00458 LONGLONG total, avail; 00459 AM_MEDIA_TYPE amt; 00460 PIN_INFO piOutput; 00461 00462 IAsyncReader_Length(pPin->pReader, &total, &avail); 00463 This->EndOfFile = total; 00464 00465 hr = IAsyncReader_SyncRead(pPin->pReader, pos, 4, header); 00466 if (SUCCEEDED(hr)) 00467 pos += 4; 00468 00469 /* Skip ID3 v2 tag, if any */ 00470 if (SUCCEEDED(hr) && !memcmp("ID3", header, 3)) 00471 do { 00472 UINT length; 00473 hr = IAsyncReader_SyncRead(pPin->pReader, pos, 6, header + 4); 00474 if (FAILED(hr)) 00475 break; 00476 pos += 6; 00477 TRACE("Found ID3 v2.%d.%d\n", header[3], header[4]); 00478 length = (header[6] & 0x7F) << 21; 00479 length += (header[7] & 0x7F) << 14; 00480 length += (header[8] & 0x7F) << 7; 00481 length += (header[9] & 0x7F); 00482 TRACE("Length: %u\n", length); 00483 pos += length; 00484 00485 /* Read the real header for the mpeg splitter */ 00486 hr = IAsyncReader_SyncRead(pPin->pReader, pos, 4, header); 00487 if (SUCCEEDED(hr)) 00488 pos += 4; 00489 TRACE("%x:%x:%x:%x\n", header[0], header[1], header[2], header[3]); 00490 } while (0); 00491 00492 while(SUCCEEDED(hr) && !(streamtype=MPEGSplitter_head_check(header))) 00493 { 00494 TRACE("%x:%x:%x:%x\n", header[0], header[1], header[2], header[3]); 00495 /* No valid header yet; shift by a byte and check again */ 00496 memmove(header, header+1, 3); 00497 hr = IAsyncReader_SyncRead(pPin->pReader, pos++, 1, header + 3); 00498 } 00499 if (FAILED(hr)) 00500 return hr; 00501 pos -= 4; 00502 This->begin_offset = pos; 00503 memcpy(This->header, header, 4); 00504 00505 This->seektable[0].bytepos = pos; 00506 This->seektable[0].timepos = 0; 00507 00508 switch(streamtype) 00509 { 00510 case MPEG_AUDIO_HEADER: 00511 { 00512 LONGLONG duration = 0; 00513 DWORD last_entry = 0; 00514 00515 DWORD ticks = GetTickCount(); 00516 00517 hr = MPEGSplitter_init_audio(This, header, &piOutput, &amt); 00518 if (SUCCEEDED(hr)) 00519 { 00520 WAVEFORMATEX *format = (WAVEFORMATEX*)amt.pbFormat; 00521 00522 props->cbAlign = 1; 00523 props->cbPrefix = 0; 00524 /* Make the output buffer a multiple of the frame size */ 00525 props->cbBuffer = 0x4000 / format->nBlockAlign * 00526 format->nBlockAlign; 00527 props->cBuffers = 3; 00528 hr = Parser_AddPin(&(This->Parser), &piOutput, props, &amt); 00529 } 00530 00531 if (FAILED(hr)) 00532 { 00533 if (amt.pbFormat) 00534 CoTaskMemFree(amt.pbFormat); 00535 ERR("Could not create pin for MPEG audio stream (%x)\n", hr); 00536 break; 00537 } 00538 00539 /* Check for idv1 tag, and remove it from stream if found */ 00540 hr = IAsyncReader_SyncRead(pPin->pReader, This->EndOfFile-128, 3, header+4); 00541 if (FAILED(hr)) 00542 break; 00543 if (!strncmp((char*)header+4, "TAG", 3)) 00544 This->EndOfFile -= 128; 00545 This->Parser.pInputPin->rtStop = MEDIATIME_FROM_BYTES(This->EndOfFile); 00546 This->Parser.pInputPin->rtStart = This->Parser.pInputPin->rtCurrent = MEDIATIME_FROM_BYTES(This->begin_offset); 00547 00548 /* http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm has a whole read up on audio headers */ 00549 while (pos + 3 < This->EndOfFile) 00550 { 00551 LONGLONG length = 0; 00552 hr = IAsyncReader_SyncRead(pPin->pReader, pos, 4, header); 00553 if (hr != S_OK) 00554 break; 00555 while (parse_header(header, &length, &duration)) 00556 { 00557 /* No valid header yet; shift by a byte and check again */ 00558 memmove(header, header+1, 3); 00559 hr = IAsyncReader_SyncRead(pPin->pReader, pos++, 1, header + 3); 00560 if (hr != S_OK || This->EndOfFile - pos < 4) 00561 break; 00562 } 00563 pos += length; 00564 00565 if (This->seektable && (duration / SEEK_INTERVAL) > last_entry) 00566 { 00567 if (last_entry + 1 > duration / SEEK_INTERVAL) 00568 { 00569 ERR("Somehow skipped %d interval lengths instead of 1\n", (DWORD)(duration/SEEK_INTERVAL) - (last_entry + 1)); 00570 } 00571 ++last_entry; 00572 00573 TRACE("Entry: %u\n", last_entry); 00574 if (last_entry >= This->seek_entries) 00575 { 00576 This->seek_entries += 64; 00577 This->seektable = CoTaskMemRealloc(This->seektable, (This->seek_entries)*sizeof(struct seek_entry)); 00578 } 00579 This->seektable[last_entry].bytepos = pos; 00580 This->seektable[last_entry].timepos = duration; 00581 } 00582 00583 TRACE("Pos: %x%08x/%x%08x\n", (DWORD)(pos >> 32), (DWORD)pos, (DWORD)(This->EndOfFile>>32), (DWORD)This->EndOfFile); 00584 } 00585 hr = S_OK; 00586 TRACE("Duration: %d seconds\n", (DWORD)(duration / 10000000)); 00587 TRACE("Parsing took %u ms\n", GetTickCount() - ticks); 00588 This->duration = duration; 00589 00590 This->Parser.mediaSeeking.llCurrent = 0; 00591 This->Parser.mediaSeeking.llDuration = duration; 00592 This->Parser.mediaSeeking.llStop = duration; 00593 break; 00594 } 00595 case MPEG_VIDEO_HEADER: 00596 FIXME("MPEG video processing not yet supported!\n"); 00597 hr = E_FAIL; 00598 break; 00599 case MPEG_SYSTEM_HEADER: 00600 FIXME("MPEG system streams not yet supported!\n"); 00601 hr = E_FAIL; 00602 break; 00603 00604 default: 00605 break; 00606 } 00607 This->position = 0; 00608 00609 return hr; 00610 } 00611 00612 static HRESULT MPEGSplitter_cleanup(LPVOID iface) 00613 { 00614 MPEGSplitterImpl *This = iface; 00615 00616 TRACE("(%p)\n", This); 00617 00618 return S_OK; 00619 } 00620 00621 static HRESULT MPEGSplitter_seek(IBaseFilter *iface) 00622 { 00623 MPEGSplitterImpl *This = (MPEGSplitterImpl*)iface; 00624 PullPin *pPin = This->Parser.pInputPin; 00625 LONGLONG newpos, timepos, bytepos; 00626 HRESULT hr = S_OK; 00627 BYTE header[4]; 00628 00629 newpos = This->Parser.mediaSeeking.llCurrent; 00630 00631 if (newpos > This->duration) 00632 { 00633 WARN("Requesting position %x%08x beyond end of stream %x%08x\n", (DWORD)(newpos>>32), (DWORD)newpos, (DWORD)(This->duration>>32), (DWORD)This->duration); 00634 return E_INVALIDARG; 00635 } 00636 00637 if (This->position/1000000 == newpos/1000000) 00638 { 00639 TRACE("Requesting position %x%08x same as current position %x%08x\n", (DWORD)(newpos>>32), (DWORD)newpos, (DWORD)(This->position>>32), (DWORD)This->position); 00640 return S_OK; 00641 } 00642 00643 /* Position, cached */ 00644 bytepos = This->seektable[newpos / SEEK_INTERVAL].bytepos; 00645 timepos = This->seektable[newpos / SEEK_INTERVAL].timepos; 00646 00647 hr = IAsyncReader_SyncRead(pPin->pReader, bytepos, 4, header); 00648 while (bytepos + 3 < This->EndOfFile) 00649 { 00650 LONGLONG length = 0; 00651 hr = IAsyncReader_SyncRead(pPin->pReader, bytepos, 4, header); 00652 if (hr != S_OK || timepos >= newpos) 00653 break; 00654 00655 while (parse_header(header, &length, &timepos) && bytepos + 3 < This->EndOfFile) 00656 { 00657 /* No valid header yet; shift by a byte and check again */ 00658 memmove(header, header+1, 3); 00659 hr = IAsyncReader_SyncRead(pPin->pReader, ++bytepos, 1, header + 3); 00660 if (hr != S_OK) 00661 break; 00662 } 00663 bytepos += length; 00664 TRACE("Pos: %x%08x/%x%08x\n", (DWORD)(bytepos >> 32), (DWORD)bytepos, (DWORD)(This->EndOfFile>>32), (DWORD)This->EndOfFile); 00665 } 00666 00667 if (SUCCEEDED(hr)) 00668 { 00669 PullPin *pin = This->Parser.pInputPin; 00670 IPin *victim = NULL; 00671 00672 TRACE("Moving sound to %08u bytes!\n", (DWORD)bytepos); 00673 00674 EnterCriticalSection(&pin->thread_lock); 00675 IPin_BeginFlush((IPin *)pin); 00676 00677 /* Make sure this is done while stopped, BeginFlush takes care of this */ 00678 EnterCriticalSection(&This->Parser.csFilter); 00679 memcpy(This->header, header, 4); 00680 IPin_ConnectedTo(This->Parser.ppPins[1], &victim); 00681 if (victim) 00682 { 00683 IPin_NewSegment(victim, newpos, This->duration, pin->dRate); 00684 IPin_Release(victim); 00685 } 00686 00687 pin->rtStart = pin->rtCurrent = MEDIATIME_FROM_BYTES(bytepos); 00688 pin->rtStop = MEDIATIME_FROM_BYTES((REFERENCE_TIME)This->EndOfFile); 00689 This->seek = TRUE; 00690 This->position = newpos; 00691 LeaveCriticalSection(&This->Parser.csFilter); 00692 00693 TRACE("Done flushing\n"); 00694 IPin_EndFlush((IPin *)pin); 00695 LeaveCriticalSection(&pin->thread_lock); 00696 } 00697 return hr; 00698 } 00699 00700 static HRESULT MPEGSplitter_disconnect(LPVOID iface) 00701 { 00702 /* TODO: Find memory leaks etc */ 00703 return S_OK; 00704 } 00705 00706 static HRESULT MPEGSplitter_first_request(LPVOID iface) 00707 { 00708 MPEGSplitterImpl *This = iface; 00709 PullPin *pin = This->Parser.pInputPin; 00710 HRESULT hr; 00711 LONGLONG length; 00712 IMediaSample *sample; 00713 00714 TRACE("Seeking? %d\n", This->seek); 00715 assert(parse_header(This->header, &length, NULL) == S_OK); 00716 00717 if (pin->rtCurrent >= pin->rtStop) 00718 { 00719 /* Last sample has already been queued, request nothing more */ 00720 FIXME("Done!\n"); 00721 return S_OK; 00722 } 00723 00724 hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0); 00725 00726 pin->rtNext = pin->rtCurrent; 00727 if (SUCCEEDED(hr)) 00728 { 00729 LONGLONG rtSampleStart = pin->rtNext; 00730 /* Add 4 for the next header, which should hopefully work */ 00731 LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(length + 4); 00732 00733 if (rtSampleStop > pin->rtStop) 00734 rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign)); 00735 00736 hr = IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop); 00737 00738 pin->rtCurrent = pin->rtNext; 00739 pin->rtNext = rtSampleStop; 00740 00741 IMediaSample_SetPreroll(sample, FALSE); 00742 IMediaSample_SetDiscontinuity(sample, This->seek); 00743 IMediaSample_SetSyncPoint(sample, 1); 00744 This->seek = 0; 00745 00746 hr = IAsyncReader_Request(pin->pReader, sample, 0); 00747 } 00748 if (FAILED(hr)) 00749 ERR("Horsemen of the apocalypse came to bring error 0x%08x\n", hr); 00750 00751 return hr; 00752 } 00753 00754 static const IBaseFilterVtbl MPEGSplitter_Vtbl = 00755 { 00756 Parser_QueryInterface, 00757 Parser_AddRef, 00758 Parser_Release, 00759 Parser_GetClassID, 00760 Parser_Stop, 00761 Parser_Pause, 00762 Parser_Run, 00763 Parser_GetState, 00764 Parser_SetSyncSource, 00765 Parser_GetSyncSource, 00766 Parser_EnumPins, 00767 Parser_FindPin, 00768 Parser_QueryFilterInfo, 00769 Parser_JoinFilterGraph, 00770 Parser_QueryVendorInfo 00771 }; 00772 00773 HRESULT MPEGSplitter_create(IUnknown * pUnkOuter, LPVOID * ppv) 00774 { 00775 MPEGSplitterImpl *This; 00776 HRESULT hr = E_FAIL; 00777 00778 TRACE("(%p, %p)\n", pUnkOuter, ppv); 00779 00780 *ppv = NULL; 00781 00782 if (pUnkOuter) 00783 return CLASS_E_NOAGGREGATION; 00784 00785 This = CoTaskMemAlloc(sizeof(MPEGSplitterImpl)); 00786 if (!This) 00787 return E_OUTOFMEMORY; 00788 00789 ZeroMemory(This, sizeof(MPEGSplitterImpl)); 00790 This->seektable = CoTaskMemAlloc(sizeof(struct seek_entry) * 64); 00791 if (!This->seektable) 00792 { 00793 CoTaskMemFree(This); 00794 return E_OUTOFMEMORY; 00795 } 00796 This->seek_entries = 64; 00797 00798 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); 00799 if (FAILED(hr)) 00800 { 00801 CoTaskMemFree(This); 00802 return hr; 00803 } 00804 This->seek = 1; 00805 00806 /* Note: This memory is managed by the parser filter once created */ 00807 *ppv = This; 00808 00809 return hr; 00810 } Generated on Sun May 27 2012 04:21:57 for ReactOS by
1.7.6.1
|