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