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