ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

mpegsplit.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.