[QUARTZ] Sync with Wine Staging 2.16. CORE-13762
[reactos.git] / reactos / dll / directx / wine / quartz / avisplit.c
1 /*
2 * AVI Splitter Filter
3 *
4 * Copyright 2003 Robert Shearman
5 * Copyright 2004-2005 Christian Costa
6 * Copyright 2008 Maarten Lankhorst
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22 /* FIXME:
23 * - Reference leaks, if they still exist
24 * - Files without an index are not handled correctly yet.
25 * - When stopping/starting, a sample is lost. This should be compensated by
26 * keeping track of previous index/position.
27 * - Debugging channels are noisy at the moment, especially with thread
28 * related messages, however this is the only correct thing to do right now,
29 * since wine doesn't correctly handle all messages yet.
30 */
31
32 #include "quartz_private.h"
33
34 #define TWOCCFromFOURCC(fcc) HIWORD(fcc)
35
36 /* four character codes used in AVI files */
37 #define ckidINFO mmioFOURCC('I','N','F','O')
38 #define ckidREC mmioFOURCC('R','E','C',' ')
39
40 typedef struct StreamData
41 {
42 DWORD dwSampleSize;
43 FLOAT fSamplesPerSec;
44 DWORD dwLength;
45
46 AVISTREAMHEADER streamheader;
47 DWORD entries;
48 AVISTDINDEX **stdindex;
49 DWORD frames;
50 BOOL seek;
51
52 /* Position, in index units */
53 DWORD pos, pos_next, index, index_next;
54
55 /* Packet handling: a thread is created and waits on the packet event handle
56 * On an event acquire the sample lock, addref the sample and set it to NULL,
57 * then queue a new packet.
58 */
59 HANDLE thread, packet_queued;
60 IMediaSample *sample;
61
62 /* Amount of preroll samples for this stream */
63 DWORD preroll;
64 } StreamData;
65
66 typedef struct AVISplitterImpl
67 {
68 ParserImpl Parser;
69 RIFFCHUNK CurrentChunk;
70 LONGLONG CurrentChunkOffset; /* in media time */
71 LONGLONG EndOfFile;
72 AVIMAINHEADER AviHeader;
73 AVIEXTHEADER ExtHeader;
74
75 AVIOLDINDEX *oldindex;
76 DWORD offset;
77
78 StreamData *streams;
79 } AVISplitterImpl;
80
81 struct thread_args {
82 AVISplitterImpl *This;
83 DWORD stream;
84 };
85
86 static inline AVISplitterImpl *impl_from_IMediaSeeking( IMediaSeeking *iface )
87 {
88 return CONTAINING_RECORD(iface, AVISplitterImpl, Parser.sourceSeeking.IMediaSeeking_iface);
89 }
90
91 /* The threading stuff cries for an explanation
92 *
93 * PullPin starts processing and calls AVISplitter_first_request
94 * AVISplitter_first_request creates a thread for each stream
95 * A stream can be audio, video, subtitles or something undefined.
96 *
97 * AVISplitter_first_request loads a single packet to each but one stream,
98 * and queues it for that last stream. This is to prevent WaitForNext to time
99 * out badly.
100 *
101 * The processing loop is entered. It calls IAsyncReader_WaitForNext in the
102 * PullPin. Every time it receives a packet, it will call AVISplitter_Sample
103 * AVISplitter_Sample will signal the relevant thread that a new sample is
104 * arrived, when that thread is ready it will read the packet and transmits
105 * it downstream with AVISplitter_Receive
106 *
107 * Threads terminate upon receiving NULL as packet or when ANY error code
108 * != S_OK occurs. This means that any error is fatal to processing.
109 */
110
111 static HRESULT AVISplitter_SendEndOfFile(AVISplitterImpl *This, DWORD streamnumber)
112 {
113 IPin* ppin = NULL;
114 HRESULT hr;
115
116 TRACE("End of file reached\n");
117
118 hr = IPin_ConnectedTo(This->Parser.ppPins[streamnumber+1], &ppin);
119 if (SUCCEEDED(hr))
120 {
121 hr = IPin_EndOfStream(ppin);
122 IPin_Release(ppin);
123 }
124 TRACE("--> %x\n", hr);
125
126 /* Force the pullpin thread to stop */
127 return S_FALSE;
128 }
129
130 /* Thread worker horse */
131 static HRESULT AVISplitter_next_request(AVISplitterImpl *This, DWORD streamnumber)
132 {
133 StreamData *stream = This->streams + streamnumber;
134 PullPin *pin = This->Parser.pInputPin;
135 IMediaSample *sample = NULL;
136 HRESULT hr;
137 ULONG ref;
138
139 TRACE("(%p, %u)->()\n", This, streamnumber);
140
141 hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0);
142 if (hr != S_OK)
143 ERR("... %08x?\n", hr);
144
145 if (SUCCEEDED(hr))
146 {
147 LONGLONG rtSampleStart;
148 /* Add 4 for the next header, which should hopefully work */
149 LONGLONG rtSampleStop;
150
151 stream->pos = stream->pos_next;
152 stream->index = stream->index_next;
153
154 IMediaSample_SetDiscontinuity(sample, stream->seek);
155 stream->seek = FALSE;
156 if (stream->preroll)
157 {
158 --stream->preroll;
159 IMediaSample_SetPreroll(sample, TRUE);
160 }
161 else
162 IMediaSample_SetPreroll(sample, FALSE);
163 IMediaSample_SetSyncPoint(sample, TRUE);
164
165 if (stream->stdindex)
166 {
167 AVISTDINDEX *index = stream->stdindex[stream->index];
168 AVISTDINDEX_ENTRY *entry = &index->aIndex[stream->pos];
169
170 /* End of file */
171 if (stream->index >= stream->entries)
172 {
173 TRACE("END OF STREAM ON %u\n", streamnumber);
174 IMediaSample_Release(sample);
175 return S_FALSE;
176 }
177
178 rtSampleStart = index->qwBaseOffset;
179 rtSampleStart += entry->dwOffset;
180 rtSampleStart = MEDIATIME_FROM_BYTES(rtSampleStart);
181
182 ++stream->pos_next;
183 if (index->nEntriesInUse == stream->pos_next)
184 {
185 stream->pos_next = 0;
186 ++stream->index_next;
187 }
188
189 rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(entry->dwSize & ~(1u << 31));
190
191 TRACE("offset(%u) size(%u)\n", (DWORD)BYTES_FROM_MEDIATIME(rtSampleStart), (DWORD)BYTES_FROM_MEDIATIME(rtSampleStop - rtSampleStart));
192 }
193 else if (This->oldindex)
194 {
195 DWORD flags = This->oldindex->aIndex[stream->pos].dwFlags;
196 DWORD size = This->oldindex->aIndex[stream->pos].dwSize;
197
198 /* End of file */
199 if (stream->index)
200 {
201 TRACE("END OF STREAM ON %u\n", streamnumber);
202 IMediaSample_Release(sample);
203 return S_FALSE;
204 }
205
206 rtSampleStart = MEDIATIME_FROM_BYTES(This->offset);
207 rtSampleStart += MEDIATIME_FROM_BYTES(This->oldindex->aIndex[stream->pos].dwOffset);
208 rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(size);
209 if (flags & AVIIF_MIDPART)
210 {
211 FIXME("Only stand alone frames are currently handled correctly!\n");
212 }
213 if (flags & AVIIF_LIST)
214 {
215 FIXME("Not sure if this is handled correctly\n");
216 rtSampleStart += MEDIATIME_FROM_BYTES(sizeof(RIFFLIST));
217 rtSampleStop += MEDIATIME_FROM_BYTES(sizeof(RIFFLIST));
218 }
219 else
220 {
221 rtSampleStart += MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK));
222 rtSampleStop += MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK));
223 }
224
225 /* Slow way of finding next index */
226 do {
227 stream->pos_next++;
228 } while (stream->pos_next * sizeof(This->oldindex->aIndex[0]) < This->oldindex->cb
229 && StreamFromFOURCC(This->oldindex->aIndex[stream->pos_next].dwChunkId) != streamnumber);
230
231 /* End of file soon */
232 if (stream->pos_next * sizeof(This->oldindex->aIndex[0]) >= This->oldindex->cb)
233 {
234 stream->pos_next = 0;
235 ++stream->index_next;
236 }
237 }
238 else /* TODO: Generate an index automagically */
239 {
240 ERR("CAN'T PLAY WITHOUT AN INDEX! SOS! SOS! SOS!\n");
241 assert(0);
242 }
243
244 if (rtSampleStart != rtSampleStop)
245 {
246 IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop);
247 hr = IAsyncReader_Request(pin->pReader, sample, streamnumber);
248
249 if (FAILED(hr))
250 {
251 ref = IMediaSample_Release(sample);
252 assert(ref == 0);
253 }
254 }
255 else
256 {
257 stream->sample = sample;
258 IMediaSample_SetActualDataLength(sample, 0);
259 SetEvent(stream->packet_queued);
260 }
261 }
262 else
263 {
264 if (sample)
265 {
266 ERR("There should be no sample!\n");
267 ref = IMediaSample_Release(sample);
268 assert(ref == 0);
269 }
270 }
271 TRACE("--> %08x\n", hr);
272
273 return hr;
274 }
275
276 static HRESULT AVISplitter_Receive(AVISplitterImpl *This, IMediaSample *sample, DWORD streamnumber)
277 {
278 Parser_OutputPin *pin = unsafe_impl_Parser_OutputPin_from_IPin(This->Parser.ppPins[1+streamnumber]);
279 HRESULT hr;
280 LONGLONG start, stop, rtstart, rtstop;
281 StreamData *stream = &This->streams[streamnumber];
282
283 start = pin->dwSamplesProcessed;
284 start *= stream->streamheader.dwScale;
285 start *= 10000000;
286 start /= stream->streamheader.dwRate;
287
288 if (stream->streamheader.dwSampleSize)
289 {
290 ULONG len = IMediaSample_GetActualDataLength(sample);
291 ULONG size = stream->streamheader.dwSampleSize;
292
293 pin->dwSamplesProcessed += len / size;
294 }
295 else
296 ++pin->dwSamplesProcessed;
297
298 stop = pin->dwSamplesProcessed;
299 stop *= stream->streamheader.dwScale;
300 stop *= 10000000;
301 stop /= stream->streamheader.dwRate;
302
303 if (IMediaSample_IsDiscontinuity(sample) == S_OK) {
304 IPin *victim;
305 EnterCriticalSection(&This->Parser.filter.csFilter);
306 pin->pin.pin.tStart = start;
307 pin->pin.pin.dRate = This->Parser.sourceSeeking.dRate;
308 hr = IPin_ConnectedTo(&pin->pin.pin.IPin_iface, &victim);
309 if (hr == S_OK)
310 {
311 hr = IPin_NewSegment(victim, start, This->Parser.sourceSeeking.llStop,
312 This->Parser.sourceSeeking.dRate);
313 if (hr != S_OK)
314 FIXME("NewSegment returns %08x\n", hr);
315 IPin_Release(victim);
316 }
317 LeaveCriticalSection(&This->Parser.filter.csFilter);
318 if (hr != S_OK)
319 return hr;
320 }
321 rtstart = (double)(start - pin->pin.pin.tStart) / pin->pin.pin.dRate;
322 rtstop = (double)(stop - pin->pin.pin.tStart) / pin->pin.pin.dRate;
323 IMediaSample_SetMediaTime(sample, &start, &stop);
324 IMediaSample_SetTime(sample, &rtstart, &rtstop);
325 IMediaSample_SetMediaTime(sample, &start, &stop);
326
327 hr = BaseOutputPinImpl_Deliver(&pin->pin, sample);
328
329 /* Uncomment this if you want to debug the time differences between the
330 * different streams, it is useful for that
331 *
332 FIXME("stream %u, hr: %08x, Start: %u.%03u, Stop: %u.%03u\n", streamnumber, hr,
333 (DWORD)(start / 10000000), (DWORD)((start / 10000)%1000),
334 (DWORD)(stop / 10000000), (DWORD)((stop / 10000)%1000));
335 */
336 return hr;
337 }
338
339 static DWORD WINAPI AVISplitter_thread_reader(LPVOID data)
340 {
341 struct thread_args *args = data;
342 AVISplitterImpl *This = args->This;
343 DWORD streamnumber = args->stream;
344 HRESULT hr = S_OK;
345
346 do
347 {
348 HRESULT nexthr = S_FALSE;
349 IMediaSample *sample;
350
351 WaitForSingleObject(This->streams[streamnumber].packet_queued, INFINITE);
352 sample = This->streams[streamnumber].sample;
353 This->streams[streamnumber].sample = NULL;
354 if (!sample)
355 break;
356
357 nexthr = AVISplitter_next_request(This, streamnumber);
358
359 hr = AVISplitter_Receive(This, sample, streamnumber);
360 if (hr != S_OK)
361 FIXME("Receiving error: %08x\n", hr);
362
363 IMediaSample_Release(sample);
364 if (hr == S_OK)
365 hr = nexthr;
366 if (nexthr == S_FALSE)
367 AVISplitter_SendEndOfFile(This, streamnumber);
368 } while (hr == S_OK);
369
370 if (hr != S_FALSE)
371 FIXME("Thread %u terminated with hr %08x!\n", streamnumber, hr);
372 else
373 TRACE("Thread %u terminated properly\n", streamnumber);
374 return hr;
375 }
376
377 static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample, DWORD_PTR cookie)
378 {
379 AVISplitterImpl *This = iface;
380 StreamData *stream = This->streams + cookie;
381 HRESULT hr = S_OK;
382
383 if (!IMediaSample_GetActualDataLength(pSample))
384 {
385 ERR("Received empty sample\n");
386 return S_OK;
387 }
388
389 /* Send the sample to whatever thread is appropriate
390 * That thread should also not have a sample queued at the moment
391 */
392 /* Debugging */
393 TRACE("(%p)->(%p size: %u, %lu)\n", This, pSample, IMediaSample_GetActualDataLength(pSample), cookie);
394 assert(cookie < This->Parser.cStreams);
395 assert(!stream->sample);
396 assert(WaitForSingleObject(stream->packet_queued, 0) == WAIT_TIMEOUT);
397
398 IMediaSample_AddRef(pSample);
399
400 stream->sample = pSample;
401 SetEvent(stream->packet_queued);
402
403 return hr;
404 }
405
406 static HRESULT AVISplitter_done_process(LPVOID iface);
407
408 /* On the first request we have to be sure that (cStreams-1) samples have
409 * already been processed, because otherwise some pins might not ever finish
410 * a Pause state change
411 */
412 static HRESULT AVISplitter_first_request(LPVOID iface)
413 {
414 AVISplitterImpl *This = iface;
415 HRESULT hr = S_OK;
416 DWORD x;
417 IMediaSample *sample = NULL;
418 BOOL have_sample = FALSE;
419
420 TRACE("(%p)->()\n", This);
421
422 for (x = 0; x < This->Parser.cStreams; ++x)
423 {
424 StreamData *stream = This->streams + x;
425
426 /* Nothing should be running at this point */
427 assert(!stream->thread);
428
429 assert(!sample);
430 /* It could be we asked the thread to terminate, and the thread
431 * already terminated before receiving the deathwish */
432 ResetEvent(stream->packet_queued);
433
434 stream->pos_next = stream->pos;
435 stream->index_next = stream->index;
436
437 /* This was sent after stopped->paused or stopped->playing, so set seek */
438 stream->seek = TRUE;
439
440 /* There should be a packet queued from AVISplitter_next_request last time
441 * It needs to be done now because this is the only way to ensure that every
442 * stream will have at least 1 packet processed
443 * If this is done after the threads start it could go all awkward and we
444 * would have no guarantees that it's successful at all
445 */
446
447 if (have_sample)
448 {
449 DWORD_PTR dwUser = ~0;
450 hr = IAsyncReader_WaitForNext(This->Parser.pInputPin->pReader, 10000, &sample, &dwUser);
451 assert(hr == S_OK);
452 assert(sample);
453
454 AVISplitter_Sample(iface, sample, dwUser);
455 IMediaSample_Release(sample);
456 }
457
458 hr = AVISplitter_next_request(This, x);
459 TRACE("-->%08x\n", hr);
460
461 /* Could be an EOF instead */
462 have_sample = (hr == S_OK);
463 if (hr == S_FALSE)
464 AVISplitter_SendEndOfFile(This, x);
465
466 if (FAILED(hr) && hr != VFW_E_NOT_CONNECTED)
467 break;
468 hr = S_OK;
469 }
470
471 /* FIXME: Don't do this for each pin that sent an EOF */
472 for (x = 0; x < This->Parser.cStreams && SUCCEEDED(hr); ++x)
473 {
474 struct thread_args *args;
475 DWORD tid;
476
477 if ((This->streams[x].stdindex && This->streams[x].index_next >= This->streams[x].entries) ||
478 (!This->streams[x].stdindex && This->streams[x].index_next))
479 {
480 This->streams[x].thread = NULL;
481 continue;
482 }
483
484 args = CoTaskMemAlloc(sizeof(*args));
485 args->This = This;
486 args->stream = x;
487 This->streams[x].thread = CreateThread(NULL, 0, AVISplitter_thread_reader, args, 0, &tid);
488 TRACE("Created stream %u thread 0x%08x\n", x, tid);
489 }
490
491 if (FAILED(hr))
492 ERR("Horsemen of the apocalypse came to bring error 0x%08x\n", hr);
493
494 return hr;
495 }
496
497 static HRESULT AVISplitter_done_process(LPVOID iface)
498 {
499 AVISplitterImpl *This = iface;
500 DWORD x;
501 ULONG ref;
502
503 for (x = 0; x < This->Parser.cStreams; ++x)
504 {
505 StreamData *stream = This->streams + x;
506
507 TRACE("Waiting for %u to terminate\n", x);
508 /* Make the thread return first */
509 SetEvent(stream->packet_queued);
510 assert(WaitForSingleObject(stream->thread, 100000) != WAIT_TIMEOUT);
511 CloseHandle(stream->thread);
512 stream->thread = NULL;
513
514 if (stream->sample)
515 {
516 ref = IMediaSample_Release(stream->sample);
517 assert(ref == 0);
518 }
519 stream->sample = NULL;
520
521 ResetEvent(stream->packet_queued);
522 }
523 TRACE("All threads are now terminated\n");
524
525 return S_OK;
526 }
527
528 static HRESULT AVISplitter_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
529 {
530 if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream) && IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_Avi))
531 return S_OK;
532 return S_FALSE;
533 }
534
535 static HRESULT AVISplitter_ProcessIndex(AVISplitterImpl *This, AVISTDINDEX **index, LONGLONG qwOffset, DWORD cb)
536 {
537 AVISTDINDEX *pIndex;
538 DWORD x;
539 int rest;
540
541 *index = NULL;
542 if (cb < sizeof(AVISTDINDEX))
543 {
544 FIXME("size %u too small\n", cb);
545 return E_INVALIDARG;
546 }
547
548 pIndex = CoTaskMemAlloc(cb);
549 if (!pIndex)
550 return E_OUTOFMEMORY;
551
552 IAsyncReader_SyncRead((impl_PullPin_from_IPin(This->Parser.ppPins[0]))->pReader, qwOffset, cb, (BYTE *)pIndex);
553 rest = cb - sizeof(AVISUPERINDEX) + sizeof(RIFFCHUNK) + sizeof(pIndex->aIndex);
554
555 TRACE("FOURCC: %s\n", debugstr_an((char *)&pIndex->fcc, 4));
556 TRACE("wLongsPerEntry: %hd\n", pIndex->wLongsPerEntry);
557 TRACE("bIndexSubType: %u\n", pIndex->bIndexSubType);
558 TRACE("bIndexType: %u\n", pIndex->bIndexType);
559 TRACE("nEntriesInUse: %u\n", pIndex->nEntriesInUse);
560 TRACE("dwChunkId: %.4s\n", (char *)&pIndex->dwChunkId);
561 TRACE("qwBaseOffset: %s\n", wine_dbgstr_longlong(pIndex->qwBaseOffset));
562 TRACE("dwReserved_3: %u\n", pIndex->dwReserved_3);
563
564 if (pIndex->bIndexType != AVI_INDEX_OF_CHUNKS
565 || pIndex->wLongsPerEntry != 2
566 || rest < (pIndex->nEntriesInUse * sizeof(DWORD) * pIndex->wLongsPerEntry)
567 || (pIndex->bIndexSubType != AVI_INDEX_SUB_DEFAULT))
568 {
569 FIXME("Invalid index chunk encountered: %u/%u, %u/%u, %u/%u, %u/%u\n",
570 pIndex->bIndexType, AVI_INDEX_OF_CHUNKS, pIndex->wLongsPerEntry, 2,
571 rest, (DWORD)(pIndex->nEntriesInUse * sizeof(DWORD) * pIndex->wLongsPerEntry),
572 pIndex->bIndexSubType, AVI_INDEX_SUB_DEFAULT);
573 *index = NULL;
574 return E_INVALIDARG;
575 }
576
577 for (x = 0; x < pIndex->nEntriesInUse; ++x)
578 {
579 BOOL keyframe = !(pIndex->aIndex[x].dwSize >> 31);
580 DWORDLONG offset = pIndex->qwBaseOffset + pIndex->aIndex[x].dwOffset;
581 TRACE("dwOffset: %s\n", wine_dbgstr_longlong(offset));
582 TRACE("dwSize: %u\n", (pIndex->aIndex[x].dwSize & ~(1u << 31)));
583 TRACE("Frame is a keyframe: %s\n", keyframe ? "yes" : "no");
584 }
585
586 *index = pIndex;
587 return S_OK;
588 }
589
590 static HRESULT AVISplitter_ProcessOldIndex(AVISplitterImpl *This)
591 {
592 ULONGLONG mov_pos = BYTES_FROM_MEDIATIME(This->CurrentChunkOffset) - sizeof(DWORD);
593 AVIOLDINDEX *pAviOldIndex = This->oldindex;
594 int relative = -1;
595 DWORD x;
596
597 for (x = 0; x < pAviOldIndex->cb / sizeof(pAviOldIndex->aIndex[0]); ++x)
598 {
599 DWORD temp, temp2 = 0, offset, chunkid;
600 PullPin *pin = This->Parser.pInputPin;
601
602 offset = pAviOldIndex->aIndex[x].dwOffset;
603 chunkid = pAviOldIndex->aIndex[x].dwChunkId;
604
605 TRACE("dwChunkId: %.4s\n", (char *)&chunkid);
606 TRACE("dwFlags: %08x\n", pAviOldIndex->aIndex[x].dwFlags);
607 TRACE("dwOffset (%s): %08x\n", relative ? "relative" : "absolute", offset);
608 TRACE("dwSize: %08x\n", pAviOldIndex->aIndex[x].dwSize);
609
610 /* Only scan once, or else this will take too long */
611 if (relative == -1)
612 {
613 IAsyncReader_SyncRead(pin->pReader, offset, sizeof(DWORD), (BYTE *)&temp);
614 relative = (chunkid != temp);
615
616 if (chunkid == mmioFOURCC('7','F','x','x')
617 && ((char *)&temp)[0] == 'i' && ((char *)&temp)[1] == 'x')
618 relative = FALSE;
619
620 if (relative)
621 {
622 if (offset + mov_pos < BYTES_FROM_MEDIATIME(This->EndOfFile))
623 IAsyncReader_SyncRead(pin->pReader, offset + mov_pos, sizeof(DWORD), (BYTE *)&temp2);
624
625 if (chunkid == mmioFOURCC('7','F','x','x')
626 && ((char *)&temp2)[0] == 'i' && ((char *)&temp2)[1] == 'x')
627 {
628 /* Do nothing, all is great */
629 }
630 else if (temp2 != chunkid)
631 {
632 ERR("Faulty index or bug in handling: Wanted FCC: %s, Abs FCC: %s (@ %x), Rel FCC: %s (@ %s)\n",
633 debugstr_an((char *)&chunkid, 4), debugstr_an((char *)&temp, 4), offset,
634 debugstr_an((char *)&temp2, 4), wine_dbgstr_longlong(mov_pos + offset));
635 relative = -1;
636 }
637 else
638 TRACE("Scanned dwChunkId: %s\n", debugstr_an((char *)&temp2, 4));
639 }
640 else if (!relative)
641 TRACE("Scanned dwChunkId: %s\n", debugstr_an((char *)&temp, 4));
642 }
643 /* Only dump one packet */
644 else break;
645 }
646
647 if (relative == -1)
648 {
649 FIXME("Dropping index: no idea whether it is relative or absolute\n");
650 CoTaskMemFree(This->oldindex);
651 This->oldindex = NULL;
652 }
653 else if (!relative)
654 This->offset = 0;
655 else
656 This->offset = (DWORD)mov_pos;
657
658 return S_OK;
659 }
660
661 static HRESULT AVISplitter_ProcessStreamList(AVISplitterImpl * This, const BYTE * pData, DWORD cb, ALLOCATOR_PROPERTIES *props)
662 {
663 PIN_INFO piOutput;
664 const RIFFCHUNK * pChunk;
665 HRESULT hr;
666 AM_MEDIA_TYPE amt;
667 float fSamplesPerSec = 0.0f;
668 DWORD dwSampleSize = 0;
669 DWORD dwLength = 0;
670 DWORD nstdindex = 0;
671 static const WCHAR wszStreamTemplate[] = {'S','t','r','e','a','m',' ','%','0','2','d',0};
672 StreamData *stream;
673
674 ZeroMemory(&amt, sizeof(amt));
675 piOutput.dir = PINDIR_OUTPUT;
676 piOutput.pFilter = &This->Parser.filter.IBaseFilter_iface;
677 wsprintfW(piOutput.achName, wszStreamTemplate, This->Parser.cStreams);
678 This->streams = CoTaskMemRealloc(This->streams, sizeof(StreamData) * (This->Parser.cStreams+1));
679 stream = This->streams + This->Parser.cStreams;
680 ZeroMemory(stream, sizeof(*stream));
681
682 for (pChunk = (const RIFFCHUNK *)pData;
683 ((const BYTE *)pChunk >= pData) && ((const BYTE *)pChunk + sizeof(RIFFCHUNK) < pData + cb) && (pChunk->cb > 0);
684 pChunk = (const RIFFCHUNK *)((const BYTE*)pChunk + sizeof(RIFFCHUNK) + pChunk->cb)
685 )
686 {
687 switch (pChunk->fcc)
688 {
689 case ckidSTREAMHEADER:
690 {
691 const AVISTREAMHEADER * pStrHdr = (const AVISTREAMHEADER *)pChunk;
692 TRACE("processing stream header\n");
693 stream->streamheader = *pStrHdr;
694
695 fSamplesPerSec = (float)pStrHdr->dwRate / (float)pStrHdr->dwScale;
696 CoTaskMemFree(amt.pbFormat);
697 amt.pbFormat = NULL;
698 amt.cbFormat = 0;
699
700 switch (pStrHdr->fccType)
701 {
702 case streamtypeVIDEO:
703 amt.formattype = FORMAT_VideoInfo;
704 break;
705 case streamtypeAUDIO:
706 amt.formattype = FORMAT_WaveFormatEx;
707 break;
708 default:
709 FIXME("fccType %.4s not handled yet\n", (const char *)&pStrHdr->fccType);
710 amt.formattype = FORMAT_None;
711 }
712 amt.majortype = MEDIATYPE_Video;
713 amt.majortype.Data1 = pStrHdr->fccType;
714 amt.subtype = MEDIATYPE_Video;
715 amt.subtype.Data1 = pStrHdr->fccHandler;
716 TRACE("Subtype FCC: %.04s\n", (LPCSTR)&pStrHdr->fccHandler);
717 amt.lSampleSize = pStrHdr->dwSampleSize;
718 amt.bFixedSizeSamples = (amt.lSampleSize != 0);
719
720 /* FIXME: Is this right? */
721 if (!amt.lSampleSize)
722 {
723 amt.lSampleSize = 1;
724 dwSampleSize = 1;
725 }
726
727 amt.bTemporalCompression = IsEqualGUID(&amt.majortype, &MEDIATYPE_Video); /* FIXME? */
728 dwSampleSize = pStrHdr->dwSampleSize;
729 dwLength = pStrHdr->dwLength;
730 if (!dwLength)
731 dwLength = This->AviHeader.dwTotalFrames;
732
733 if (pStrHdr->dwSuggestedBufferSize && pStrHdr->dwSuggestedBufferSize > props->cbBuffer)
734 props->cbBuffer = pStrHdr->dwSuggestedBufferSize;
735
736 break;
737 }
738 case ckidSTREAMFORMAT:
739 TRACE("processing stream format data\n");
740 if (IsEqualIID(&amt.formattype, &FORMAT_VideoInfo))
741 {
742 VIDEOINFOHEADER * pvi;
743 /* biCompression member appears to override the value in the stream header.
744 * i.e. the stream header can say something completely contradictory to what
745 * is in the BITMAPINFOHEADER! */
746 if (pChunk->cb < sizeof(BITMAPINFOHEADER))
747 {
748 ERR("Not enough bytes for BITMAPINFOHEADER\n");
749 return E_FAIL;
750 }
751 amt.cbFormat = sizeof(VIDEOINFOHEADER) - sizeof(BITMAPINFOHEADER) + pChunk->cb;
752 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
753 ZeroMemory(amt.pbFormat, amt.cbFormat);
754 pvi = (VIDEOINFOHEADER *)amt.pbFormat;
755 pvi->AvgTimePerFrame = (LONGLONG)(10000000.0 / fSamplesPerSec);
756
757 CopyMemory(&pvi->bmiHeader, pChunk + 1, pChunk->cb);
758 if (pvi->bmiHeader.biCompression)
759 amt.subtype.Data1 = pvi->bmiHeader.biCompression;
760 }
761 else if (IsEqualIID(&amt.formattype, &FORMAT_WaveFormatEx))
762 {
763 amt.cbFormat = pChunk->cb;
764 if (amt.cbFormat < sizeof(WAVEFORMATEX))
765 amt.cbFormat = sizeof(WAVEFORMATEX);
766 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
767 ZeroMemory(amt.pbFormat, amt.cbFormat);
768 CopyMemory(amt.pbFormat, pChunk + 1, pChunk->cb);
769 }
770 else
771 {
772 amt.cbFormat = pChunk->cb;
773 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
774 CopyMemory(amt.pbFormat, pChunk + 1, amt.cbFormat);
775 }
776 break;
777 case ckidSTREAMNAME:
778 TRACE("processing stream name\n");
779 /* FIXME: this doesn't exactly match native version (we omit the "##)" prefix), but hey... */
780 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)(pChunk + 1), pChunk->cb, piOutput.achName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
781 break;
782 case ckidSTREAMHANDLERDATA:
783 FIXME("process stream handler data\n");
784 break;
785 case ckidAVIPADDING:
786 TRACE("JUNK chunk ignored\n");
787 break;
788 case ckidAVISUPERINDEX:
789 {
790 const AVISUPERINDEX *pIndex = (const AVISUPERINDEX *)pChunk;
791 DWORD x;
792 UINT rest = pIndex->cb - sizeof(AVISUPERINDEX) + sizeof(RIFFCHUNK) + sizeof(pIndex->aIndex[0]) * ANYSIZE_ARRAY;
793
794 if (pIndex->cb < sizeof(AVISUPERINDEX) - sizeof(RIFFCHUNK))
795 {
796 FIXME("size %u\n", pIndex->cb);
797 break;
798 }
799
800 if (nstdindex++ > 0)
801 {
802 ERR("Stream %d got more than 1 superindex?\n", This->Parser.cStreams);
803 break;
804 }
805
806 TRACE("wLongsPerEntry: %hd\n", pIndex->wLongsPerEntry);
807 TRACE("bIndexSubType: %u\n", pIndex->bIndexSubType);
808 TRACE("bIndexType: %u\n", pIndex->bIndexType);
809 TRACE("nEntriesInUse: %u\n", pIndex->nEntriesInUse);
810 TRACE("dwChunkId: %.4s\n", (const char *)&pIndex->dwChunkId);
811 if (pIndex->dwReserved[0])
812 TRACE("dwReserved[0]: %u\n", pIndex->dwReserved[0]);
813 if (pIndex->dwReserved[1])
814 TRACE("dwReserved[1]: %u\n", pIndex->dwReserved[1]);
815 if (pIndex->dwReserved[2])
816 TRACE("dwReserved[2]: %u\n", pIndex->dwReserved[2]);
817
818 if (pIndex->bIndexType != AVI_INDEX_OF_INDEXES
819 || pIndex->wLongsPerEntry != 4
820 || rest < (pIndex->nEntriesInUse * sizeof(DWORD) * pIndex->wLongsPerEntry)
821 || (pIndex->bIndexSubType != AVI_INDEX_SUB_2FIELD && pIndex->bIndexSubType != AVI_INDEX_SUB_DEFAULT))
822 {
823 FIXME("Invalid index chunk encountered\n");
824 break;
825 }
826
827 stream->entries = pIndex->nEntriesInUse;
828 stream->stdindex = CoTaskMemRealloc(stream->stdindex, sizeof(*stream->stdindex) * stream->entries);
829 for (x = 0; x < pIndex->nEntriesInUse; ++x)
830 {
831 TRACE("qwOffset: %s\n", wine_dbgstr_longlong(pIndex->aIndex[x].qwOffset));
832 TRACE("dwSize: %u\n", pIndex->aIndex[x].dwSize);
833 TRACE("dwDuration: %u (unreliable)\n", pIndex->aIndex[x].dwDuration);
834
835 AVISplitter_ProcessIndex(This, &stream->stdindex[x], pIndex->aIndex[x].qwOffset, pIndex->aIndex[x].dwSize);
836 }
837 break;
838 }
839 default:
840 FIXME("unknown chunk type \"%.04s\" ignored\n", (LPCSTR)&pChunk->fcc);
841 }
842 }
843
844 if (IsEqualGUID(&amt.formattype, &FORMAT_WaveFormatEx))
845 {
846 amt.subtype = MEDIATYPE_Video;
847 amt.subtype.Data1 = ((WAVEFORMATEX *)amt.pbFormat)->wFormatTag;
848 }
849
850 dump_AM_MEDIA_TYPE(&amt);
851 TRACE("fSamplesPerSec = %f\n", (double)fSamplesPerSec);
852 TRACE("dwSampleSize = %x\n", dwSampleSize);
853 TRACE("dwLength = %x\n", dwLength);
854
855 stream->fSamplesPerSec = fSamplesPerSec;
856 stream->dwSampleSize = dwSampleSize;
857 stream->dwLength = dwLength; /* TODO: Use this for mediaseeking */
858 stream->packet_queued = CreateEventW(NULL, 0, 0, NULL);
859
860 hr = Parser_AddPin(&(This->Parser), &piOutput, props, &amt);
861 CoTaskMemFree(amt.pbFormat);
862
863
864 return hr;
865 }
866
867 static HRESULT AVISplitter_ProcessODML(AVISplitterImpl * This, const BYTE * pData, DWORD cb)
868 {
869 const RIFFCHUNK * pChunk;
870
871 for (pChunk = (const RIFFCHUNK *)pData;
872 ((const BYTE *)pChunk >= pData) && ((const BYTE *)pChunk + sizeof(RIFFCHUNK) < pData + cb) && (pChunk->cb > 0);
873 pChunk = (const RIFFCHUNK *)((const BYTE*)pChunk + sizeof(RIFFCHUNK) + pChunk->cb)
874 )
875 {
876 switch (pChunk->fcc)
877 {
878 case ckidAVIEXTHEADER:
879 {
880 int x;
881 const AVIEXTHEADER * pExtHdr = (const AVIEXTHEADER *)pChunk;
882
883 TRACE("processing extension header\n");
884 if (pExtHdr->cb != sizeof(AVIEXTHEADER) - sizeof(RIFFCHUNK))
885 {
886 FIXME("Size: %u\n", pExtHdr->cb);
887 break;
888 }
889 TRACE("dwGrandFrames: %u\n", pExtHdr->dwGrandFrames);
890 for (x = 0; x < 61; ++x)
891 if (pExtHdr->dwFuture[x])
892 FIXME("dwFuture[%i] = %u (0x%08x)\n", x, pExtHdr->dwFuture[x], pExtHdr->dwFuture[x]);
893 This->ExtHeader = *pExtHdr;
894 break;
895 }
896 default:
897 FIXME("unknown chunk type \"%.04s\" ignored\n", (LPCSTR)&pChunk->fcc);
898 }
899 }
900
901 return S_OK;
902 }
903
904 static HRESULT AVISplitter_InitializeStreams(AVISplitterImpl *This)
905 {
906 unsigned int x;
907
908 if (This->oldindex)
909 {
910 DWORD nMax, n;
911
912 for (x = 0; x < This->Parser.cStreams; ++x)
913 {
914 This->streams[x].frames = 0;
915 This->streams[x].pos = ~0;
916 This->streams[x].index = 0;
917 }
918
919 nMax = This->oldindex->cb / sizeof(This->oldindex->aIndex[0]);
920
921 /* Ok, maybe this is more of an exercise to see if I interpret everything correctly or not, but that is useful for now. */
922 for (n = 0; n < nMax; ++n)
923 {
924 DWORD streamId = StreamFromFOURCC(This->oldindex->aIndex[n].dwChunkId);
925 if (streamId >= This->Parser.cStreams)
926 {
927 FIXME("Stream id %s ignored\n", debugstr_an((char*)&This->oldindex->aIndex[n].dwChunkId, 4));
928 continue;
929 }
930 if (This->streams[streamId].pos == ~0U)
931 This->streams[streamId].pos = n;
932
933 if (This->streams[streamId].streamheader.dwSampleSize)
934 This->streams[streamId].frames += This->oldindex->aIndex[n].dwSize / This->streams[streamId].streamheader.dwSampleSize;
935 else
936 ++This->streams[streamId].frames;
937 }
938
939 for (x = 0; x < This->Parser.cStreams; ++x)
940 {
941 if ((DWORD)This->streams[x].frames != This->streams[x].streamheader.dwLength)
942 {
943 FIXME("stream %u: frames found: %u, frames meant to be found: %u\n", x, (DWORD)This->streams[x].frames, This->streams[x].streamheader.dwLength);
944 }
945 }
946
947 }
948 else if (!This->streams[0].entries)
949 {
950 for (x = 0; x < This->Parser.cStreams; ++x)
951 {
952 This->streams[x].frames = This->streams[x].streamheader.dwLength;
953 }
954 /* MS Avi splitter does seek through the whole file, we should! */
955 ERR("We should be manually seeking through the entire file to build an index, because the index is missing!!!\n");
956 return E_NOTIMPL;
957 }
958
959 /* Not much here yet */
960 for (x = 0; x < This->Parser.cStreams; ++x)
961 {
962 StreamData *stream = This->streams + x;
963 DWORD y;
964 DWORD64 frames = 0;
965
966 stream->seek = TRUE;
967
968 if (stream->stdindex)
969 {
970 stream->index = 0;
971 stream->pos = 0;
972 for (y = 0; y < stream->entries; ++y)
973 {
974 if (stream->streamheader.dwSampleSize)
975 {
976 DWORD z;
977
978 for (z = 0; z < stream->stdindex[y]->nEntriesInUse; ++z)
979 {
980 UINT len = stream->stdindex[y]->aIndex[z].dwSize & ~(1u << 31);
981 frames += len / stream->streamheader.dwSampleSize + !!(len % stream->streamheader.dwSampleSize);
982 }
983 }
984 else
985 frames += stream->stdindex[y]->nEntriesInUse;
986 }
987 }
988 else frames = stream->frames;
989
990 frames *= stream->streamheader.dwScale;
991 /* Keep accuracy as high as possible for duration */
992 This->Parser.sourceSeeking.llDuration = frames * 10000000;
993 This->Parser.sourceSeeking.llDuration /= stream->streamheader.dwRate;
994 This->Parser.sourceSeeking.llStop = This->Parser.sourceSeeking.llDuration;
995 This->Parser.sourceSeeking.llCurrent = 0;
996
997 frames /= stream->streamheader.dwRate;
998
999 TRACE("Duration: %d days, %d hours, %d minutes and %d.%03u seconds\n", (DWORD)(frames / 86400),
1000 (DWORD)((frames % 86400) / 3600), (DWORD)((frames % 3600) / 60), (DWORD)(frames % 60),
1001 (DWORD)(This->Parser.sourceSeeking.llDuration/10000) % 1000);
1002 }
1003
1004 return S_OK;
1005 }
1006
1007 static HRESULT AVISplitter_Disconnect(LPVOID iface);
1008
1009 /* FIXME: fix leaks on failure here */
1010 static HRESULT AVISplitter_InputPin_PreConnect(IPin * iface, IPin * pConnectPin, ALLOCATOR_PROPERTIES *props)
1011 {
1012 PullPin *This = impl_PullPin_from_IPin(iface);
1013 HRESULT hr;
1014 RIFFLIST list;
1015 LONGLONG pos = 0; /* in bytes */
1016 BYTE * pBuffer;
1017 RIFFCHUNK * pCurrentChunk;
1018 LONGLONG total, avail;
1019 ULONG x;
1020 DWORD indexes;
1021
1022 AVISplitterImpl * pAviSplit = (AVISplitterImpl *)This->pin.pinInfo.pFilter;
1023
1024 hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
1025 pos += sizeof(list);
1026
1027 if (list.fcc != FOURCC_RIFF)
1028 {
1029 ERR("Input stream not a RIFF file\n");
1030 return E_FAIL;
1031 }
1032 if (list.fccListType != formtypeAVI)
1033 {
1034 ERR("Input stream not an AVI RIFF file\n");
1035 return E_FAIL;
1036 }
1037
1038 hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
1039 if (list.fcc != FOURCC_LIST)
1040 {
1041 ERR("Expected LIST chunk, but got %.04s\n", (LPSTR)&list.fcc);
1042 return E_FAIL;
1043 }
1044 if (list.fccListType != listtypeAVIHEADER)
1045 {
1046 ERR("Header list expected. Got: %.04s\n", (LPSTR)&list.fccListType);
1047 return E_FAIL;
1048 }
1049
1050 pBuffer = HeapAlloc(GetProcessHeap(), 0, list.cb - sizeof(RIFFLIST) + sizeof(RIFFCHUNK));
1051 hr = IAsyncReader_SyncRead(This->pReader, pos + sizeof(list), list.cb - sizeof(RIFFLIST) + sizeof(RIFFCHUNK), pBuffer);
1052
1053 pAviSplit->AviHeader.cb = 0;
1054
1055 /* Stream list will set the buffer size here, so set a default and allow an override */
1056 props->cbBuffer = 0x20000;
1057
1058 for (pCurrentChunk = (RIFFCHUNK *)pBuffer; (BYTE *)pCurrentChunk + sizeof(*pCurrentChunk) < pBuffer + list.cb; pCurrentChunk = (RIFFCHUNK *)(((BYTE *)pCurrentChunk) + sizeof(*pCurrentChunk) + pCurrentChunk->cb))
1059 {
1060 RIFFLIST * pList;
1061
1062 switch (pCurrentChunk->fcc)
1063 {
1064 case ckidMAINAVIHEADER:
1065 /* AVIMAINHEADER includes the structure that is pCurrentChunk at the moment */
1066 memcpy(&pAviSplit->AviHeader, pCurrentChunk, sizeof(pAviSplit->AviHeader));
1067 break;
1068 case FOURCC_LIST:
1069 pList = (RIFFLIST *)pCurrentChunk;
1070 switch (pList->fccListType)
1071 {
1072 case ckidSTREAMLIST:
1073 hr = AVISplitter_ProcessStreamList(pAviSplit, (BYTE *)pCurrentChunk + sizeof(RIFFLIST), pCurrentChunk->cb + sizeof(RIFFCHUNK) - sizeof(RIFFLIST), props);
1074 break;
1075 case ckidODML:
1076 hr = AVISplitter_ProcessODML(pAviSplit, (BYTE *)pCurrentChunk + sizeof(RIFFLIST), pCurrentChunk->cb + sizeof(RIFFCHUNK) - sizeof(RIFFLIST));
1077 break;
1078 }
1079 break;
1080 case ckidAVIPADDING:
1081 /* ignore */
1082 break;
1083 default:
1084 FIXME("unrecognised header list type: %.04s\n", (LPSTR)&pCurrentChunk->fcc);
1085 }
1086 }
1087 HeapFree(GetProcessHeap(), 0, pBuffer);
1088
1089 if (pAviSplit->AviHeader.cb != sizeof(pAviSplit->AviHeader) - sizeof(RIFFCHUNK))
1090 {
1091 ERR("Avi Header wrong size!\n");
1092 return E_FAIL;
1093 }
1094
1095 /* Skip any chunks until we find the LIST chunk */
1096 do
1097 {
1098 pos += sizeof(RIFFCHUNK) + list.cb;
1099 hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
1100 }
1101 while (hr == S_OK && (list.fcc != FOURCC_LIST ||
1102 (list.fcc == FOURCC_LIST && list.fccListType != listtypeAVIMOVIE)));
1103
1104 if (hr != S_OK)
1105 {
1106 ERR("Failed to find LIST chunk from AVI file\n");
1107 return E_FAIL;
1108 }
1109
1110 IAsyncReader_Length(This->pReader, &total, &avail);
1111
1112 /* FIXME: AVIX files are extended beyond the FOURCC chunk "AVI ", and thus won't be played here,
1113 * once I get one of the files I'll try to fix it */
1114 This->rtStart = pAviSplit->CurrentChunkOffset = MEDIATIME_FROM_BYTES(pos + sizeof(RIFFLIST));
1115 pos += list.cb + sizeof(RIFFCHUNK);
1116
1117 pAviSplit->EndOfFile = This->rtStop = MEDIATIME_FROM_BYTES(pos);
1118 if (pos > total)
1119 {
1120 ERR("File smaller (%s) then EndOfFile (%s)\n", wine_dbgstr_longlong(total), wine_dbgstr_longlong(pAviSplit->EndOfFile));
1121 return E_FAIL;
1122 }
1123
1124 hr = IAsyncReader_SyncRead(This->pReader, BYTES_FROM_MEDIATIME(pAviSplit->CurrentChunkOffset), sizeof(pAviSplit->CurrentChunk), (BYTE *)&pAviSplit->CurrentChunk);
1125
1126 props->cbAlign = 1;
1127 props->cbPrefix = 0;
1128 /* Comrades, prevent shortage of buffers, or you will feel the consequences! DA! */
1129 props->cBuffers = 2 * pAviSplit->Parser.cStreams;
1130
1131 /* Now peek into the idx1 index, if available */
1132 if (hr == S_OK && (total - pos) > sizeof(RIFFCHUNK))
1133 {
1134 memset(&list, 0, sizeof(list));
1135
1136 hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
1137 if (list.fcc == ckidAVIOLDINDEX)
1138 {
1139 pAviSplit->oldindex = CoTaskMemRealloc(pAviSplit->oldindex, list.cb + sizeof(RIFFCHUNK));
1140 if (pAviSplit->oldindex)
1141 {
1142 hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(RIFFCHUNK) + list.cb, (BYTE *)pAviSplit->oldindex);
1143 if (hr == S_OK)
1144 {
1145 hr = AVISplitter_ProcessOldIndex(pAviSplit);
1146 }
1147 else
1148 {
1149 CoTaskMemFree(pAviSplit->oldindex);
1150 pAviSplit->oldindex = NULL;
1151 hr = S_OK;
1152 }
1153 }
1154 }
1155 }
1156
1157 indexes = 0;
1158 for (x = 0; x < pAviSplit->Parser.cStreams; ++x)
1159 if (pAviSplit->streams[x].entries)
1160 ++indexes;
1161
1162 if (indexes)
1163 {
1164 CoTaskMemFree(pAviSplit->oldindex);
1165 pAviSplit->oldindex = NULL;
1166 if (indexes < pAviSplit->Parser.cStreams)
1167 {
1168 /* This error could possible be survived by switching to old type index,
1169 * but I would rather find out why it doesn't find everything here
1170 */
1171 ERR("%d indexes expected, but only have %d\n", indexes, pAviSplit->Parser.cStreams);
1172 indexes = 0;
1173 }
1174 }
1175 else if (pAviSplit->oldindex)
1176 indexes = pAviSplit->Parser.cStreams;
1177
1178 if (!indexes && pAviSplit->AviHeader.dwFlags & AVIF_MUSTUSEINDEX)
1179 {
1180 FIXME("No usable index was found!\n");
1181 hr = E_FAIL;
1182 }
1183
1184 /* Now, set up the streams */
1185 if (hr == S_OK)
1186 hr = AVISplitter_InitializeStreams(pAviSplit);
1187
1188 if (hr != S_OK)
1189 {
1190 AVISplitter_Disconnect(pAviSplit);
1191 return E_FAIL;
1192 }
1193
1194 TRACE("AVI File ok\n");
1195
1196 return hr;
1197 }
1198
1199 static HRESULT AVISplitter_Flush(LPVOID iface)
1200 {
1201 AVISplitterImpl *This = iface;
1202 DWORD x;
1203 ULONG ref;
1204
1205 TRACE("(%p)->()\n", This);
1206
1207 for (x = 0; x < This->Parser.cStreams; ++x)
1208 {
1209 StreamData *stream = This->streams + x;
1210
1211 if (stream->sample)
1212 {
1213 ref = IMediaSample_Release(stream->sample);
1214 assert(ref == 0);
1215 }
1216 stream->sample = NULL;
1217
1218 ResetEvent(stream->packet_queued);
1219 assert(!stream->thread);
1220 }
1221
1222 return S_OK;
1223 }
1224
1225 static HRESULT AVISplitter_Disconnect(LPVOID iface)
1226 {
1227 AVISplitterImpl *This = iface;
1228 ULONG x;
1229
1230 /* TODO: Remove other memory that's allocated during connect */
1231 CoTaskMemFree(This->oldindex);
1232 This->oldindex = NULL;
1233
1234 for (x = 0; x < This->Parser.cStreams; ++x)
1235 {
1236 DWORD i;
1237
1238 StreamData *stream = &This->streams[x];
1239
1240 for (i = 0; i < stream->entries; ++i)
1241 CoTaskMemFree(stream->stdindex[i]);
1242
1243 CoTaskMemFree(stream->stdindex);
1244 CloseHandle(stream->packet_queued);
1245 }
1246 CoTaskMemFree(This->streams);
1247 This->streams = NULL;
1248 return S_OK;
1249 }
1250
1251 static ULONG WINAPI AVISplitter_Release(IBaseFilter *iface)
1252 {
1253 AVISplitterImpl *This = (AVISplitterImpl *)iface;
1254 ULONG ref;
1255
1256 ref = InterlockedDecrement(&This->Parser.filter.refCount);
1257
1258 TRACE("(%p)->() Release from %d\n", This, ref + 1);
1259
1260 if (!ref)
1261 {
1262 AVISplitter_Flush(This);
1263 Parser_Destroy(&This->Parser);
1264 }
1265
1266 return ref;
1267 }
1268
1269 static HRESULT WINAPI AVISplitter_seek(IMediaSeeking *iface)
1270 {
1271 AVISplitterImpl *This = impl_from_IMediaSeeking(iface);
1272 PullPin *pPin = This->Parser.pInputPin;
1273 LONGLONG newpos, endpos;
1274 DWORD x;
1275
1276 newpos = This->Parser.sourceSeeking.llCurrent;
1277 endpos = This->Parser.sourceSeeking.llDuration;
1278
1279 if (newpos > endpos)
1280 {
1281 WARN("Requesting position %x%08x beyond end of stream %x%08x\n", (DWORD)(newpos>>32), (DWORD)newpos, (DWORD)(endpos>>32), (DWORD)endpos);
1282 return E_INVALIDARG;
1283 }
1284
1285 FIXME("Moving position to %u.%03u s!\n", (DWORD)(newpos / 10000000), (DWORD)((newpos / 10000)%1000));
1286
1287 EnterCriticalSection(&pPin->thread_lock);
1288 /* Send a flush to all output pins */
1289 IPin_BeginFlush(&pPin->pin.IPin_iface);
1290
1291 /* Make sure this is done while stopped, BeginFlush takes care of this */
1292 EnterCriticalSection(&This->Parser.filter.csFilter);
1293 for (x = 0; x < This->Parser.cStreams; ++x)
1294 {
1295 Parser_OutputPin *pin = unsafe_impl_Parser_OutputPin_from_IPin(This->Parser.ppPins[1+x]);
1296 StreamData *stream = This->streams + x;
1297 LONGLONG wanted_frames;
1298 DWORD last_keyframe = 0, last_keyframeidx = 0, preroll = 0;
1299
1300 wanted_frames = newpos;
1301 wanted_frames *= stream->streamheader.dwRate;
1302 wanted_frames /= 10000000;
1303 wanted_frames /= stream->streamheader.dwScale;
1304
1305 pin->dwSamplesProcessed = 0;
1306 stream->index = 0;
1307 stream->pos = 0;
1308 stream->seek = TRUE;
1309 if (stream->stdindex)
1310 {
1311 DWORD y, z = 0;
1312
1313 for (y = 0; y < stream->entries; ++y)
1314 {
1315 for (z = 0; z < stream->stdindex[y]->nEntriesInUse; ++z)
1316 {
1317 if (stream->streamheader.dwSampleSize)
1318 {
1319 ULONG len = stream->stdindex[y]->aIndex[z].dwSize & ~(1u << 31);
1320 ULONG size = stream->streamheader.dwSampleSize;
1321
1322 pin->dwSamplesProcessed += len / size;
1323 if (len % size)
1324 ++pin->dwSamplesProcessed;
1325 }
1326 else ++pin->dwSamplesProcessed;
1327
1328 if (!(stream->stdindex[y]->aIndex[z].dwSize >> 31))
1329 {
1330 last_keyframe = z;
1331 last_keyframeidx = y;
1332 preroll = 0;
1333 }
1334 else
1335 ++preroll;
1336
1337 if (pin->dwSamplesProcessed >= wanted_frames)
1338 break;
1339 }
1340 if (pin->dwSamplesProcessed >= wanted_frames)
1341 break;
1342 }
1343 stream->index = last_keyframeidx;
1344 stream->pos = last_keyframe;
1345 }
1346 else
1347 {
1348 DWORD nMax, n;
1349 nMax = This->oldindex->cb / sizeof(This->oldindex->aIndex[0]);
1350
1351 for (n = 0; n < nMax; ++n)
1352 {
1353 DWORD streamId = StreamFromFOURCC(This->oldindex->aIndex[n].dwChunkId);
1354 if (streamId != x)
1355 continue;
1356
1357 if (stream->streamheader.dwSampleSize)
1358 {
1359 ULONG len = This->oldindex->aIndex[n].dwSize;
1360 ULONG size = stream->streamheader.dwSampleSize;
1361
1362 pin->dwSamplesProcessed += len / size;
1363 if (len % size)
1364 ++pin->dwSamplesProcessed;
1365 }
1366 else ++pin->dwSamplesProcessed;
1367
1368 if (This->oldindex->aIndex[n].dwFlags & AVIIF_KEYFRAME)
1369 {
1370 last_keyframe = n;
1371 preroll = 0;
1372 }
1373 else
1374 ++preroll;
1375
1376 if (pin->dwSamplesProcessed >= wanted_frames)
1377 break;
1378 }
1379 assert(n < nMax);
1380 stream->pos = last_keyframe;
1381 stream->index = 0;
1382 }
1383 stream->preroll = preroll;
1384 stream->seek = TRUE;
1385 }
1386 LeaveCriticalSection(&This->Parser.filter.csFilter);
1387
1388 TRACE("Done flushing\n");
1389 IPin_EndFlush(&pPin->pin.IPin_iface);
1390 LeaveCriticalSection(&pPin->thread_lock);
1391
1392 return S_OK;
1393 }
1394
1395 static const IBaseFilterVtbl AVISplitterImpl_Vtbl =
1396 {
1397 Parser_QueryInterface,
1398 Parser_AddRef,
1399 AVISplitter_Release,
1400 Parser_GetClassID,
1401 Parser_Stop,
1402 Parser_Pause,
1403 Parser_Run,
1404 Parser_GetState,
1405 Parser_SetSyncSource,
1406 Parser_GetSyncSource,
1407 Parser_EnumPins,
1408 Parser_FindPin,
1409 Parser_QueryFilterInfo,
1410 Parser_JoinFilterGraph,
1411 Parser_QueryVendorInfo
1412 };
1413
1414 HRESULT AVISplitter_create(IUnknown * pUnkOuter, LPVOID * ppv)
1415 {
1416 HRESULT hr;
1417 AVISplitterImpl * This;
1418
1419 TRACE("(%p, %p)\n", pUnkOuter, ppv);
1420
1421 *ppv = NULL;
1422
1423 if (pUnkOuter)
1424 return CLASS_E_NOAGGREGATION;
1425
1426 /* Note: This memory is managed by the transform filter once created */
1427 This = CoTaskMemAlloc(sizeof(AVISplitterImpl));
1428
1429 This->streams = NULL;
1430 This->oldindex = NULL;
1431
1432 hr = Parser_Create(&(This->Parser), &AVISplitterImpl_Vtbl, &CLSID_AviSplitter, AVISplitter_Sample, AVISplitter_QueryAccept, AVISplitter_InputPin_PreConnect, AVISplitter_Flush, AVISplitter_Disconnect, AVISplitter_first_request, AVISplitter_done_process, NULL, AVISplitter_seek, NULL);
1433
1434 if (FAILED(hr))
1435 return hr;
1436
1437 *ppv = &This->Parser.filter.IBaseFilter_iface;
1438
1439 return hr;
1440 }