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