xref: /reactos/dll/directx/wine/quartz/mpegsplit.c (revision 37b2c145)
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