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