4 * Copyright 2003 Robert Shearman
5 * Copyright 2004-2005 Christian Costa
6 * Copyright 2008 Maarten Lankhorst
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.
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.
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
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.
32 #include "quartz_private.h"
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
49 #define TWOCCFromFOURCC(fcc) HIWORD(fcc)
51 /* four character codes used in AVI files */
52 #define ckidINFO mmioFOURCC('I','N','F','O')
53 #define ckidREC mmioFOURCC('R','E','C',' ')
55 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
57 typedef struct StreamData
63 AVISTREAMHEADER streamheader
;
65 AVISTDINDEX
**stdindex
;
69 /* Position, in index units */
70 DWORD pos
, pos_next
, index
, index_next
;
72 /* Packet handling: a thread is created and waits on the packet event handle
73 * On an event acquire the sample lock, addref the sample and set it to NULL,
74 * then queue a new packet.
76 HANDLE thread
, packet_queued
;
79 /* Amount of preroll samples for this stream */
83 typedef struct AVISplitterImpl
86 RIFFCHUNK CurrentChunk
;
87 LONGLONG CurrentChunkOffset
; /* in media time */
89 AVIMAINHEADER AviHeader
;
90 AVIEXTHEADER ExtHeader
;
92 AVIOLDINDEX
*oldindex
;
99 AVISplitterImpl
*This
;
103 static inline AVISplitterImpl
*impl_from_IMediaSeeking( IMediaSeeking
*iface
)
105 return CONTAINING_RECORD(iface
, AVISplitterImpl
, Parser
.sourceSeeking
.IMediaSeeking_iface
);
108 /* The threading stuff cries for an explanation
110 * PullPin starts processing and calls AVISplitter_first_request
111 * AVISplitter_first_request creates a thread for each stream
112 * A stream can be audio, video, subtitles or something undefined.
114 * AVISplitter_first_request loads a single packet to each but one stream,
115 * and queues it for that last stream. This is to prevent WaitForNext to time
118 * The processing loop is entered. It calls IAsyncReader_WaitForNext in the
119 * PullPin. Every time it receives a packet, it will call AVISplitter_Sample
120 * AVISplitter_Sample will signal the relevant thread that a new sample is
121 * arrived, when that thread is ready it will read the packet and transmits
122 * it downstream with AVISplitter_Receive
124 * Threads terminate upon receiving NULL as packet or when ANY error code
125 * != S_OK occurs. This means that any error is fatal to processing.
128 static HRESULT
AVISplitter_SendEndOfFile(AVISplitterImpl
*This
, DWORD streamnumber
)
133 TRACE("End of file reached\n");
135 hr
= IPin_ConnectedTo(This
->Parser
.ppPins
[streamnumber
+1], &ppin
);
138 hr
= IPin_EndOfStream(ppin
);
141 TRACE("--> %x\n", hr
);
143 /* Force the pullpin thread to stop */
147 /* Thread worker horse */
148 static HRESULT
AVISplitter_next_request(AVISplitterImpl
*This
, DWORD streamnumber
)
150 StreamData
*stream
= This
->streams
+ streamnumber
;
151 PullPin
*pin
= This
->Parser
.pInputPin
;
152 IMediaSample
*sample
= NULL
;
155 TRACE("(%p, %u)->()\n", This
, streamnumber
);
157 hr
= IMemAllocator_GetBuffer(pin
->pAlloc
, &sample
, NULL
, NULL
, 0);
159 ERR("... %08x?\n", hr
);
163 LONGLONG rtSampleStart
;
164 /* Add 4 for the next header, which should hopefully work */
165 LONGLONG rtSampleStop
;
167 stream
->pos
= stream
->pos_next
;
168 stream
->index
= stream
->index_next
;
170 IMediaSample_SetDiscontinuity(sample
, stream
->seek
);
171 stream
->seek
= FALSE
;
175 IMediaSample_SetPreroll(sample
, TRUE
);
178 IMediaSample_SetPreroll(sample
, FALSE
);
179 IMediaSample_SetSyncPoint(sample
, TRUE
);
181 if (stream
->stdindex
)
183 AVISTDINDEX
*index
= stream
->stdindex
[stream
->index
];
184 AVISTDINDEX_ENTRY
*entry
= &index
->aIndex
[stream
->pos
];
187 if (stream
->index
>= stream
->entries
)
189 TRACE("END OF STREAM ON %u\n", streamnumber
);
190 IMediaSample_Release(sample
);
194 rtSampleStart
= index
->qwBaseOffset
;
195 rtSampleStart
+= entry
->dwOffset
;
196 rtSampleStart
= MEDIATIME_FROM_BYTES(rtSampleStart
);
199 if (index
->nEntriesInUse
== stream
->pos_next
)
201 stream
->pos_next
= 0;
202 ++stream
->index_next
;
205 rtSampleStop
= rtSampleStart
+ MEDIATIME_FROM_BYTES(entry
->dwSize
& ~(1 << 31));
207 TRACE("offset(%u) size(%u)\n", (DWORD
)BYTES_FROM_MEDIATIME(rtSampleStart
), (DWORD
)BYTES_FROM_MEDIATIME(rtSampleStop
- rtSampleStart
));
209 else if (This
->oldindex
)
211 DWORD flags
= This
->oldindex
->aIndex
[stream
->pos
].dwFlags
;
212 DWORD size
= This
->oldindex
->aIndex
[stream
->pos
].dwSize
;
217 TRACE("END OF STREAM ON %u\n", streamnumber
);
218 IMediaSample_Release(sample
);
222 rtSampleStart
= MEDIATIME_FROM_BYTES(This
->offset
);
223 rtSampleStart
+= MEDIATIME_FROM_BYTES(This
->oldindex
->aIndex
[stream
->pos
].dwOffset
);
224 rtSampleStop
= rtSampleStart
+ MEDIATIME_FROM_BYTES(size
);
225 if (flags
& AVIIF_MIDPART
)
227 FIXME("Only stand alone frames are currently handled correctly!\n");
229 if (flags
& AVIIF_LIST
)
231 FIXME("Not sure if this is handled correctly\n");
232 rtSampleStart
+= MEDIATIME_FROM_BYTES(sizeof(RIFFLIST
));
233 rtSampleStop
+= MEDIATIME_FROM_BYTES(sizeof(RIFFLIST
));
237 rtSampleStart
+= MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
));
238 rtSampleStop
+= MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK
));
241 /* Slow way of finding next index */
244 } while (stream
->pos_next
* sizeof(This
->oldindex
->aIndex
[0]) < This
->oldindex
->cb
245 && StreamFromFOURCC(This
->oldindex
->aIndex
[stream
->pos_next
].dwChunkId
) != streamnumber
);
247 /* End of file soon */
248 if (stream
->pos_next
* sizeof(This
->oldindex
->aIndex
[0]) >= This
->oldindex
->cb
)
250 stream
->pos_next
= 0;
251 ++stream
->index_next
;
254 else /* TODO: Generate an index automagically */
256 ERR("CAN'T PLAY WITHOUT AN INDEX! SOS! SOS! SOS!\n");
260 if (rtSampleStart
!= rtSampleStop
)
262 hr
= IMediaSample_SetTime(sample
, &rtSampleStart
, &rtSampleStop
);
264 hr
= IAsyncReader_Request(pin
->pReader
, sample
, streamnumber
);
267 assert(IMediaSample_Release(sample
) == 0);
271 stream
->sample
= sample
;
272 IMediaSample_SetActualDataLength(sample
, 0);
273 SetEvent(stream
->packet_queued
);
280 ERR("There should be no sample!\n");
281 assert(IMediaSample_Release(sample
) == 0);
284 TRACE("--> %08x\n", hr
);
289 static HRESULT
AVISplitter_Receive(AVISplitterImpl
*This
, IMediaSample
*sample
, DWORD streamnumber
)
291 Parser_OutputPin
*pin
= unsafe_impl_Parser_OutputPin_from_IPin(This
->Parser
.ppPins
[1+streamnumber
]);
293 LONGLONG start
, stop
, rtstart
, rtstop
;
294 StreamData
*stream
= &This
->streams
[streamnumber
];
296 start
= pin
->dwSamplesProcessed
;
297 start
*= stream
->streamheader
.dwScale
;
299 start
/= stream
->streamheader
.dwRate
;
301 if (stream
->streamheader
.dwSampleSize
)
303 ULONG len
= IMediaSample_GetActualDataLength(sample
);
304 ULONG size
= stream
->streamheader
.dwSampleSize
;
306 pin
->dwSamplesProcessed
+= len
/ size
;
309 ++pin
->dwSamplesProcessed
;
311 stop
= pin
->dwSamplesProcessed
;
312 stop
*= stream
->streamheader
.dwScale
;
314 stop
/= stream
->streamheader
.dwRate
;
316 if (IMediaSample_IsDiscontinuity(sample
) == S_OK
) {
318 EnterCriticalSection(&This
->Parser
.filter
.csFilter
);
319 pin
->pin
.pin
.tStart
= start
;
320 pin
->pin
.pin
.dRate
= This
->Parser
.sourceSeeking
.dRate
;
321 hr
= IPin_ConnectedTo(&pin
->pin
.pin
.IPin_iface
, &victim
);
324 hr
= IPin_NewSegment(victim
, start
, This
->Parser
.sourceSeeking
.llStop
,
325 This
->Parser
.sourceSeeking
.dRate
);
327 FIXME("NewSegment returns %08x\n", hr
);
328 IPin_Release(victim
);
330 LeaveCriticalSection(&This
->Parser
.filter
.csFilter
);
334 rtstart
= (double)(start
- pin
->pin
.pin
.tStart
) / pin
->pin
.pin
.dRate
;
335 rtstop
= (double)(stop
- pin
->pin
.pin
.tStart
) / pin
->pin
.pin
.dRate
;
336 hr
= IMediaSample_SetMediaTime(sample
, &start
, &stop
);
337 IMediaSample_SetTime(sample
, &rtstart
, &rtstop
);
338 IMediaSample_SetMediaTime(sample
, &start
, &stop
);
340 hr
= BaseOutputPinImpl_Deliver(&pin
->pin
, sample
);
342 /* Uncomment this if you want to debug the time differences between the
343 * different streams, it is useful for that
345 FIXME("stream %u, hr: %08x, Start: %u.%03u, Stop: %u.%03u\n", streamnumber, hr,
346 (DWORD)(start / 10000000), (DWORD)((start / 10000)%1000),
347 (DWORD)(stop / 10000000), (DWORD)((stop / 10000)%1000));
352 static DWORD WINAPI
AVISplitter_thread_reader(LPVOID data
)
354 struct thread_args
*args
= data
;
355 AVISplitterImpl
*This
= args
->This
;
356 DWORD streamnumber
= args
->stream
;
361 HRESULT nexthr
= S_FALSE
;
362 IMediaSample
*sample
;
364 WaitForSingleObject(This
->streams
[streamnumber
].packet_queued
, INFINITE
);
365 sample
= This
->streams
[streamnumber
].sample
;
366 This
->streams
[streamnumber
].sample
= NULL
;
370 nexthr
= AVISplitter_next_request(This
, streamnumber
);
372 hr
= AVISplitter_Receive(This
, sample
, streamnumber
);
374 FIXME("Receiving error: %08x\n", hr
);
376 IMediaSample_Release(sample
);
379 if (nexthr
== S_FALSE
)
380 AVISplitter_SendEndOfFile(This
, streamnumber
);
381 } while (hr
== S_OK
);
384 FIXME("Thread %u terminated with hr %08x!\n", streamnumber
, hr
);
386 TRACE("Thread %u terminated properly\n", streamnumber
);
390 static HRESULT
AVISplitter_Sample(LPVOID iface
, IMediaSample
* pSample
, DWORD_PTR cookie
)
392 AVISplitterImpl
*This
= iface
;
393 StreamData
*stream
= This
->streams
+ cookie
;
396 if (!IMediaSample_GetActualDataLength(pSample
))
398 ERR("Received empty sample\n");
402 /* Send the sample to whatever thread is appropriate
403 * That thread should also not have a sample queued at the moment
406 TRACE("(%p)->(%p size: %u, %lu)\n", This
, pSample
, IMediaSample_GetActualDataLength(pSample
), cookie
);
407 assert(cookie
< This
->Parser
.cStreams
);
408 assert(!stream
->sample
);
409 assert(WaitForSingleObject(stream
->packet_queued
, 0) == WAIT_TIMEOUT
);
411 IMediaSample_AddRef(pSample
);
413 stream
->sample
= pSample
;
414 SetEvent(stream
->packet_queued
);
419 static HRESULT
AVISplitter_done_process(LPVOID iface
);
421 /* On the first request we have to be sure that (cStreams-1) samples have
422 * already been processed, because otherwise some pins might not ever finish
423 * a Pause state change
425 static HRESULT
AVISplitter_first_request(LPVOID iface
)
427 AVISplitterImpl
*This
= iface
;
430 IMediaSample
*sample
= NULL
;
431 BOOL have_sample
= FALSE
;
433 TRACE("(%p)->()\n", This
);
435 for (x
= 0; x
< This
->Parser
.cStreams
; ++x
)
437 StreamData
*stream
= This
->streams
+ x
;
439 /* Nothing should be running at this point */
440 assert(!stream
->thread
);
443 /* It could be we asked the thread to terminate, and the thread
444 * already terminated before receiving the deathwish */
445 ResetEvent(stream
->packet_queued
);
447 stream
->pos_next
= stream
->pos
;
448 stream
->index_next
= stream
->index
;
450 /* This was sent after stopped->paused or stopped->playing, so set seek */
453 /* There should be a packet queued from AVISplitter_next_request last time
454 * It needs to be done now because this is the only way to ensure that every
455 * stream will have at least 1 packet processed
456 * If this is done after the threads start it could go all awkward and we
457 * would have no guarantees that it's successful at all
462 DWORD_PTR dwUser
= ~0;
463 hr
= IAsyncReader_WaitForNext(This
->Parser
.pInputPin
->pReader
, 10000, &sample
, &dwUser
);
467 AVISplitter_Sample(iface
, sample
, dwUser
);
468 IMediaSample_Release(sample
);
471 hr
= AVISplitter_next_request(This
, x
);
472 TRACE("-->%08x\n", hr
);
474 /* Could be an EOF instead */
475 have_sample
= (hr
== S_OK
);
477 AVISplitter_SendEndOfFile(This
, x
);
479 if (FAILED(hr
) && hr
!= VFW_E_NOT_CONNECTED
)
484 /* FIXME: Don't do this for each pin that sent an EOF */
485 for (x
= 0; x
< This
->Parser
.cStreams
&& SUCCEEDED(hr
); ++x
)
487 struct thread_args
*args
;
490 if ((This
->streams
[x
].stdindex
&& This
->streams
[x
].index_next
>= This
->streams
[x
].entries
) ||
491 (!This
->streams
[x
].stdindex
&& This
->streams
[x
].index_next
))
493 This
->streams
[x
].thread
= NULL
;
497 args
= CoTaskMemAlloc(sizeof(*args
));
500 This
->streams
[x
].thread
= CreateThread(NULL
, 0, AVISplitter_thread_reader
, args
, 0, &tid
);
501 TRACE("Created stream %u thread 0x%08x\n", x
, tid
);
505 ERR("Horsemen of the apocalypse came to bring error 0x%08x\n", hr
);
510 static HRESULT
AVISplitter_done_process(LPVOID iface
)
512 AVISplitterImpl
*This
= iface
;
516 for (x
= 0; x
< This
->Parser
.cStreams
; ++x
)
518 StreamData
*stream
= This
->streams
+ x
;
520 TRACE("Waiting for %u to terminate\n", x
);
521 /* Make the thread return first */
522 SetEvent(stream
->packet_queued
);
523 assert(WaitForSingleObject(stream
->thread
, 100000) != WAIT_TIMEOUT
);
524 CloseHandle(stream
->thread
);
525 stream
->thread
= NULL
;
528 assert(IMediaSample_Release(stream
->sample
) == 0);
529 stream
->sample
= NULL
;
531 ResetEvent(stream
->packet_queued
);
533 TRACE("All threads are now terminated\n");
538 static HRESULT
AVISplitter_QueryAccept(LPVOID iface
, const AM_MEDIA_TYPE
* pmt
)
540 if (IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Stream
) && IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_Avi
))
545 static HRESULT
AVISplitter_ProcessIndex(AVISplitterImpl
*This
, AVISTDINDEX
**index
, LONGLONG qwOffset
, DWORD cb
)
552 if (cb
< sizeof(AVISTDINDEX
))
554 FIXME("size %u too small\n", cb
);
558 pIndex
= CoTaskMemAlloc(cb
);
560 return E_OUTOFMEMORY
;
562 IAsyncReader_SyncRead((impl_PullPin_from_IPin(This
->Parser
.ppPins
[0]))->pReader
, qwOffset
, cb
, (BYTE
*)pIndex
);
563 rest
= cb
- sizeof(AVISUPERINDEX
) + sizeof(RIFFCHUNK
) + sizeof(pIndex
->aIndex
);
565 TRACE("FOURCC: %s\n", debugstr_an((char *)&pIndex
->fcc
, 4));
566 TRACE("wLongsPerEntry: %hd\n", pIndex
->wLongsPerEntry
);
567 TRACE("bIndexSubType: %u\n", pIndex
->bIndexSubType
);
568 TRACE("bIndexType: %u\n", pIndex
->bIndexType
);
569 TRACE("nEntriesInUse: %u\n", pIndex
->nEntriesInUse
);
570 TRACE("dwChunkId: %.4s\n", (char *)&pIndex
->dwChunkId
);
571 TRACE("qwBaseOffset: %x%08x\n", (DWORD
)(pIndex
->qwBaseOffset
>> 32), (DWORD
)pIndex
->qwBaseOffset
);
572 TRACE("dwReserved_3: %u\n", pIndex
->dwReserved_3
);
574 if (pIndex
->bIndexType
!= AVI_INDEX_OF_CHUNKS
575 || pIndex
->wLongsPerEntry
!= 2
576 || rest
< (pIndex
->nEntriesInUse
* sizeof(DWORD
) * pIndex
->wLongsPerEntry
)
577 || (pIndex
->bIndexSubType
!= AVI_INDEX_SUB_DEFAULT
))
579 FIXME("Invalid index chunk encountered: %u/%u, %u/%u, %u/%u, %u/%u\n",
580 pIndex
->bIndexType
, AVI_INDEX_OF_CHUNKS
, pIndex
->wLongsPerEntry
, 2,
581 rest
, (DWORD
)(pIndex
->nEntriesInUse
* sizeof(DWORD
) * pIndex
->wLongsPerEntry
),
582 pIndex
->bIndexSubType
, AVI_INDEX_SUB_DEFAULT
);
587 for (x
= 0; x
< pIndex
->nEntriesInUse
; ++x
)
589 BOOL keyframe
= !(pIndex
->aIndex
[x
].dwSize
>> 31);
590 DWORDLONG offset
= pIndex
->qwBaseOffset
+ pIndex
->aIndex
[x
].dwOffset
;
591 TRACE("dwOffset: %x%08x\n", (DWORD
)(offset
>> 32), (DWORD
)offset
);
592 TRACE("dwSize: %u\n", (pIndex
->aIndex
[x
].dwSize
& ~(1<<31)));
593 TRACE("Frame is a keyframe: %s\n", keyframe
? "yes" : "no");
600 static HRESULT
AVISplitter_ProcessOldIndex(AVISplitterImpl
*This
)
602 ULONGLONG mov_pos
= BYTES_FROM_MEDIATIME(This
->CurrentChunkOffset
) - sizeof(DWORD
);
603 AVIOLDINDEX
*pAviOldIndex
= This
->oldindex
;
607 for (x
= 0; x
< pAviOldIndex
->cb
/ sizeof(pAviOldIndex
->aIndex
[0]); ++x
)
609 DWORD temp
, temp2
= 0, offset
, chunkid
;
610 PullPin
*pin
= This
->Parser
.pInputPin
;
612 offset
= pAviOldIndex
->aIndex
[x
].dwOffset
;
613 chunkid
= pAviOldIndex
->aIndex
[x
].dwChunkId
;
615 TRACE("dwChunkId: %.4s\n", (char *)&chunkid
);
616 TRACE("dwFlags: %08x\n", pAviOldIndex
->aIndex
[x
].dwFlags
);
617 TRACE("dwOffset (%s): %08x\n", relative
? "relative" : "absolute", offset
);
618 TRACE("dwSize: %08x\n", pAviOldIndex
->aIndex
[x
].dwSize
);
620 /* Only scan once, or else this will take too long */
623 IAsyncReader_SyncRead(pin
->pReader
, offset
, sizeof(DWORD
), (BYTE
*)&temp
);
624 relative
= (chunkid
!= temp
);
626 if (chunkid
== mmioFOURCC('7','F','x','x')
627 && ((char *)&temp
)[0] == 'i' && ((char *)&temp
)[1] == 'x')
632 if (offset
+ mov_pos
< BYTES_FROM_MEDIATIME(This
->EndOfFile
))
633 IAsyncReader_SyncRead(pin
->pReader
, offset
+ mov_pos
, sizeof(DWORD
), (BYTE
*)&temp2
);
635 if (chunkid
== mmioFOURCC('7','F','x','x')
636 && ((char *)&temp2
)[0] == 'i' && ((char *)&temp2
)[1] == 'x')
638 /* Do nothing, all is great */
640 else if (temp2
!= chunkid
)
642 ERR("Faulty index or bug in handling: Wanted FCC: %s, Abs FCC: %s (@ %x), Rel FCC: %s (@ %.0x%08x)\n",
643 debugstr_an((char *)&chunkid
, 4), debugstr_an((char *)&temp
, 4), offset
,
644 debugstr_an((char *)&temp2
, 4), (DWORD
)((mov_pos
+ offset
) >> 32), (DWORD
)(mov_pos
+ offset
));
648 TRACE("Scanned dwChunkId: %s\n", debugstr_an((char *)&temp2
, 4));
651 TRACE("Scanned dwChunkId: %s\n", debugstr_an((char *)&temp
, 4));
653 /* Only dump one packet */
659 FIXME("Dropping index: no idea whether it is relative or absolute\n");
660 CoTaskMemFree(This
->oldindex
);
661 This
->oldindex
= NULL
;
666 This
->offset
= (DWORD
)mov_pos
;
671 static HRESULT
AVISplitter_ProcessStreamList(AVISplitterImpl
* This
, const BYTE
* pData
, DWORD cb
, ALLOCATOR_PROPERTIES
*props
)
674 const RIFFCHUNK
* pChunk
;
677 float fSamplesPerSec
= 0.0f
;
678 DWORD dwSampleSize
= 0;
681 static const WCHAR wszStreamTemplate
[] = {'S','t','r','e','a','m',' ','%','0','2','d',0};
684 ZeroMemory(&amt
, sizeof(amt
));
685 piOutput
.dir
= PINDIR_OUTPUT
;
686 piOutput
.pFilter
= (IBaseFilter
*)This
;
687 wsprintfW(piOutput
.achName
, wszStreamTemplate
, This
->Parser
.cStreams
);
688 This
->streams
= CoTaskMemRealloc(This
->streams
, sizeof(StreamData
) * (This
->Parser
.cStreams
+1));
689 stream
= This
->streams
+ This
->Parser
.cStreams
;
690 ZeroMemory(stream
, sizeof(*stream
));
692 for (pChunk
= (const RIFFCHUNK
*)pData
;
693 ((const BYTE
*)pChunk
>= pData
) && ((const BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) < pData
+ cb
) && (pChunk
->cb
> 0);
694 pChunk
= (const RIFFCHUNK
*)((const BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) + pChunk
->cb
)
699 case ckidSTREAMHEADER
:
701 const AVISTREAMHEADER
* pStrHdr
= (const AVISTREAMHEADER
*)pChunk
;
702 TRACE("processing stream header\n");
703 stream
->streamheader
= *pStrHdr
;
705 fSamplesPerSec
= (float)pStrHdr
->dwRate
/ (float)pStrHdr
->dwScale
;
706 CoTaskMemFree(amt
.pbFormat
);
710 switch (pStrHdr
->fccType
)
712 case streamtypeVIDEO
:
713 amt
.formattype
= FORMAT_VideoInfo
;
715 case streamtypeAUDIO
:
716 amt
.formattype
= FORMAT_WaveFormatEx
;
719 FIXME("fccType %.4s not handled yet\n", (const char *)&pStrHdr
->fccType
);
720 amt
.formattype
= FORMAT_None
;
722 amt
.majortype
= MEDIATYPE_Video
;
723 amt
.majortype
.Data1
= pStrHdr
->fccType
;
724 amt
.subtype
= MEDIATYPE_Video
;
725 amt
.subtype
.Data1
= pStrHdr
->fccHandler
;
726 TRACE("Subtype FCC: %.04s\n", (LPCSTR
)&pStrHdr
->fccHandler
);
727 amt
.lSampleSize
= pStrHdr
->dwSampleSize
;
728 amt
.bFixedSizeSamples
= (amt
.lSampleSize
!= 0);
730 /* FIXME: Is this right? */
731 if (!amt
.lSampleSize
)
737 amt
.bTemporalCompression
= IsEqualGUID(&amt
.majortype
, &MEDIATYPE_Video
); /* FIXME? */
738 dwSampleSize
= pStrHdr
->dwSampleSize
;
739 dwLength
= pStrHdr
->dwLength
;
741 dwLength
= This
->AviHeader
.dwTotalFrames
;
743 if (pStrHdr
->dwSuggestedBufferSize
&& pStrHdr
->dwSuggestedBufferSize
> props
->cbBuffer
)
744 props
->cbBuffer
= pStrHdr
->dwSuggestedBufferSize
;
748 case ckidSTREAMFORMAT
:
749 TRACE("processing stream format data\n");
750 if (IsEqualIID(&amt
.formattype
, &FORMAT_VideoInfo
))
752 VIDEOINFOHEADER
* pvi
;
753 /* biCompression member appears to override the value in the stream header.
754 * i.e. the stream header can say something completely contradictory to what
755 * is in the BITMAPINFOHEADER! */
756 if (pChunk
->cb
< sizeof(BITMAPINFOHEADER
))
758 ERR("Not enough bytes for BITMAPINFOHEADER\n");
761 amt
.cbFormat
= sizeof(VIDEOINFOHEADER
) - sizeof(BITMAPINFOHEADER
) + pChunk
->cb
;
762 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
763 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
764 pvi
= (VIDEOINFOHEADER
*)amt
.pbFormat
;
765 pvi
->AvgTimePerFrame
= (LONGLONG
)(10000000.0 / fSamplesPerSec
);
767 CopyMemory(&pvi
->bmiHeader
, pChunk
+ 1, pChunk
->cb
);
768 if (pvi
->bmiHeader
.biCompression
)
769 amt
.subtype
.Data1
= pvi
->bmiHeader
.biCompression
;
771 else if (IsEqualIID(&amt
.formattype
, &FORMAT_WaveFormatEx
))
773 amt
.cbFormat
= pChunk
->cb
;
774 if (amt
.cbFormat
< sizeof(WAVEFORMATEX
))
775 amt
.cbFormat
= sizeof(WAVEFORMATEX
);
776 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
777 ZeroMemory(amt
.pbFormat
, amt
.cbFormat
);
778 CopyMemory(amt
.pbFormat
, pChunk
+ 1, pChunk
->cb
);
782 amt
.cbFormat
= pChunk
->cb
;
783 amt
.pbFormat
= CoTaskMemAlloc(amt
.cbFormat
);
784 CopyMemory(amt
.pbFormat
, pChunk
+ 1, amt
.cbFormat
);
788 TRACE("processing stream name\n");
789 /* FIXME: this doesn't exactly match native version (we omit the "##)" prefix), but hey... */
790 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)(pChunk
+ 1), pChunk
->cb
, piOutput
.achName
, sizeof(piOutput
.achName
) / sizeof(piOutput
.achName
[0]));
792 case ckidSTREAMHANDLERDATA
:
793 FIXME("process stream handler data\n");
796 TRACE("JUNK chunk ignored\n");
798 case ckidAVISUPERINDEX
:
800 const AVISUPERINDEX
*pIndex
= (const AVISUPERINDEX
*)pChunk
;
802 UINT rest
= pIndex
->cb
- sizeof(AVISUPERINDEX
) + sizeof(RIFFCHUNK
) + sizeof(pIndex
->aIndex
[0]) * ANYSIZE_ARRAY
;
804 if (pIndex
->cb
< sizeof(AVISUPERINDEX
) - sizeof(RIFFCHUNK
))
806 FIXME("size %u\n", pIndex
->cb
);
812 ERR("Stream %d got more than 1 superindex?\n", This
->Parser
.cStreams
);
816 TRACE("wLongsPerEntry: %hd\n", pIndex
->wLongsPerEntry
);
817 TRACE("bIndexSubType: %u\n", pIndex
->bIndexSubType
);
818 TRACE("bIndexType: %u\n", pIndex
->bIndexType
);
819 TRACE("nEntriesInUse: %u\n", pIndex
->nEntriesInUse
);
820 TRACE("dwChunkId: %.4s\n", (const char *)&pIndex
->dwChunkId
);
821 if (pIndex
->dwReserved
[0])
822 TRACE("dwReserved[0]: %u\n", pIndex
->dwReserved
[0]);
823 if (pIndex
->dwReserved
[1])
824 TRACE("dwReserved[1]: %u\n", pIndex
->dwReserved
[1]);
825 if (pIndex
->dwReserved
[2])
826 TRACE("dwReserved[2]: %u\n", pIndex
->dwReserved
[2]);
828 if (pIndex
->bIndexType
!= AVI_INDEX_OF_INDEXES
829 || pIndex
->wLongsPerEntry
!= 4
830 || rest
< (pIndex
->nEntriesInUse
* sizeof(DWORD
) * pIndex
->wLongsPerEntry
)
831 || (pIndex
->bIndexSubType
!= AVI_INDEX_SUB_2FIELD
&& pIndex
->bIndexSubType
!= AVI_INDEX_SUB_DEFAULT
))
833 FIXME("Invalid index chunk encountered\n");
837 stream
->entries
= pIndex
->nEntriesInUse
;
838 stream
->stdindex
= CoTaskMemRealloc(stream
->stdindex
, sizeof(*stream
->stdindex
) * stream
->entries
);
839 for (x
= 0; x
< pIndex
->nEntriesInUse
; ++x
)
841 TRACE("qwOffset: %x%08x\n", (DWORD
)(pIndex
->aIndex
[x
].qwOffset
>> 32), (DWORD
)pIndex
->aIndex
[x
].qwOffset
);
842 TRACE("dwSize: %u\n", pIndex
->aIndex
[x
].dwSize
);
843 TRACE("dwDuration: %u (unreliable)\n", pIndex
->aIndex
[x
].dwDuration
);
845 AVISplitter_ProcessIndex(This
, &stream
->stdindex
[x
], pIndex
->aIndex
[x
].qwOffset
, pIndex
->aIndex
[x
].dwSize
);
850 FIXME("unknown chunk type \"%.04s\" ignored\n", (LPCSTR
)&pChunk
->fcc
);
854 if (IsEqualGUID(&amt
.formattype
, &FORMAT_WaveFormatEx
))
856 amt
.subtype
= MEDIATYPE_Video
;
857 amt
.subtype
.Data1
= ((WAVEFORMATEX
*)amt
.pbFormat
)->wFormatTag
;
860 dump_AM_MEDIA_TYPE(&amt
);
861 TRACE("fSamplesPerSec = %f\n", (double)fSamplesPerSec
);
862 TRACE("dwSampleSize = %x\n", dwSampleSize
);
863 TRACE("dwLength = %x\n", dwLength
);
865 stream
->fSamplesPerSec
= fSamplesPerSec
;
866 stream
->dwSampleSize
= dwSampleSize
;
867 stream
->dwLength
= dwLength
; /* TODO: Use this for mediaseeking */
868 stream
->packet_queued
= CreateEventW(NULL
, 0, 0, NULL
);
870 hr
= Parser_AddPin(&(This
->Parser
), &piOutput
, props
, &amt
);
871 CoTaskMemFree(amt
.pbFormat
);
877 static HRESULT
AVISplitter_ProcessODML(AVISplitterImpl
* This
, const BYTE
* pData
, DWORD cb
)
879 const RIFFCHUNK
* pChunk
;
881 for (pChunk
= (const RIFFCHUNK
*)pData
;
882 ((const BYTE
*)pChunk
>= pData
) && ((const BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) < pData
+ cb
) && (pChunk
->cb
> 0);
883 pChunk
= (const RIFFCHUNK
*)((const BYTE
*)pChunk
+ sizeof(RIFFCHUNK
) + pChunk
->cb
)
888 case ckidAVIEXTHEADER
:
891 const AVIEXTHEADER
* pExtHdr
= (const AVIEXTHEADER
*)pChunk
;
893 TRACE("processing extension header\n");
894 if (pExtHdr
->cb
!= sizeof(AVIEXTHEADER
) - sizeof(RIFFCHUNK
))
896 FIXME("Size: %u\n", pExtHdr
->cb
);
899 TRACE("dwGrandFrames: %u\n", pExtHdr
->dwGrandFrames
);
900 for (x
= 0; x
< 61; ++x
)
901 if (pExtHdr
->dwFuture
[x
])
902 FIXME("dwFuture[%i] = %u (0x%08x)\n", x
, pExtHdr
->dwFuture
[x
], pExtHdr
->dwFuture
[x
]);
903 This
->ExtHeader
= *pExtHdr
;
907 FIXME("unknown chunk type \"%.04s\" ignored\n", (LPCSTR
)&pChunk
->fcc
);
914 static HRESULT
AVISplitter_InitializeStreams(AVISplitterImpl
*This
)
922 for (x
= 0; x
< This
->Parser
.cStreams
; ++x
)
924 This
->streams
[x
].frames
= 0;
925 This
->streams
[x
].pos
= ~0;
926 This
->streams
[x
].index
= 0;
929 nMax
= This
->oldindex
->cb
/ sizeof(This
->oldindex
->aIndex
[0]);
931 /* Ok, maybe this is more of an exercise to see if I interpret everything correctly or not, but that is useful for now. */
932 for (n
= 0; n
< nMax
; ++n
)
934 DWORD streamId
= StreamFromFOURCC(This
->oldindex
->aIndex
[n
].dwChunkId
);
935 if (streamId
>= This
->Parser
.cStreams
)
937 FIXME("Stream id %s ignored\n", debugstr_an((char*)&This
->oldindex
->aIndex
[n
].dwChunkId
, 4));
940 if (This
->streams
[streamId
].pos
== ~0U)
941 This
->streams
[streamId
].pos
= n
;
943 if (This
->streams
[streamId
].streamheader
.dwSampleSize
)
944 This
->streams
[streamId
].frames
+= This
->oldindex
->aIndex
[n
].dwSize
/ This
->streams
[streamId
].streamheader
.dwSampleSize
;
946 ++This
->streams
[streamId
].frames
;
949 for (x
= 0; x
< This
->Parser
.cStreams
; ++x
)
951 if ((DWORD
)This
->streams
[x
].frames
!= This
->streams
[x
].streamheader
.dwLength
)
953 FIXME("stream %u: frames found: %u, frames meant to be found: %u\n", x
, (DWORD
)This
->streams
[x
].frames
, This
->streams
[x
].streamheader
.dwLength
);
958 else if (!This
->streams
[0].entries
)
960 for (x
= 0; x
< This
->Parser
.cStreams
; ++x
)
962 This
->streams
[x
].frames
= This
->streams
[x
].streamheader
.dwLength
;
964 /* MS Avi splitter does seek through the whole file, we should! */
965 ERR("We should be manually seeking through the entire file to build an index, because the index is missing!!!\n");
969 /* Not much here yet */
970 for (x
= 0; x
< This
->Parser
.cStreams
; ++x
)
972 StreamData
*stream
= This
->streams
+ x
;
978 if (stream
->stdindex
)
982 for (y
= 0; y
< stream
->entries
; ++y
)
984 if (stream
->streamheader
.dwSampleSize
)
988 for (z
= 0; z
< stream
->stdindex
[y
]->nEntriesInUse
; ++z
)
990 UINT len
= stream
->stdindex
[y
]->aIndex
[z
].dwSize
& ~(1 << 31);
991 frames
+= len
/ stream
->streamheader
.dwSampleSize
+ !!(len
% stream
->streamheader
.dwSampleSize
);
995 frames
+= stream
->stdindex
[y
]->nEntriesInUse
;
998 else frames
= stream
->frames
;
1000 frames
*= stream
->streamheader
.dwScale
;
1001 /* Keep accuracy as high as possible for duration */
1002 This
->Parser
.sourceSeeking
.llDuration
= frames
* 10000000;
1003 This
->Parser
.sourceSeeking
.llDuration
/= stream
->streamheader
.dwRate
;
1004 This
->Parser
.sourceSeeking
.llStop
= This
->Parser
.sourceSeeking
.llDuration
;
1005 This
->Parser
.sourceSeeking
.llCurrent
= 0;
1007 frames
/= stream
->streamheader
.dwRate
;
1009 TRACE("Duration: %d days, %d hours, %d minutes and %d.%03u seconds\n", (DWORD
)(frames
/ 86400),
1010 (DWORD
)((frames
% 86400) / 3600), (DWORD
)((frames
% 3600) / 60), (DWORD
)(frames
% 60),
1011 (DWORD
)(This
->Parser
.sourceSeeking
.llDuration
/10000) % 1000);
1017 static HRESULT
AVISplitter_Disconnect(LPVOID iface
);
1019 /* FIXME: fix leaks on failure here */
1020 static HRESULT
AVISplitter_InputPin_PreConnect(IPin
* iface
, IPin
* pConnectPin
, ALLOCATOR_PROPERTIES
*props
)
1022 PullPin
*This
= impl_PullPin_from_IPin(iface
);
1025 LONGLONG pos
= 0; /* in bytes */
1027 RIFFCHUNK
* pCurrentChunk
;
1028 LONGLONG total
, avail
;
1032 AVISplitterImpl
* pAviSplit
= (AVISplitterImpl
*)This
->pin
.pinInfo
.pFilter
;
1034 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
1035 pos
+= sizeof(list
);
1037 if (list
.fcc
!= FOURCC_RIFF
)
1039 ERR("Input stream not a RIFF file\n");
1042 if (list
.fccListType
!= formtypeAVI
)
1044 ERR("Input stream not an AVI RIFF file\n");
1048 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
1049 if (list
.fcc
!= FOURCC_LIST
)
1051 ERR("Expected LIST chunk, but got %.04s\n", (LPSTR
)&list
.fcc
);
1054 if (list
.fccListType
!= listtypeAVIHEADER
)
1056 ERR("Header list expected. Got: %.04s\n", (LPSTR
)&list
.fccListType
);
1060 pBuffer
= HeapAlloc(GetProcessHeap(), 0, list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
));
1061 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
+ sizeof(list
), list
.cb
- sizeof(RIFFLIST
) + sizeof(RIFFCHUNK
), pBuffer
);
1063 pAviSplit
->AviHeader
.cb
= 0;
1065 /* Stream list will set the buffer size here, so set a default and allow an override */
1066 props
->cbBuffer
= 0x20000;
1068 for (pCurrentChunk
= (RIFFCHUNK
*)pBuffer
; (BYTE
*)pCurrentChunk
+ sizeof(*pCurrentChunk
) < pBuffer
+ list
.cb
; pCurrentChunk
= (RIFFCHUNK
*)(((BYTE
*)pCurrentChunk
) + sizeof(*pCurrentChunk
) + pCurrentChunk
->cb
))
1072 switch (pCurrentChunk
->fcc
)
1074 case ckidMAINAVIHEADER
:
1075 /* AVIMAINHEADER includes the structure that is pCurrentChunk at the moment */
1076 memcpy(&pAviSplit
->AviHeader
, pCurrentChunk
, sizeof(pAviSplit
->AviHeader
));
1079 pList
= (RIFFLIST
*)pCurrentChunk
;
1080 switch (pList
->fccListType
)
1082 case ckidSTREAMLIST
:
1083 hr
= AVISplitter_ProcessStreamList(pAviSplit
, (BYTE
*)pCurrentChunk
+ sizeof(RIFFLIST
), pCurrentChunk
->cb
+ sizeof(RIFFCHUNK
) - sizeof(RIFFLIST
), props
);
1086 hr
= AVISplitter_ProcessODML(pAviSplit
, (BYTE
*)pCurrentChunk
+ sizeof(RIFFLIST
), pCurrentChunk
->cb
+ sizeof(RIFFCHUNK
) - sizeof(RIFFLIST
));
1090 case ckidAVIPADDING
:
1094 FIXME("unrecognised header list type: %.04s\n", (LPSTR
)&pCurrentChunk
->fcc
);
1097 HeapFree(GetProcessHeap(), 0, pBuffer
);
1099 if (pAviSplit
->AviHeader
.cb
!= sizeof(pAviSplit
->AviHeader
) - sizeof(RIFFCHUNK
))
1101 ERR("Avi Header wrong size!\n");
1105 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
1106 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
1108 while (list
.fcc
== ckidAVIPADDING
|| (list
.fcc
== FOURCC_LIST
&& list
.fccListType
!= listtypeAVIMOVIE
))
1110 pos
+= sizeof(RIFFCHUNK
) + list
.cb
;
1112 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
1115 if (list
.fcc
!= FOURCC_LIST
)
1117 ERR("Expected LIST, but got %.04s\n", (LPSTR
)&list
.fcc
);
1120 if (list
.fccListType
!= listtypeAVIMOVIE
)
1122 ERR("Expected AVI movie list, but got %.04s\n", (LPSTR
)&list
.fccListType
);
1126 IAsyncReader_Length(This
->pReader
, &total
, &avail
);
1128 /* FIXME: AVIX files are extended beyond the FOURCC chunk "AVI ", and thus won't be played here,
1129 * once I get one of the files I'll try to fix it */
1132 This
->rtStart
= pAviSplit
->CurrentChunkOffset
= MEDIATIME_FROM_BYTES(pos
+ sizeof(RIFFLIST
));
1133 pos
+= list
.cb
+ sizeof(RIFFCHUNK
);
1135 pAviSplit
->EndOfFile
= This
->rtStop
= MEDIATIME_FROM_BYTES(pos
);
1138 ERR("File smaller (%x%08x) then EndOfFile (%x%08x)\n", (DWORD
)(total
>> 32), (DWORD
)total
, (DWORD
)(pAviSplit
->EndOfFile
>> 32), (DWORD
)pAviSplit
->EndOfFile
);
1142 hr
= IAsyncReader_SyncRead(This
->pReader
, BYTES_FROM_MEDIATIME(pAviSplit
->CurrentChunkOffset
), sizeof(pAviSplit
->CurrentChunk
), (BYTE
*)&pAviSplit
->CurrentChunk
);
1146 props
->cbPrefix
= 0;
1147 /* Comrades, prevent shortage of buffers, or you will feel the consequences! DA! */
1148 props
->cBuffers
= 2 * pAviSplit
->Parser
.cStreams
;
1150 /* Now peek into the idx1 index, if available */
1151 if (hr
== S_OK
&& (total
- pos
) > sizeof(RIFFCHUNK
))
1153 memset(&list
, 0, sizeof(list
));
1155 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(list
), (BYTE
*)&list
);
1156 if (list
.fcc
== ckidAVIOLDINDEX
)
1158 pAviSplit
->oldindex
= CoTaskMemRealloc(pAviSplit
->oldindex
, list
.cb
+ sizeof(RIFFCHUNK
));
1159 if (pAviSplit
->oldindex
)
1161 hr
= IAsyncReader_SyncRead(This
->pReader
, pos
, sizeof(RIFFCHUNK
) + list
.cb
, (BYTE
*)pAviSplit
->oldindex
);
1164 hr
= AVISplitter_ProcessOldIndex(pAviSplit
);
1168 CoTaskMemFree(pAviSplit
->oldindex
);
1169 pAviSplit
->oldindex
= NULL
;
1177 for (x
= 0; x
< pAviSplit
->Parser
.cStreams
; ++x
)
1178 if (pAviSplit
->streams
[x
].entries
)
1183 CoTaskMemFree(pAviSplit
->oldindex
);
1184 pAviSplit
->oldindex
= NULL
;
1185 if (indexes
< pAviSplit
->Parser
.cStreams
)
1187 /* This error could possible be survived by switching to old type index,
1188 * but I would rather find out why it doesn't find everything here
1190 ERR("%d indexes expected, but only have %d\n", indexes
, pAviSplit
->Parser
.cStreams
);
1194 else if (!indexes
&& pAviSplit
->oldindex
)
1195 indexes
= pAviSplit
->Parser
.cStreams
;
1197 if (!indexes
&& pAviSplit
->AviHeader
.dwFlags
& AVIF_MUSTUSEINDEX
)
1199 FIXME("No usable index was found!\n");
1203 /* Now, set up the streams */
1205 hr
= AVISplitter_InitializeStreams(pAviSplit
);
1209 AVISplitter_Disconnect(pAviSplit
);
1213 TRACE("AVI File ok\n");
1218 static HRESULT
AVISplitter_Flush(LPVOID iface
)
1220 AVISplitterImpl
*This
= iface
;
1223 TRACE("(%p)->()\n", This
);
1225 for (x
= 0; x
< This
->Parser
.cStreams
; ++x
)
1227 StreamData
*stream
= This
->streams
+ x
;
1230 assert(IMediaSample_Release(stream
->sample
) == 0);
1231 stream
->sample
= NULL
;
1233 ResetEvent(stream
->packet_queued
);
1234 assert(!stream
->thread
);
1240 static HRESULT
AVISplitter_Disconnect(LPVOID iface
)
1242 AVISplitterImpl
*This
= iface
;
1245 /* TODO: Remove other memory that's allocated during connect */
1246 CoTaskMemFree(This
->oldindex
);
1247 This
->oldindex
= NULL
;
1249 for (x
= 0; x
< This
->Parser
.cStreams
; ++x
)
1253 StreamData
*stream
= &This
->streams
[x
];
1255 for (i
= 0; i
< stream
->entries
; ++i
)
1256 CoTaskMemFree(stream
->stdindex
[i
]);
1258 CoTaskMemFree(stream
->stdindex
);
1259 CloseHandle(stream
->packet_queued
);
1261 CoTaskMemFree(This
->streams
);
1262 This
->streams
= NULL
;
1266 static ULONG WINAPI
AVISplitter_Release(IBaseFilter
*iface
)
1268 AVISplitterImpl
*This
= (AVISplitterImpl
*)iface
;
1271 ref
= InterlockedDecrement(&This
->Parser
.filter
.refCount
);
1273 TRACE("(%p)->() Release from %d\n", This
, ref
+ 1);
1277 AVISplitter_Flush(This
);
1278 Parser_Destroy(&This
->Parser
);
1284 static HRESULT WINAPI
AVISplitter_seek(IMediaSeeking
*iface
)
1286 AVISplitterImpl
*This
= impl_from_IMediaSeeking(iface
);
1287 PullPin
*pPin
= This
->Parser
.pInputPin
;
1288 LONGLONG newpos
, endpos
;
1291 newpos
= This
->Parser
.sourceSeeking
.llCurrent
;
1292 endpos
= This
->Parser
.sourceSeeking
.llDuration
;
1294 if (newpos
> endpos
)
1296 WARN("Requesting position %x%08x beyond end of stream %x%08x\n", (DWORD
)(newpos
>>32), (DWORD
)newpos
, (DWORD
)(endpos
>>32), (DWORD
)endpos
);
1297 return E_INVALIDARG
;
1300 FIXME("Moving position to %u.%03u s!\n", (DWORD
)(newpos
/ 10000000), (DWORD
)((newpos
/ 10000)%1000));
1302 EnterCriticalSection(&pPin
->thread_lock
);
1303 /* Send a flush to all output pins */
1304 IPin_BeginFlush(&pPin
->pin
.IPin_iface
);
1306 /* Make sure this is done while stopped, BeginFlush takes care of this */
1307 EnterCriticalSection(&This
->Parser
.filter
.csFilter
);
1308 for (x
= 0; x
< This
->Parser
.cStreams
; ++x
)
1310 Parser_OutputPin
*pin
= unsafe_impl_Parser_OutputPin_from_IPin(This
->Parser
.ppPins
[1+x
]);
1311 StreamData
*stream
= This
->streams
+ x
;
1312 LONGLONG wanted_frames
;
1313 DWORD last_keyframe
= 0, last_keyframeidx
= 0, preroll
= 0;
1315 wanted_frames
= newpos
;
1316 wanted_frames
*= stream
->streamheader
.dwRate
;
1317 wanted_frames
/= 10000000;
1318 wanted_frames
/= stream
->streamheader
.dwScale
;
1320 pin
->dwSamplesProcessed
= 0;
1323 stream
->seek
= TRUE
;
1324 if (stream
->stdindex
)
1328 for (y
= 0; y
< stream
->entries
; ++y
)
1330 for (z
= 0; z
< stream
->stdindex
[y
]->nEntriesInUse
; ++z
)
1332 if (stream
->streamheader
.dwSampleSize
)
1334 ULONG len
= stream
->stdindex
[y
]->aIndex
[z
].dwSize
& ~(1 << 31);
1335 ULONG size
= stream
->streamheader
.dwSampleSize
;
1337 pin
->dwSamplesProcessed
+= len
/ size
;
1339 ++pin
->dwSamplesProcessed
;
1341 else ++pin
->dwSamplesProcessed
;
1343 if (!(stream
->stdindex
[y
]->aIndex
[z
].dwSize
>> 31))
1346 last_keyframeidx
= y
;
1352 if (pin
->dwSamplesProcessed
>= wanted_frames
)
1355 if (pin
->dwSamplesProcessed
>= wanted_frames
)
1358 stream
->index
= last_keyframeidx
;
1359 stream
->pos
= last_keyframe
;
1364 nMax
= This
->oldindex
->cb
/ sizeof(This
->oldindex
->aIndex
[0]);
1366 for (n
= 0; n
< nMax
; ++n
)
1368 DWORD streamId
= StreamFromFOURCC(This
->oldindex
->aIndex
[n
].dwChunkId
);
1372 if (stream
->streamheader
.dwSampleSize
)
1374 ULONG len
= This
->oldindex
->aIndex
[n
].dwSize
;
1375 ULONG size
= stream
->streamheader
.dwSampleSize
;
1377 pin
->dwSamplesProcessed
+= len
/ size
;
1379 ++pin
->dwSamplesProcessed
;
1381 else ++pin
->dwSamplesProcessed
;
1383 if (This
->oldindex
->aIndex
[n
].dwFlags
& AVIIF_KEYFRAME
)
1391 if (pin
->dwSamplesProcessed
>= wanted_frames
)
1395 stream
->pos
= last_keyframe
;
1398 stream
->preroll
= preroll
;
1399 stream
->seek
= TRUE
;
1401 LeaveCriticalSection(&This
->Parser
.filter
.csFilter
);
1403 TRACE("Done flushing\n");
1404 IPin_EndFlush(&pPin
->pin
.IPin_iface
);
1405 LeaveCriticalSection(&pPin
->thread_lock
);
1410 static const IBaseFilterVtbl AVISplitterImpl_Vtbl
=
1412 Parser_QueryInterface
,
1414 AVISplitter_Release
,
1420 Parser_SetSyncSource
,
1421 Parser_GetSyncSource
,
1424 Parser_QueryFilterInfo
,
1425 Parser_JoinFilterGraph
,
1426 Parser_QueryVendorInfo
1429 HRESULT
AVISplitter_create(IUnknown
* pUnkOuter
, LPVOID
* ppv
)
1432 AVISplitterImpl
* This
;
1434 TRACE("(%p, %p)\n", pUnkOuter
, ppv
);
1439 return CLASS_E_NOAGGREGATION
;
1441 /* Note: This memory is managed by the transform filter once created */
1442 This
= CoTaskMemAlloc(sizeof(AVISplitterImpl
));
1444 This
->streams
= NULL
;
1445 This
->oldindex
= NULL
;
1447 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
);