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