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