push 6fe5edf8439c19d3885814583531c2f2b1495177
[wine/hacks.git] / dlls / quartz / mpegsplit.c
blob9af8973bba9b6b14f1ad659574826663ec7f9210
1 /*
2 * MPEG Splitter Filter
4 * Copyright 2003 Robert Shearman
5 * Copyright 2004-2005 Christian Costa
6 * Copyright 2007 Chris Robinson
7 * Copyright 2008 Maarten Lankhorst
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.
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.
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
24 #include <assert.h>
25 #include <math.h>
27 #include "quartz_private.h"
28 #include "control_private.h"
29 #include "pin.h"
31 #include "uuids.h"
32 #include "mmreg.h"
33 #include "mmsystem.h"
35 #include "winternl.h"
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
40 #include "parser.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
44 #define SEQUENCE_HEADER_CODE 0xB3
45 #define PACK_START_CODE 0xBA
47 #define SYSTEM_START_CODE 0xBB
48 #define AUDIO_ELEMENTARY_STREAM 0xC0
49 #define VIDEO_ELEMENTARY_STREAM 0xE0
51 #define MPEG_SYSTEM_HEADER 3
52 #define MPEG_VIDEO_HEADER 2
53 #define MPEG_AUDIO_HEADER 1
54 #define MPEG_NO_HEADER 0
56 #define SEEK_INTERVAL (ULONGLONG)(30 * 10000000) /* Add an entry every 30 seconds */
58 struct seek_entry {
59 ULONGLONG bytepos;
60 ULONGLONG timepos;
63 typedef struct MPEGSplitterImpl
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;
78 static int MPEGSplitter_head_check(const BYTE *header)
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)
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;
89 /* Check for a MPEG video sequence start code */
90 if (header[3] == SEQUENCE_HEADER_CODE)
91 return MPEG_VIDEO_HEADER;
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;
100 /* Nothing yet.. */
101 return MPEG_NO_HEADER;
104 static const WCHAR wszAudioStream[] = {'A','u','d','i','o',0};
105 static const WCHAR wszVideoStream[] = {'V','i','d','e','o',0};
107 static const DWORD freqs[10] = { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000, 0 };
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,} },
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,} }
120 static HRESULT parse_header(BYTE *header, LONGLONG *plen, LONGLONG *pduration)
122 LONGLONG duration = *pduration;
124 int bitrate_index, freq_index, mode_ext, emphasis, lsf = 1, mpeg1, layer, mode, padding, bitrate, length;
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))
130 FIXME("Not a valid header: %02x:%02x\n", header[0], header[1]);
131 return E_INVALIDARG;
134 mpeg1 = (header[1]>>4)&0x1;
135 if (mpeg1)
136 lsf = ((header[1]>>3)&0x1)^1;
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);
146 bitrate = tabsel_123[lsf][layer-1][bitrate_index] * 1000;
147 if (!bitrate || layer != 3)
149 FIXME("Not a valid header: %02x:%02x:%02x:%02x\n", header[0], header[1], header[2], header[3]);
150 return E_INVALIDARG;
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);
159 duration = (ULONGLONG)10000000 * (ULONGLONG)(length) / (ULONGLONG)(bitrate/8);
160 *plen = length;
161 *pduration += duration;
162 return S_OK;
166 static void skip_data(BYTE** from, DWORD *flen, DWORD amount)
168 *flen -= amount;
169 if (!*flen)
170 *from = NULL;
171 else
172 *from += amount;
175 static HRESULT copy_data(IMediaSample *to, BYTE** from, DWORD *flen, DWORD amount)
177 HRESULT hr = S_OK;
178 BYTE *ptr = NULL;
179 DWORD oldlength = IMediaSample_GetActualDataLength(to);
181 hr = IMediaSample_SetActualDataLength(to, oldlength + amount);
182 if (FAILED(hr))
184 if (!oldlength || oldlength <= 4)
185 WARN("Could not set require length\n");
186 return hr;
189 IMediaSample_GetPointer(to, &ptr);
190 memcpy(ptr + oldlength, *from, amount);
191 skip_data(from, flen, amount);
192 return hr;
195 static HRESULT FillBuffer(MPEGSplitterImpl *This, BYTE** fbuf, DWORD *flen, IMediaSample *pCurrentSample)
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;
204 TRACE("Source length: %u, skip length: %u, remaining: %u\n", *flen, This->skipbytes, This->remaining_bytes);
206 /* Case where bytes are skipped */
207 if (This->skipbytes)
209 DWORD skip = min(This->skipbytes, *flen);
210 skip_data(fbuf, flen, skip);
211 This->skipbytes -= skip;
212 return S_OK;
215 /* Case where there is already an output sample being held */
216 if (This->remaining_bytes)
218 DWORD towrite = min(This->remaining_bytes, *flen);
220 hr = copy_data(pCurrentSample, fbuf, flen, towrite);
221 if (FAILED(hr))
223 WARN("Could not resize sample: %08x\n", hr);
224 return hr;
227 This->remaining_bytes -= towrite;
228 if (This->remaining_bytes)
229 return hr;
231 /* Optimize: Try appending more samples to the stream */
232 goto out_append;
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)
239 BYTE *header = NULL;
240 DWORD attempts = 0;
242 /* Shoot anyone with a small sample! */
243 assert(*flen >= 6);
245 hr = IMediaSample_GetPointer(pCurrentSample, &header);
247 if (SUCCEEDED(hr))
248 hr = IMediaSample_SetActualDataLength(pCurrentSample, 7);
250 if (FAILED(hr))
252 WARN("Could not resize sample: %08x\n", hr);
253 return hr;
256 memcpy(header + dlen, *fbuf, 6 - dlen);
258 while (FAILED(parse_header(header+attempts, &length, &This->position)) && attempts < dlen)
260 attempts++;
263 /* No header found */
264 if (attempts == dlen)
266 hr = IMediaSample_SetActualDataLength(pCurrentSample, 0);
267 return hr;
270 IMediaSample_SetActualDataLength(pCurrentSample, 4);
271 IMediaSample_SetTime(pCurrentSample, &time, &This->position);
273 /* Move header back to beginning */
274 if (attempts)
275 memmove(header, header+attempts, 4);
277 This->remaining_bytes = length - 4;
278 *flen -= (4 - dlen + attempts);
279 *fbuf += (4 - dlen + attempts);
280 return hr;
283 /* Destination sample should contain no data! But the source sample should */
284 assert(!dlen);
285 assert(*flen);
287 /* Find the next valid header.. it <SHOULD> be right here */
288 while (*flen > 3 && FAILED(parse_header(*fbuf, &length, &This->position)))
290 skip_data(fbuf, flen, 1);
293 /* Uh oh, no header found! */
294 if (*flen < 4)
296 assert(!length);
297 hr = copy_data(pCurrentSample, fbuf, flen, *flen);
298 return hr;
301 IMediaSample_SetTime(pCurrentSample, &time, &This->position);
303 if (*flen < length)
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;
311 hr = copy_data(pCurrentSample, fbuf, flen, length);
312 if (FAILED(hr))
314 WARN("Couldn't set data size to %x%08x\n", (DWORD)(length >> 32), (DWORD)length);
315 This->skipbytes = length;
316 return hr;
319 out_append:
320 /* Optimize: Send multiple samples! */
321 while (extrasamples--)
323 if (*flen < 4)
324 break;
326 if (FAILED(parse_header(*fbuf, &length, &sampleduration)))
327 break;
329 if (length > *flen)
330 break;
332 if (FAILED(copy_data(pCurrentSample, fbuf, flen, length)))
333 break;
335 This->position += sampleduration;
336 sampleduration = 0;
337 IMediaSample_SetTime(pCurrentSample, &time, &This->position);
339 TRACE("Media time: %u.%03u\n", (DWORD)(This->position/10000000), (DWORD)((This->position/10000)%1000));
341 hr = OutputPin_SendSample(&pOutputPin->pin, pCurrentSample);
343 if (hr != S_OK)
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;
352 IMediaSample_Release(pCurrentSample);
353 This->pCurrentSample = NULL;
354 return hr;
358 static HRESULT MPEGSplitter_process_sample(LPVOID iface, IMediaSample * pSample)
360 MPEGSplitterImpl *This = (MPEGSplitterImpl*)iface;
361 BYTE *pbSrcStream;
362 DWORD cbSrcStream = 0;
363 REFERENCE_TIME tStart, tStop;
364 Parser_OutputPin * pOutputPin;
365 HRESULT hr;
367 pOutputPin = (Parser_OutputPin*)This->Parser.ppPins[1];
369 hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
370 if (SUCCEEDED(hr))
372 cbSrcStream = IMediaSample_GetActualDataLength(pSample);
373 hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
376 /* trace removed for performance reasons */
377 /* TRACE("(%p), %llu -> %llu\n", pSample, tStart, tStop); */
379 /* Try to get rid of current sample, if any */
380 if (This->pCurrentSample && !This->skipbytes && !This->remaining_bytes && IMediaSample_GetActualDataLength(This->pCurrentSample) > 4)
382 Parser_OutputPin * pOutputPin = (Parser_OutputPin*)This->Parser.ppPins[1];
383 IMediaSample *pCurrentSample = This->pCurrentSample;
384 HRESULT hr;
386 /* Unset advancement */
387 This->Parser.pInputPin->rtCurrent -= MEDIATIME_FROM_BYTES(cbSrcStream);
389 hr = OutputPin_SendSample(&pOutputPin->pin, pCurrentSample);
391 if (hr != S_OK)
392 return hr;
394 IMediaSample_Release(This->pCurrentSample);
395 This->pCurrentSample = NULL;
397 This->Parser.pInputPin->rtCurrent += MEDIATIME_FROM_BYTES(cbSrcStream);
400 /* Now, try to find a new header */
401 while (cbSrcStream > 0)
403 if (!This->pCurrentSample)
405 if (FAILED(hr = OutputPin_GetDeliveryBuffer(&pOutputPin->pin, &This->pCurrentSample, NULL, NULL, 0)))
407 TRACE("Failed with hres: %08x!\n", hr);
408 break;
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;
418 hr = FillBuffer(This, &pbSrcStream, &cbSrcStream, This->pCurrentSample);
419 if (hr == S_OK)
420 continue;
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;
429 This->Parser.pInputPin->rtCurrent = MEDIATIME_FROM_BYTES(BYTES_FROM_MEDIATIME(tStop) - cbSrcStream);
431 /* If set to S_FALSE we keep the sample, to transmit it next time */
432 if (hr != S_FALSE && This->pCurrentSample)
434 IMediaSample_SetActualDataLength(This->pCurrentSample, 0);
435 IMediaSample_Release(This->pCurrentSample);
436 This->pCurrentSample = NULL;
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;
445 if (BYTES_FROM_MEDIATIME(tStop) >= This->EndOfFile || This->position >= This->Parser.mediaSeeking.llStop)
447 int i;
449 TRACE("End of file reached\n");
451 for (i = 0; i < This->Parser.cStreams; i++)
453 IPin* ppin;
455 hr = IPin_ConnectedTo(This->Parser.ppPins[i+1], &ppin);
456 if (SUCCEEDED(hr))
458 hr = IPin_EndOfStream(ppin);
459 IPin_Release(ppin);
461 if (FAILED(hr))
462 WARN("Error sending EndOfStream to pin %d (%x)\n", i, hr);
465 /* Force the pullpin thread to stop */
466 hr = S_FALSE;
469 return hr;
473 static HRESULT MPEGSplitter_query_accept(LPVOID iface, const AM_MEDIA_TYPE *pmt)
475 if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream))
476 return S_FALSE;
478 if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_MPEG1Audio))
479 return S_OK;
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");
488 return S_FALSE;
492 static HRESULT MPEGSplitter_init_audio(MPEGSplitterImpl *This, const BYTE *header, PIN_INFO *ppiOutput, AM_MEDIA_TYPE *pamt)
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;
504 ZeroMemory(pamt, sizeof(*pamt));
505 ppiOutput->dir = PINDIR_OUTPUT;
506 ppiOutput->pFilter = (IBaseFilter*)This;
507 wsprintfW(ppiOutput->achName, wszAudioStream);
509 pamt->formattype = FORMAT_WaveFormatEx;
510 pamt->majortype = MEDIATYPE_Audio;
511 pamt->subtype = MEDIASUBTYPE_MPEG1AudioPayload;
513 pamt->lSampleSize = 0;
514 pamt->bFixedSizeSamples = FALSE;
515 pamt->bTemporalCompression = 0;
517 mpeg1 = (header[1]>>4)&0x1;
518 if (mpeg1)
519 lsf = ((header[1]>>3)&0x1)^1;
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);
528 if (!bitrate_index)
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;
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;
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;
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);
558 format->wBitsPerSample = 0;
560 if (layer == 3)
562 MPEGLAYER3WAVEFORMAT *mp3format = (MPEGLAYER3WAVEFORMAT*)format;
564 format->cbSize = MPEGLAYER3_WFX_EXTRA_BYTES;
566 mp3format->wID = MPEGLAYER3_ID_MPEG;
567 mp3format->fdwFlags = MPEGLAYER3_FLAG_PADDING_ON;
568 mp3format->nBlockSize = format->nBlockAlign;
569 mp3format->nFramesPerBlock = 1;
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).
576 mp3format->nCodecDelay = 1393;
578 else
580 MPEG1WAVEFORMAT *mpgformat = (MPEG1WAVEFORMAT*)format;
582 format->cbSize = 22;
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;
596 pamt->subtype.Data1 = format->wFormatTag;
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);
606 dump_AM_MEDIA_TYPE(pamt);
608 return S_OK;
612 static HRESULT MPEGSplitter_pre_connect(IPin *iface, IPin *pConnectPin)
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;
625 IAsyncReader_Length(pPin->pReader, &total, &avail);
626 This->EndOfFile = total;
628 hr = IAsyncReader_SyncRead(pPin->pReader, pos, 4, header);
629 if (SUCCEEDED(hr))
630 pos += 4;
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;
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);
655 while(SUCCEEDED(hr) && !(streamtype=MPEGSplitter_head_check(header)))
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);
662 if (FAILED(hr))
663 return hr;
664 pos -= 4;
665 This->header_bytes = pos;
666 This->skipbytes = 0;
668 This->seektable[0].bytepos = pos;
669 This->seektable[0].timepos = 0;
671 switch(streamtype)
673 case MPEG_AUDIO_HEADER:
675 LONGLONG duration = 0;
676 DWORD last_entry = 0;
678 DWORD ticks = GetTickCount();
680 hr = MPEGSplitter_init_audio(This, header, &piOutput, &amt);
681 if (SUCCEEDED(hr))
683 WAVEFORMATEX *format = (WAVEFORMATEX*)amt.pbFormat;
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);
694 if (FAILED(hr))
696 if (amt.pbFormat)
697 CoTaskMemFree(amt.pbFormat);
698 ERR("Could not create pin for MPEG audio stream (%x)\n", hr);
699 break;
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);
711 /* http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm has a whole read up on audio headers */
712 while (pos + 3 < This->EndOfFile)
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))
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;
726 pos += length;
728 if (This->seektable && (duration / SEEK_INTERVAL) > last_entry)
730 if (last_entry + 1 > duration / SEEK_INTERVAL)
732 ERR("Somehow skipped %d interval lengths instead of 1\n", (DWORD)(duration/SEEK_INTERVAL) - (last_entry + 1));
734 ++last_entry;
736 TRACE("Entry: %u\n", last_entry);
737 if (last_entry >= This->seek_entries)
739 This->seek_entries += 64;
740 This->seektable = CoTaskMemRealloc(This->seektable, (This->seek_entries)*sizeof(struct seek_entry));
742 This->seektable[last_entry].bytepos = pos;
743 This->seektable[last_entry].timepos = duration;
746 TRACE("Pos: %x%08x/%x%08x\n", (DWORD)(pos >> 32), (DWORD)pos, (DWORD)(This->EndOfFile>>32), (DWORD)This->EndOfFile);
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;
753 This->Parser.mediaSeeking.llCurrent = 0;
754 This->Parser.mediaSeeking.llDuration = duration;
755 This->Parser.mediaSeeking.llStop = duration;
756 break;
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;
767 default:
768 break;
770 This->remaining_bytes = 0;
771 This->position = 0;
773 return hr;
776 static HRESULT MPEGSplitter_cleanup(LPVOID iface)
778 MPEGSplitterImpl *This = (MPEGSplitterImpl*)iface;
780 TRACE("(%p) Deleting sample\n", This);
782 if (This->pCurrentSample)
783 IMediaSample_Release(This->pCurrentSample);
784 This->pCurrentSample = NULL;
786 This->remaining_bytes = This->skipbytes = 0;
787 return S_OK;
790 static HRESULT MPEGSplitter_seek(IBaseFilter *iface)
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];
798 newpos = This->Parser.mediaSeeking.llCurrent;
800 if (newpos > This->duration)
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;
806 if (This->position/1000000 == newpos/1000000)
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;
812 /* Position, cached */
813 bytepos = This->seektable[newpos / SEEK_INTERVAL].bytepos;
814 timepos = This->seektable[newpos / SEEK_INTERVAL].timepos;
816 hr = IAsyncReader_SyncRead(pPin->pReader, bytepos, 4, header);
817 while (timepos < newpos && bytepos + 3 < This->EndOfFile)
819 LONGLONG length = 0;
820 hr = IAsyncReader_SyncRead(pPin->pReader, bytepos, 4, header);
821 if (hr != S_OK)
822 break;
824 while (parse_header(header, &length, &timepos) && bytepos + 3 < This->EndOfFile)
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;
832 bytepos += length;
833 TRACE("Pos: %x%08x/%x%08x\n", (DWORD)(bytepos >> 32), (DWORD)bytepos, (DWORD)(This->EndOfFile>>32), (DWORD)This->EndOfFile);
836 if (SUCCEEDED(hr))
838 PullPin *pin = This->Parser.pInputPin;
839 IPin *victim = NULL;
841 TRACE("Moving sound to %08u bytes!\n", (DWORD)bytepos);
843 EnterCriticalSection(&pin->thread_lock);
844 IPin_BeginFlush((IPin *)pin);
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)
851 IPin_NewSegment(victim, newpos, This->duration, pin->dRate);
852 IPin_Release(victim);
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);
861 TRACE("Done flushing\n");
862 IPin_EndFlush((IPin *)pin);
863 LeaveCriticalSection(&pin->thread_lock);
865 return hr;
868 static HRESULT MPEGSplitter_destroy(LPVOID iface)
870 /* TODO: Find memory leaks etc */
871 return S_OK;
874 HRESULT MPEGSplitter_create(IUnknown * pUnkOuter, LPVOID * ppv)
876 MPEGSplitterImpl *This;
877 HRESULT hr = E_FAIL;
879 TRACE("(%p, %p)\n", pUnkOuter, ppv);
881 *ppv = NULL;
883 if (pUnkOuter)
884 return CLASS_E_NOAGGREGATION;
886 This = CoTaskMemAlloc(sizeof(MPEGSplitterImpl));
887 if (!This)
888 return E_OUTOFMEMORY;
890 ZeroMemory(This, sizeof(MPEGSplitterImpl));
891 This->seektable = CoTaskMemAlloc(sizeof(struct seek_entry) * 64);
892 if (!This->seektable)
894 CoTaskMemFree(This);
895 return E_OUTOFMEMORY;
897 This->seek_entries = 64;
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))
902 CoTaskMemFree(This);
903 return hr;
905 This->seek = TRUE;
907 /* Note: This memory is managed by the parser filter once created */
908 *ppv = (LPVOID)This;
910 return hr;