[PATCH 5/5] winegstreamer: Reimplement the AVI splitter on top of the avidemux plugin.
Zebediah Figura
z.figura12 at gmail.com
Thu Sep 19 18:59:38 CDT 2019
Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
dlls/quartz/Makefile.in | 1 -
dlls/quartz/avisplit.c | 1444 ------------------------------
dlls/quartz/main.c | 1 -
dlls/quartz/quartz_private.h | 1 -
dlls/quartz/quartz_strmif.idl | 7 -
dlls/quartz/regsvr.c | 17 -
dlls/quartz/tests/avisplit.c | 13 +-
dlls/winegstreamer/gst_private.h | 1 +
dlls/winegstreamer/gstdemux.c | 112 ++-
dlls/winegstreamer/main.c | 43 +
10 files changed, 166 insertions(+), 1474 deletions(-)
delete mode 100644 dlls/quartz/avisplit.c
diff --git a/dlls/quartz/Makefile.in b/dlls/quartz/Makefile.in
index 7963e212fc5..689783e98c6 100644
--- a/dlls/quartz/Makefile.in
+++ b/dlls/quartz/Makefile.in
@@ -7,7 +7,6 @@ EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
acmwrapper.c \
avidec.c \
- avisplit.c \
dsoundrender.c \
enummedia.c \
enummoniker.c \
diff --git a/dlls/quartz/avisplit.c b/dlls/quartz/avisplit.c
deleted file mode 100644
index 15627eeb08c..00000000000
--- a/dlls/quartz/avisplit.c
+++ /dev/null
@@ -1,1444 +0,0 @@
-/*
- * AVI Splitter Filter
- *
- * Copyright 2003 Robert Shearman
- * Copyright 2004-2005 Christian Costa
- * Copyright 2008 Maarten Lankhorst
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-/* FIXME:
- * - Reference leaks, if they still exist
- * - Files without an index are not handled correctly yet.
- * - When stopping/starting, a sample is lost. This should be compensated by
- * keeping track of previous index/position.
- * - Debugging channels are noisy at the moment, especially with thread
- * related messages, however this is the only correct thing to do right now,
- * since wine doesn't correctly handle all messages yet.
- */
-
-#include "quartz_private.h"
-#include "pin.h"
-
-#include "uuids.h"
-#include "vfw.h"
-#include "aviriff.h"
-#include "vfwmsgs.h"
-#include "amvideo.h"
-
-#include "wine/debug.h"
-
-#include <math.h>
-#include <assert.h>
-
-#include "parser.h"
-
-#define TWOCCFromFOURCC(fcc) HIWORD(fcc)
-
-/* four character codes used in AVI files */
-#define ckidINFO mmioFOURCC('I','N','F','O')
-#define ckidREC mmioFOURCC('R','E','C',' ')
-
-WINE_DEFAULT_DEBUG_CHANNEL(quartz);
-
-typedef struct StreamData
-{
- DWORD dwSampleSize;
- FLOAT fSamplesPerSec;
- DWORD dwLength;
-
- AVISTREAMHEADER streamheader;
- DWORD entries;
- AVISTDINDEX **stdindex;
- DWORD frames;
- BOOL seek;
-
- /* Position, in index units */
- DWORD pos, pos_next, index, index_next;
-
- /* Packet handling: a thread is created and waits on the packet event handle
- * On an event acquire the sample lock, addref the sample and set it to NULL,
- * then queue a new packet.
- */
- HANDLE thread, packet_queued;
- IMediaSample *sample;
-
- /* Amount of preroll samples for this stream */
- DWORD preroll;
-} StreamData;
-
-typedef struct AVISplitterImpl
-{
- ParserImpl Parser;
- RIFFCHUNK CurrentChunk;
- LONGLONG CurrentChunkOffset; /* in media time */
- LONGLONG EndOfFile;
- AVIMAINHEADER AviHeader;
- AVIEXTHEADER ExtHeader;
-
- AVIOLDINDEX *oldindex;
- DWORD offset;
-
- StreamData *streams;
-} AVISplitterImpl;
-
-struct thread_args {
- AVISplitterImpl *This;
- DWORD stream;
-};
-
-static inline AVISplitterImpl *impl_from_IMediaSeeking( IMediaSeeking *iface )
-{
- return CONTAINING_RECORD(iface, AVISplitterImpl, Parser.sourceSeeking.IMediaSeeking_iface);
-}
-
-static inline AVISplitterImpl *impl_from_IBaseFilter(IBaseFilter *iface)
-{
- return CONTAINING_RECORD(iface, AVISplitterImpl, Parser.filter.IBaseFilter_iface);
-}
-
-/* The threading stuff cries for an explanation
- *
- * PullPin starts processing and calls AVISplitter_first_request
- * AVISplitter_first_request creates a thread for each stream
- * A stream can be audio, video, subtitles or something undefined.
- *
- * AVISplitter_first_request loads a single packet to each but one stream,
- * and queues it for that last stream. This is to prevent WaitForNext to time
- * out badly.
- *
- * The processing loop is entered. It calls IAsyncReader_WaitForNext in the
- * PullPin. Every time it receives a packet, it will call AVISplitter_Sample
- * AVISplitter_Sample will signal the relevant thread that a new sample is
- * arrived, when that thread is ready it will read the packet and transmits
- * it downstream with AVISplitter_Receive
- *
- * Threads terminate upon receiving NULL as packet or when ANY error code
- * != S_OK occurs. This means that any error is fatal to processing.
- */
-
-static HRESULT AVISplitter_SendEndOfFile(AVISplitterImpl *filter, DWORD index)
-{
- IPin *peer;
-
- TRACE("End of file reached\n");
-
- if ((peer = filter->Parser.sources[index]->pin.pin.pConnectedTo))
- IPin_EndOfStream(peer);
-
- /* Force the pullpin thread to stop */
- return S_FALSE;
-}
-
-/* Thread worker horse */
-static HRESULT AVISplitter_next_request(AVISplitterImpl *This, DWORD streamnumber)
-{
- StreamData *stream = This->streams + streamnumber;
- PullPin *pin = This->Parser.pInputPin;
- IMediaSample *sample = NULL;
- HRESULT hr;
- ULONG ref;
-
- TRACE("(%p, %u)->()\n", This, streamnumber);
-
- hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0);
- if (hr != S_OK)
- ERR("... %08x?\n", hr);
-
- if (SUCCEEDED(hr))
- {
- LONGLONG rtSampleStart;
- /* Add 4 for the next header, which should hopefully work */
- LONGLONG rtSampleStop;
-
- stream->pos = stream->pos_next;
- stream->index = stream->index_next;
-
- IMediaSample_SetDiscontinuity(sample, stream->seek);
- stream->seek = FALSE;
- if (stream->preroll)
- {
- --stream->preroll;
- IMediaSample_SetPreroll(sample, TRUE);
- }
- else
- IMediaSample_SetPreroll(sample, FALSE);
- IMediaSample_SetSyncPoint(sample, TRUE);
-
- if (stream->stdindex)
- {
- AVISTDINDEX *index = stream->stdindex[stream->index];
- AVISTDINDEX_ENTRY *entry = &index->aIndex[stream->pos];
-
- /* End of file */
- if (stream->index >= stream->entries)
- {
- TRACE("END OF STREAM ON %u\n", streamnumber);
- IMediaSample_Release(sample);
- return S_FALSE;
- }
-
- rtSampleStart = index->qwBaseOffset;
- rtSampleStart += entry->dwOffset;
- rtSampleStart = MEDIATIME_FROM_BYTES(rtSampleStart);
-
- ++stream->pos_next;
- if (index->nEntriesInUse == stream->pos_next)
- {
- stream->pos_next = 0;
- ++stream->index_next;
- }
-
- rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(entry->dwSize & ~(1u << 31));
-
- TRACE("offset(%u) size(%u)\n", (DWORD)BYTES_FROM_MEDIATIME(rtSampleStart), (DWORD)BYTES_FROM_MEDIATIME(rtSampleStop - rtSampleStart));
- }
- else if (This->oldindex)
- {
- DWORD flags = This->oldindex->aIndex[stream->pos].dwFlags;
- DWORD size = This->oldindex->aIndex[stream->pos].dwSize;
-
- /* End of file */
- if (stream->index)
- {
- TRACE("END OF STREAM ON %u\n", streamnumber);
- IMediaSample_Release(sample);
- return S_FALSE;
- }
-
- rtSampleStart = MEDIATIME_FROM_BYTES(This->offset);
- rtSampleStart += MEDIATIME_FROM_BYTES(This->oldindex->aIndex[stream->pos].dwOffset);
- rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(size);
- if (flags & AVIIF_MIDPART)
- {
- FIXME("Only stand alone frames are currently handled correctly!\n");
- }
- if (flags & AVIIF_LIST)
- {
- FIXME("Not sure if this is handled correctly\n");
- rtSampleStart += MEDIATIME_FROM_BYTES(sizeof(RIFFLIST));
- rtSampleStop += MEDIATIME_FROM_BYTES(sizeof(RIFFLIST));
- }
- else
- {
- rtSampleStart += MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK));
- rtSampleStop += MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK));
- }
-
- /* Slow way of finding next index */
- do {
- stream->pos_next++;
- } while (stream->pos_next * sizeof(This->oldindex->aIndex[0]) < This->oldindex->cb
- && StreamFromFOURCC(This->oldindex->aIndex[stream->pos_next].dwChunkId) != streamnumber);
-
- /* End of file soon */
- if (stream->pos_next * sizeof(This->oldindex->aIndex[0]) >= This->oldindex->cb)
- {
- stream->pos_next = 0;
- ++stream->index_next;
- }
- }
- else /* TODO: Generate an index automagically */
- {
- ERR("CAN'T PLAY WITHOUT AN INDEX! SOS! SOS! SOS!\n");
- assert(0);
- }
-
- if (rtSampleStart != rtSampleStop)
- {
- IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop);
- hr = IAsyncReader_Request(pin->pReader, sample, streamnumber);
-
- if (FAILED(hr))
- {
- ref = IMediaSample_Release(sample);
- assert(ref == 0);
- }
- }
- else
- {
- stream->sample = sample;
- IMediaSample_SetActualDataLength(sample, 0);
- SetEvent(stream->packet_queued);
- }
- }
- else
- {
- if (sample)
- {
- ERR("There should be no sample!\n");
- ref = IMediaSample_Release(sample);
- assert(ref == 0);
- }
- }
- TRACE("--> %08x\n", hr);
-
- return hr;
-}
-
-static HRESULT AVISplitter_Receive(AVISplitterImpl *This, IMediaSample *sample, DWORD streamnumber)
-{
- Parser_OutputPin *pin = This->Parser.sources[streamnumber];
- HRESULT hr;
- LONGLONG start, stop, rtstart, rtstop;
- StreamData *stream = &This->streams[streamnumber];
-
- start = pin->dwSamplesProcessed;
- start *= stream->streamheader.dwScale;
- start *= 10000000;
- start /= stream->streamheader.dwRate;
-
- if (stream->streamheader.dwSampleSize)
- {
- ULONG len = IMediaSample_GetActualDataLength(sample);
- ULONG size = stream->streamheader.dwSampleSize;
-
- pin->dwSamplesProcessed += len / size;
- }
- else
- ++pin->dwSamplesProcessed;
-
- stop = pin->dwSamplesProcessed;
- stop *= stream->streamheader.dwScale;
- stop *= 10000000;
- stop /= stream->streamheader.dwRate;
-
- if (IMediaSample_IsDiscontinuity(sample) == S_OK) {
- IPin *victim;
- EnterCriticalSection(&This->Parser.filter.csFilter);
- pin->pin.pin.tStart = start;
- pin->pin.pin.dRate = This->Parser.sourceSeeking.dRate;
- hr = IPin_ConnectedTo(&pin->pin.pin.IPin_iface, &victim);
- if (hr == S_OK)
- {
- hr = IPin_NewSegment(victim, start, This->Parser.sourceSeeking.llStop,
- This->Parser.sourceSeeking.dRate);
- if (hr != S_OK)
- FIXME("NewSegment returns %08x\n", hr);
- IPin_Release(victim);
- }
- LeaveCriticalSection(&This->Parser.filter.csFilter);
- if (hr != S_OK)
- return hr;
- }
- rtstart = (double)(start - pin->pin.pin.tStart) / pin->pin.pin.dRate;
- rtstop = (double)(stop - pin->pin.pin.tStart) / pin->pin.pin.dRate;
- IMediaSample_SetMediaTime(sample, &start, &stop);
- IMediaSample_SetTime(sample, &rtstart, &rtstop);
- IMediaSample_SetMediaTime(sample, &start, &stop);
-
- hr = BaseOutputPinImpl_Deliver(&pin->pin, sample);
-
-/* Uncomment this if you want to debug the time differences between the
- * different streams, it is useful for that
- *
- FIXME("stream %u, hr: %08x, Start: %u.%03u, Stop: %u.%03u\n", streamnumber, hr,
- (DWORD)(start / 10000000), (DWORD)((start / 10000)%1000),
- (DWORD)(stop / 10000000), (DWORD)((stop / 10000)%1000));
-*/
- return hr;
-}
-
-static DWORD WINAPI AVISplitter_thread_reader(LPVOID data)
-{
- struct thread_args *args = data;
- AVISplitterImpl *This = args->This;
- DWORD streamnumber = args->stream;
- HRESULT hr = S_OK;
-
- do
- {
- HRESULT nexthr = S_FALSE;
- IMediaSample *sample;
-
- WaitForSingleObject(This->streams[streamnumber].packet_queued, INFINITE);
- sample = This->streams[streamnumber].sample;
- This->streams[streamnumber].sample = NULL;
- if (!sample)
- break;
-
- nexthr = AVISplitter_next_request(This, streamnumber);
-
- hr = AVISplitter_Receive(This, sample, streamnumber);
- if (hr != S_OK)
- FIXME("Receiving error: %08x\n", hr);
-
- IMediaSample_Release(sample);
- if (hr == S_OK)
- hr = nexthr;
- if (nexthr == S_FALSE)
- AVISplitter_SendEndOfFile(This, streamnumber);
- } while (hr == S_OK);
-
- if (hr != S_FALSE)
- FIXME("Thread %u terminated with hr %08x!\n", streamnumber, hr);
- else
- TRACE("Thread %u terminated properly\n", streamnumber);
- return hr;
-}
-
-static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample, DWORD_PTR cookie)
-{
- AVISplitterImpl *This = iface;
- StreamData *stream = This->streams + cookie;
- HRESULT hr = S_OK;
-
- if (!IMediaSample_GetActualDataLength(pSample))
- {
- ERR("Received empty sample\n");
- return S_OK;
- }
-
- /* Send the sample to whatever thread is appropriate
- * That thread should also not have a sample queued at the moment
- */
- /* Debugging */
- TRACE("(%p)->(%p size: %u, %lu)\n", This, pSample, IMediaSample_GetActualDataLength(pSample), cookie);
- assert(cookie < This->Parser.cStreams);
- assert(!stream->sample);
- assert(WaitForSingleObject(stream->packet_queued, 0) == WAIT_TIMEOUT);
-
- IMediaSample_AddRef(pSample);
-
- stream->sample = pSample;
- SetEvent(stream->packet_queued);
-
- return hr;
-}
-
-static HRESULT AVISplitter_done_process(LPVOID iface);
-
-/* On the first request we have to be sure that (cStreams-1) samples have
- * already been processed, because otherwise some pins might not ever finish
- * a Pause state change
- */
-static HRESULT AVISplitter_first_request(LPVOID iface)
-{
- AVISplitterImpl *This = iface;
- HRESULT hr = S_OK;
- DWORD x;
- IMediaSample *sample = NULL;
- BOOL have_sample = FALSE;
-
- TRACE("(%p)->()\n", This);
-
- for (x = 0; x < This->Parser.cStreams; ++x)
- {
- StreamData *stream = This->streams + x;
-
- /* Nothing should be running at this point */
- assert(!stream->thread);
-
- assert(!sample);
- /* It could be we asked the thread to terminate, and the thread
- * already terminated before receiving the deathwish */
- ResetEvent(stream->packet_queued);
-
- stream->pos_next = stream->pos;
- stream->index_next = stream->index;
-
- /* This was sent after stopped->paused or stopped->playing, so set seek */
- stream->seek = TRUE;
-
- /* There should be a packet queued from AVISplitter_next_request last time
- * It needs to be done now because this is the only way to ensure that every
- * stream will have at least 1 packet processed
- * If this is done after the threads start it could go all awkward and we
- * would have no guarantees that it's successful at all
- */
-
- if (have_sample)
- {
- DWORD_PTR dwUser = ~0;
- hr = IAsyncReader_WaitForNext(This->Parser.pInputPin->pReader, 10000, &sample, &dwUser);
- assert(hr == S_OK);
- assert(sample);
-
- AVISplitter_Sample(iface, sample, dwUser);
- IMediaSample_Release(sample);
- }
-
- hr = AVISplitter_next_request(This, x);
- TRACE("-->%08x\n", hr);
-
- /* Could be an EOF instead */
- have_sample = (hr == S_OK);
- if (hr == S_FALSE)
- AVISplitter_SendEndOfFile(This, x);
-
- if (FAILED(hr) && hr != VFW_E_NOT_CONNECTED)
- break;
- hr = S_OK;
- }
-
- /* FIXME: Don't do this for each pin that sent an EOF */
- for (x = 0; x < This->Parser.cStreams && SUCCEEDED(hr); ++x)
- {
- struct thread_args *args;
- DWORD tid;
-
- if ((This->streams[x].stdindex && This->streams[x].index_next >= This->streams[x].entries) ||
- (!This->streams[x].stdindex && This->streams[x].index_next))
- {
- This->streams[x].thread = NULL;
- continue;
- }
-
- args = CoTaskMemAlloc(sizeof(*args));
- args->This = This;
- args->stream = x;
- This->streams[x].thread = CreateThread(NULL, 0, AVISplitter_thread_reader, args, 0, &tid);
- TRACE("Created stream %u thread 0x%08x\n", x, tid);
- }
-
- if (FAILED(hr))
- ERR("Horsemen of the apocalypse came to bring error 0x%08x\n", hr);
-
- return hr;
-}
-
-static HRESULT AVISplitter_done_process(LPVOID iface)
-{
- AVISplitterImpl *This = iface;
- DWORD x;
- ULONG ref;
-
- for (x = 0; x < This->Parser.cStreams; ++x)
- {
- StreamData *stream = This->streams + x;
-
- TRACE("Waiting for %u to terminate\n", x);
- /* Make the thread return first */
- SetEvent(stream->packet_queued);
- assert(WaitForSingleObject(stream->thread, 100000) != WAIT_TIMEOUT);
- CloseHandle(stream->thread);
- stream->thread = NULL;
-
- if (stream->sample)
- {
- ref = IMediaSample_Release(stream->sample);
- assert(ref == 0);
- }
- stream->sample = NULL;
-
- ResetEvent(stream->packet_queued);
- }
- TRACE("All threads are now terminated\n");
-
- return S_OK;
-}
-
-static HRESULT AVISplitter_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
-{
- if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream) && IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_Avi))
- return S_OK;
- return S_FALSE;
-}
-
-static HRESULT AVISplitter_ProcessIndex(AVISplitterImpl *This, AVISTDINDEX **index, LONGLONG qwOffset, DWORD cb)
-{
- AVISTDINDEX *pIndex;
- DWORD x;
- int rest;
-
- *index = NULL;
- if (cb < sizeof(AVISTDINDEX))
- {
- FIXME("size %u too small\n", cb);
- return E_INVALIDARG;
- }
-
- pIndex = CoTaskMemAlloc(cb);
- if (!pIndex)
- return E_OUTOFMEMORY;
-
- IAsyncReader_SyncRead(This->Parser.pInputPin->pReader, qwOffset, cb, (BYTE *)pIndex);
- rest = cb - sizeof(AVISUPERINDEX) + sizeof(RIFFCHUNK) + sizeof(pIndex->aIndex);
-
- TRACE("FOURCC: %s\n", debugstr_an((char *)&pIndex->fcc, 4));
- TRACE("wLongsPerEntry: %hd\n", pIndex->wLongsPerEntry);
- TRACE("bIndexSubType: %u\n", pIndex->bIndexSubType);
- TRACE("bIndexType: %u\n", pIndex->bIndexType);
- TRACE("nEntriesInUse: %u\n", pIndex->nEntriesInUse);
- TRACE("dwChunkId: %.4s\n", (char *)&pIndex->dwChunkId);
- TRACE("qwBaseOffset: %s\n", wine_dbgstr_longlong(pIndex->qwBaseOffset));
- TRACE("dwReserved_3: %u\n", pIndex->dwReserved_3);
-
- if (pIndex->bIndexType != AVI_INDEX_OF_CHUNKS
- || pIndex->wLongsPerEntry != 2
- || rest < (pIndex->nEntriesInUse * sizeof(DWORD) * pIndex->wLongsPerEntry)
- || (pIndex->bIndexSubType != AVI_INDEX_SUB_DEFAULT))
- {
- FIXME("Invalid index chunk encountered: %u/%u, %u/%u, %u/%u, %u/%u\n",
- pIndex->bIndexType, AVI_INDEX_OF_CHUNKS, pIndex->wLongsPerEntry, 2,
- rest, (DWORD)(pIndex->nEntriesInUse * sizeof(DWORD) * pIndex->wLongsPerEntry),
- pIndex->bIndexSubType, AVI_INDEX_SUB_DEFAULT);
- *index = NULL;
- return E_INVALIDARG;
- }
-
- for (x = 0; x < pIndex->nEntriesInUse; ++x)
- {
- BOOL keyframe = !(pIndex->aIndex[x].dwSize >> 31);
- DWORDLONG offset = pIndex->qwBaseOffset + pIndex->aIndex[x].dwOffset;
- TRACE("dwOffset: %s\n", wine_dbgstr_longlong(offset));
- TRACE("dwSize: %u\n", (pIndex->aIndex[x].dwSize & ~(1u << 31)));
- TRACE("Frame is a keyframe: %s\n", keyframe ? "yes" : "no");
- }
-
- *index = pIndex;
- return S_OK;
-}
-
-static HRESULT AVISplitter_ProcessOldIndex(AVISplitterImpl *This)
-{
- ULONGLONG mov_pos = BYTES_FROM_MEDIATIME(This->CurrentChunkOffset) - sizeof(DWORD);
- AVIOLDINDEX *pAviOldIndex = This->oldindex;
- int relative = -1;
- DWORD x;
-
- for (x = 0; x < pAviOldIndex->cb / sizeof(pAviOldIndex->aIndex[0]); ++x)
- {
- DWORD temp, temp2 = 0, offset, chunkid;
- PullPin *pin = This->Parser.pInputPin;
-
- offset = pAviOldIndex->aIndex[x].dwOffset;
- chunkid = pAviOldIndex->aIndex[x].dwChunkId;
-
- TRACE("dwChunkId: %.4s\n", (char *)&chunkid);
- TRACE("dwFlags: %08x\n", pAviOldIndex->aIndex[x].dwFlags);
- TRACE("dwOffset (%s): %08x\n", relative ? "relative" : "absolute", offset);
- TRACE("dwSize: %08x\n", pAviOldIndex->aIndex[x].dwSize);
-
- /* Only scan once, or else this will take too long */
- if (relative == -1)
- {
- IAsyncReader_SyncRead(pin->pReader, offset, sizeof(DWORD), (BYTE *)&temp);
- relative = (chunkid != temp);
-
- if (chunkid == mmioFOURCC('7','F','x','x')
- && ((char *)&temp)[0] == 'i' && ((char *)&temp)[1] == 'x')
- relative = FALSE;
-
- if (relative)
- {
- if (offset + mov_pos < BYTES_FROM_MEDIATIME(This->EndOfFile))
- IAsyncReader_SyncRead(pin->pReader, offset + mov_pos, sizeof(DWORD), (BYTE *)&temp2);
-
- if (chunkid == mmioFOURCC('7','F','x','x')
- && ((char *)&temp2)[0] == 'i' && ((char *)&temp2)[1] == 'x')
- {
- /* Do nothing, all is great */
- }
- else if (temp2 != chunkid)
- {
- ERR("Faulty index or bug in handling: Wanted FCC: %s, Abs FCC: %s (@ %x), Rel FCC: %s (@ %s)\n",
- debugstr_an((char *)&chunkid, 4), debugstr_an((char *)&temp, 4), offset,
- debugstr_an((char *)&temp2, 4), wine_dbgstr_longlong(mov_pos + offset));
- relative = -1;
- }
- else
- TRACE("Scanned dwChunkId: %s\n", debugstr_an((char *)&temp2, 4));
- }
- else if (!relative)
- TRACE("Scanned dwChunkId: %s\n", debugstr_an((char *)&temp, 4));
- }
- /* Only dump one packet */
- else break;
- }
-
- if (relative == -1)
- {
- FIXME("Dropping index: no idea whether it is relative or absolute\n");
- CoTaskMemFree(This->oldindex);
- This->oldindex = NULL;
- }
- else if (!relative)
- This->offset = 0;
- else
- This->offset = (DWORD)mov_pos;
-
- return S_OK;
-}
-
-static HRESULT AVISplitter_ProcessStreamList(AVISplitterImpl * This, const BYTE * pData, DWORD cb, ALLOCATOR_PROPERTIES *props)
-{
- const RIFFCHUNK * pChunk;
- HRESULT hr;
- AM_MEDIA_TYPE amt;
- float fSamplesPerSec = 0.0f;
- DWORD dwSampleSize = 0;
- DWORD dwLength = 0;
- DWORD nstdindex = 0;
- static const WCHAR wszStreamTemplate[] = {'S','t','r','e','a','m',' ','%','0','2','d',0};
- StreamData *stream;
- WCHAR name[18];
-
- ZeroMemory(&amt, sizeof(amt));
- This->streams = CoTaskMemRealloc(This->streams, sizeof(StreamData) * (This->Parser.cStreams+1));
- stream = This->streams + This->Parser.cStreams;
- ZeroMemory(stream, sizeof(*stream));
-
- for (pChunk = (const RIFFCHUNK *)pData;
- ((const BYTE *)pChunk >= pData) && ((const BYTE *)pChunk + sizeof(RIFFCHUNK) < pData + cb) && (pChunk->cb > 0);
- pChunk = (const RIFFCHUNK *)((const BYTE*)pChunk + sizeof(RIFFCHUNK) + pChunk->cb)
- )
- {
- switch (pChunk->fcc)
- {
- case ckidSTREAMHEADER:
- {
- const AVISTREAMHEADER * pStrHdr = (const AVISTREAMHEADER *)pChunk;
- TRACE("processing stream header\n");
- stream->streamheader = *pStrHdr;
-
- fSamplesPerSec = (float)pStrHdr->dwRate / (float)pStrHdr->dwScale;
- CoTaskMemFree(amt.pbFormat);
- amt.pbFormat = NULL;
- amt.cbFormat = 0;
-
- switch (pStrHdr->fccType)
- {
- case streamtypeVIDEO:
- amt.formattype = FORMAT_VideoInfo;
- break;
- case streamtypeAUDIO:
- amt.formattype = FORMAT_WaveFormatEx;
- break;
- default:
- FIXME("fccType %.4s not handled yet\n", (const char *)&pStrHdr->fccType);
- amt.formattype = FORMAT_None;
- }
- amt.majortype = MEDIATYPE_Video;
- amt.majortype.Data1 = pStrHdr->fccType;
- amt.subtype = MEDIATYPE_Video;
- amt.subtype.Data1 = pStrHdr->fccHandler;
- TRACE("Subtype FCC: %.04s\n", (LPCSTR)&pStrHdr->fccHandler);
- amt.lSampleSize = pStrHdr->dwSampleSize;
- amt.bFixedSizeSamples = (amt.lSampleSize != 0);
-
- /* FIXME: Is this right? */
- if (!amt.lSampleSize)
- {
- amt.lSampleSize = 1;
- dwSampleSize = 1;
- }
-
- amt.bTemporalCompression = IsEqualGUID(&amt.majortype, &MEDIATYPE_Video); /* FIXME? */
- dwSampleSize = pStrHdr->dwSampleSize;
- dwLength = pStrHdr->dwLength;
- if (!dwLength)
- dwLength = This->AviHeader.dwTotalFrames;
-
- if (pStrHdr->dwSuggestedBufferSize && pStrHdr->dwSuggestedBufferSize > props->cbBuffer)
- props->cbBuffer = pStrHdr->dwSuggestedBufferSize;
-
- break;
- }
- case ckidSTREAMFORMAT:
- TRACE("processing stream format data\n");
- if (IsEqualIID(&amt.formattype, &FORMAT_VideoInfo))
- {
- VIDEOINFOHEADER * pvi;
- /* biCompression member appears to override the value in the stream header.
- * i.e. the stream header can say something completely contradictory to what
- * is in the BITMAPINFOHEADER! */
- if (pChunk->cb < sizeof(BITMAPINFOHEADER))
- {
- ERR("Not enough bytes for BITMAPINFOHEADER\n");
- return E_FAIL;
- }
- amt.cbFormat = sizeof(VIDEOINFOHEADER) - sizeof(BITMAPINFOHEADER) + pChunk->cb;
- amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
- ZeroMemory(amt.pbFormat, amt.cbFormat);
- pvi = (VIDEOINFOHEADER *)amt.pbFormat;
- pvi->AvgTimePerFrame = (LONGLONG)(10000000.0 / fSamplesPerSec);
-
- CopyMemory(&pvi->bmiHeader, pChunk + 1, pChunk->cb);
- if (pvi->bmiHeader.biCompression)
- amt.subtype.Data1 = pvi->bmiHeader.biCompression;
- }
- else if (IsEqualIID(&amt.formattype, &FORMAT_WaveFormatEx))
- {
- amt.cbFormat = pChunk->cb;
- if (amt.cbFormat < sizeof(WAVEFORMATEX))
- amt.cbFormat = sizeof(WAVEFORMATEX);
- amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
- ZeroMemory(amt.pbFormat, amt.cbFormat);
- CopyMemory(amt.pbFormat, pChunk + 1, pChunk->cb);
- }
- else
- {
- amt.cbFormat = pChunk->cb;
- amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
- CopyMemory(amt.pbFormat, pChunk + 1, amt.cbFormat);
- }
- break;
- case ckidSTREAMNAME:
- TRACE("processing stream name\n");
- /* FIXME: this doesn't exactly match native version (we omit the "##)" prefix), but hey... */
- MultiByteToWideChar(CP_ACP, 0, (LPCSTR)(pChunk + 1), pChunk->cb, name, ARRAY_SIZE(name));
- break;
- case ckidSTREAMHANDLERDATA:
- FIXME("process stream handler data\n");
- break;
- case ckidAVIPADDING:
- TRACE("JUNK chunk ignored\n");
- break;
- case ckidAVISUPERINDEX:
- {
- const AVISUPERINDEX *pIndex = (const AVISUPERINDEX *)pChunk;
- DWORD x;
- UINT rest = pIndex->cb - sizeof(AVISUPERINDEX) + sizeof(RIFFCHUNK) + sizeof(pIndex->aIndex[0]) * ANYSIZE_ARRAY;
-
- if (pIndex->cb < sizeof(AVISUPERINDEX) - sizeof(RIFFCHUNK))
- {
- FIXME("size %u\n", pIndex->cb);
- break;
- }
-
- if (nstdindex++ > 0)
- {
- ERR("Stream %d got more than 1 superindex?\n", This->Parser.cStreams);
- break;
- }
-
- TRACE("wLongsPerEntry: %hd\n", pIndex->wLongsPerEntry);
- TRACE("bIndexSubType: %u\n", pIndex->bIndexSubType);
- TRACE("bIndexType: %u\n", pIndex->bIndexType);
- TRACE("nEntriesInUse: %u\n", pIndex->nEntriesInUse);
- TRACE("dwChunkId: %.4s\n", (const char *)&pIndex->dwChunkId);
- if (pIndex->dwReserved[0])
- TRACE("dwReserved[0]: %u\n", pIndex->dwReserved[0]);
- if (pIndex->dwReserved[1])
- TRACE("dwReserved[1]: %u\n", pIndex->dwReserved[1]);
- if (pIndex->dwReserved[2])
- TRACE("dwReserved[2]: %u\n", pIndex->dwReserved[2]);
-
- if (pIndex->bIndexType != AVI_INDEX_OF_INDEXES
- || pIndex->wLongsPerEntry != 4
- || rest < (pIndex->nEntriesInUse * sizeof(DWORD) * pIndex->wLongsPerEntry)
- || (pIndex->bIndexSubType != AVI_INDEX_SUB_2FIELD && pIndex->bIndexSubType != AVI_INDEX_SUB_DEFAULT))
- {
- FIXME("Invalid index chunk encountered\n");
- break;
- }
-
- stream->entries = pIndex->nEntriesInUse;
- stream->stdindex = CoTaskMemRealloc(stream->stdindex, sizeof(*stream->stdindex) * stream->entries);
- for (x = 0; x < pIndex->nEntriesInUse; ++x)
- {
- TRACE("qwOffset: %s\n", wine_dbgstr_longlong(pIndex->aIndex[x].qwOffset));
- TRACE("dwSize: %u\n", pIndex->aIndex[x].dwSize);
- TRACE("dwDuration: %u (unreliable)\n", pIndex->aIndex[x].dwDuration);
-
- AVISplitter_ProcessIndex(This, &stream->stdindex[x], pIndex->aIndex[x].qwOffset, pIndex->aIndex[x].dwSize);
- }
- break;
- }
- default:
- FIXME("unknown chunk type \"%.04s\" ignored\n", (LPCSTR)&pChunk->fcc);
- }
- }
-
- if (IsEqualGUID(&amt.formattype, &FORMAT_WaveFormatEx))
- {
- amt.subtype = MEDIATYPE_Video;
- amt.subtype.Data1 = ((WAVEFORMATEX *)amt.pbFormat)->wFormatTag;
- }
-
- dump_AM_MEDIA_TYPE(&amt);
- TRACE("fSamplesPerSec = %f\n", (double)fSamplesPerSec);
- TRACE("dwSampleSize = %x\n", dwSampleSize);
- TRACE("dwLength = %x\n", dwLength);
-
- stream->fSamplesPerSec = fSamplesPerSec;
- stream->dwSampleSize = dwSampleSize;
- stream->dwLength = dwLength; /* TODO: Use this for mediaseeking */
- stream->packet_queued = CreateEventW(NULL, 0, 0, NULL);
-
- swprintf(name, ARRAY_SIZE(name), wszStreamTemplate, This->Parser.cStreams);
- hr = Parser_AddPin(&This->Parser, name, props, &amt);
- CoTaskMemFree(amt.pbFormat);
-
-
- return hr;
-}
-
-static HRESULT AVISplitter_ProcessODML(AVISplitterImpl * This, const BYTE * pData, DWORD cb)
-{
- const RIFFCHUNK * pChunk;
-
- for (pChunk = (const RIFFCHUNK *)pData;
- ((const BYTE *)pChunk >= pData) && ((const BYTE *)pChunk + sizeof(RIFFCHUNK) < pData + cb) && (pChunk->cb > 0);
- pChunk = (const RIFFCHUNK *)((const BYTE*)pChunk + sizeof(RIFFCHUNK) + pChunk->cb)
- )
- {
- switch (pChunk->fcc)
- {
- case ckidAVIEXTHEADER:
- {
- int x;
- const AVIEXTHEADER * pExtHdr = (const AVIEXTHEADER *)pChunk;
-
- TRACE("processing extension header\n");
- if (pExtHdr->cb != sizeof(AVIEXTHEADER) - sizeof(RIFFCHUNK))
- {
- FIXME("Size: %u\n", pExtHdr->cb);
- break;
- }
- TRACE("dwGrandFrames: %u\n", pExtHdr->dwGrandFrames);
- for (x = 0; x < 61; ++x)
- if (pExtHdr->dwFuture[x])
- FIXME("dwFuture[%i] = %u (0x%08x)\n", x, pExtHdr->dwFuture[x], pExtHdr->dwFuture[x]);
- This->ExtHeader = *pExtHdr;
- break;
- }
- default:
- FIXME("unknown chunk type \"%.04s\" ignored\n", (LPCSTR)&pChunk->fcc);
- }
- }
-
- return S_OK;
-}
-
-static HRESULT AVISplitter_InitializeStreams(AVISplitterImpl *This)
-{
- unsigned int x;
-
- if (This->oldindex)
- {
- DWORD nMax, n;
-
- for (x = 0; x < This->Parser.cStreams; ++x)
- {
- This->streams[x].frames = 0;
- This->streams[x].pos = ~0;
- This->streams[x].index = 0;
- }
-
- nMax = This->oldindex->cb / sizeof(This->oldindex->aIndex[0]);
-
- /* Ok, maybe this is more of an exercise to see if I interpret everything correctly or not, but that is useful for now. */
- for (n = 0; n < nMax; ++n)
- {
- DWORD streamId = StreamFromFOURCC(This->oldindex->aIndex[n].dwChunkId);
- if (streamId >= This->Parser.cStreams)
- {
- FIXME("Stream id %s ignored\n", debugstr_an((char*)&This->oldindex->aIndex[n].dwChunkId, 4));
- continue;
- }
- if (This->streams[streamId].pos == ~0U)
- This->streams[streamId].pos = n;
-
- if (This->streams[streamId].streamheader.dwSampleSize)
- This->streams[streamId].frames += This->oldindex->aIndex[n].dwSize / This->streams[streamId].streamheader.dwSampleSize;
- else
- ++This->streams[streamId].frames;
- }
-
- for (x = 0; x < This->Parser.cStreams; ++x)
- {
- if (This->streams[x].frames != This->streams[x].streamheader.dwLength)
- FIXME("stream %u: frames found: %u, frames meant to be found: %u\n", x, This->streams[x].frames, This->streams[x].streamheader.dwLength);
- }
-
- }
- else if (!This->streams[0].entries)
- {
- for (x = 0; x < This->Parser.cStreams; ++x)
- {
- This->streams[x].frames = This->streams[x].streamheader.dwLength;
- }
- /* MS Avi splitter does seek through the whole file, we should! */
- ERR("We should be manually seeking through the entire file to build an index, because the index is missing!!!\n");
- return E_NOTIMPL;
- }
-
- /* Not much here yet */
- for (x = 0; x < This->Parser.cStreams; ++x)
- {
- StreamData *stream = This->streams + x;
- DWORD y;
- DWORD64 frames = 0;
-
- stream->seek = TRUE;
-
- if (stream->stdindex)
- {
- stream->index = 0;
- stream->pos = 0;
- for (y = 0; y < stream->entries; ++y)
- {
- if (stream->streamheader.dwSampleSize)
- {
- DWORD z;
-
- for (z = 0; z < stream->stdindex[y]->nEntriesInUse; ++z)
- {
- UINT len = stream->stdindex[y]->aIndex[z].dwSize & ~(1u << 31);
- frames += len / stream->streamheader.dwSampleSize + !!(len % stream->streamheader.dwSampleSize);
- }
- }
- else
- frames += stream->stdindex[y]->nEntriesInUse;
- }
- }
- else frames = stream->frames;
-
- frames *= stream->streamheader.dwScale;
- /* Keep accuracy as high as possible for duration */
- This->Parser.sourceSeeking.llDuration = frames * 10000000;
- This->Parser.sourceSeeking.llDuration /= stream->streamheader.dwRate;
- This->Parser.sourceSeeking.llStop = This->Parser.sourceSeeking.llDuration;
- This->Parser.sourceSeeking.llCurrent = 0;
-
- frames /= stream->streamheader.dwRate;
-
- TRACE("Duration: %d days, %d hours, %d minutes and %d.%03u seconds\n", (DWORD)(frames / 86400),
- (DWORD)((frames % 86400) / 3600), (DWORD)((frames % 3600) / 60), (DWORD)(frames % 60),
- (DWORD)(This->Parser.sourceSeeking.llDuration/10000) % 1000);
- }
-
- return S_OK;
-}
-
-static HRESULT AVISplitter_Disconnect(LPVOID iface);
-
-/* FIXME: fix leaks on failure here */
-static HRESULT AVISplitter_InputPin_PreConnect(IPin * iface, IPin * pConnectPin, ALLOCATOR_PROPERTIES *props)
-{
- PullPin *This = impl_PullPin_from_IPin(iface);
- AVISplitterImpl *pAviSplit = impl_from_IBaseFilter(&This->pin.filter->IBaseFilter_iface);
- HRESULT hr;
- RIFFLIST list;
- LONGLONG pos = 0; /* in bytes */
- BYTE * pBuffer;
- RIFFCHUNK * pCurrentChunk;
- LONGLONG total, avail;
- ULONG x;
- DWORD indexes;
-
- hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
- pos += sizeof(list);
-
- if (list.fcc != FOURCC_RIFF)
- {
- ERR("Input stream not a RIFF file\n");
- return E_FAIL;
- }
- if (list.fccListType != formtypeAVI)
- {
- ERR("Input stream not an AVI RIFF file\n");
- return E_FAIL;
- }
-
- hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
- if (list.fcc != FOURCC_LIST)
- {
- ERR("Expected LIST chunk, but got %.04s\n", (LPSTR)&list.fcc);
- return E_FAIL;
- }
- if (list.fccListType != listtypeAVIHEADER)
- {
- ERR("Header list expected. Got: %.04s\n", (LPSTR)&list.fccListType);
- return E_FAIL;
- }
-
- pBuffer = HeapAlloc(GetProcessHeap(), 0, list.cb - sizeof(RIFFLIST) + sizeof(RIFFCHUNK));
- hr = IAsyncReader_SyncRead(This->pReader, pos + sizeof(list), list.cb - sizeof(RIFFLIST) + sizeof(RIFFCHUNK), pBuffer);
-
- pAviSplit->AviHeader.cb = 0;
-
- /* Stream list will set the buffer size here, so set a default and allow an override */
- props->cbBuffer = 0x20000;
-
- for (pCurrentChunk = (RIFFCHUNK *)pBuffer; (BYTE *)pCurrentChunk + sizeof(*pCurrentChunk) < pBuffer + list.cb; pCurrentChunk = (RIFFCHUNK *)(((BYTE *)pCurrentChunk) + sizeof(*pCurrentChunk) + pCurrentChunk->cb))
- {
- RIFFLIST * pList;
-
- switch (pCurrentChunk->fcc)
- {
- case ckidMAINAVIHEADER:
- /* AVIMAINHEADER includes the structure that is pCurrentChunk at the moment */
- memcpy(&pAviSplit->AviHeader, pCurrentChunk, sizeof(pAviSplit->AviHeader));
- break;
- case FOURCC_LIST:
- pList = (RIFFLIST *)pCurrentChunk;
- switch (pList->fccListType)
- {
- case ckidSTREAMLIST:
- hr = AVISplitter_ProcessStreamList(pAviSplit, (BYTE *)pCurrentChunk + sizeof(RIFFLIST), pCurrentChunk->cb + sizeof(RIFFCHUNK) - sizeof(RIFFLIST), props);
- break;
- case ckidODML:
- hr = AVISplitter_ProcessODML(pAviSplit, (BYTE *)pCurrentChunk + sizeof(RIFFLIST), pCurrentChunk->cb + sizeof(RIFFCHUNK) - sizeof(RIFFLIST));
- break;
- }
- break;
- case ckidAVIPADDING:
- /* ignore */
- break;
- default:
- FIXME("unrecognised header list type: %.04s\n", (LPSTR)&pCurrentChunk->fcc);
- }
- }
- HeapFree(GetProcessHeap(), 0, pBuffer);
-
- if (pAviSplit->AviHeader.cb != sizeof(pAviSplit->AviHeader) - sizeof(RIFFCHUNK))
- {
- ERR("Avi Header wrong size!\n");
- return E_FAIL;
- }
-
- /* Skip any chunks until we find the LIST chunk */
- do
- {
- pos += sizeof(RIFFCHUNK) + list.cb;
- hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
- }
- while (hr == S_OK && (list.fcc != FOURCC_LIST || list.fccListType != listtypeAVIMOVIE));
-
- if (hr != S_OK)
- {
- ERR("Failed to find LIST chunk from AVI file\n");
- return E_FAIL;
- }
-
- IAsyncReader_Length(This->pReader, &total, &avail);
-
- /* FIXME: AVIX files are extended beyond the FOURCC chunk "AVI ", and thus won't be played here,
- * once I get one of the files I'll try to fix it */
- This->rtStart = pAviSplit->CurrentChunkOffset = MEDIATIME_FROM_BYTES(pos + sizeof(RIFFLIST));
- pos += list.cb + sizeof(RIFFCHUNK);
-
- pAviSplit->EndOfFile = This->rtStop = MEDIATIME_FROM_BYTES(pos);
- if (pos > total)
- {
- ERR("File smaller (%s) then EndOfFile (%s)\n", wine_dbgstr_longlong(total), wine_dbgstr_longlong(pAviSplit->EndOfFile));
- return E_FAIL;
- }
-
- hr = IAsyncReader_SyncRead(This->pReader, BYTES_FROM_MEDIATIME(pAviSplit->CurrentChunkOffset), sizeof(pAviSplit->CurrentChunk), (BYTE *)&pAviSplit->CurrentChunk);
-
- props->cbAlign = 1;
- props->cbPrefix = 0;
- /* Comrades, prevent shortage of buffers, or you will feel the consequences! DA! */
- props->cBuffers = 2 * pAviSplit->Parser.cStreams;
-
- /* Now peek into the idx1 index, if available */
- if (hr == S_OK && (total - pos) > sizeof(RIFFCHUNK))
- {
- memset(&list, 0, sizeof(list));
-
- hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
- if (list.fcc == ckidAVIOLDINDEX)
- {
- pAviSplit->oldindex = CoTaskMemRealloc(pAviSplit->oldindex, list.cb + sizeof(RIFFCHUNK));
- if (pAviSplit->oldindex)
- {
- hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(RIFFCHUNK) + list.cb, (BYTE *)pAviSplit->oldindex);
- if (hr == S_OK)
- {
- hr = AVISplitter_ProcessOldIndex(pAviSplit);
- }
- else
- {
- CoTaskMemFree(pAviSplit->oldindex);
- pAviSplit->oldindex = NULL;
- hr = S_OK;
- }
- }
- }
- }
-
- indexes = 0;
- for (x = 0; x < pAviSplit->Parser.cStreams; ++x)
- if (pAviSplit->streams[x].entries)
- ++indexes;
-
- if (indexes)
- {
- CoTaskMemFree(pAviSplit->oldindex);
- pAviSplit->oldindex = NULL;
- if (indexes < pAviSplit->Parser.cStreams)
- {
- /* This error could possible be survived by switching to old type index,
- * but I would rather find out why it doesn't find everything here
- */
- ERR("%d indexes expected, but only have %d\n", indexes, pAviSplit->Parser.cStreams);
- indexes = 0;
- }
- }
- else if (pAviSplit->oldindex)
- indexes = pAviSplit->Parser.cStreams;
-
- if (!indexes && pAviSplit->AviHeader.dwFlags & AVIF_MUSTUSEINDEX)
- {
- FIXME("No usable index was found!\n");
- hr = E_FAIL;
- }
-
- /* Now, set up the streams */
- if (hr == S_OK)
- hr = AVISplitter_InitializeStreams(pAviSplit);
-
- if (hr != S_OK)
- {
- AVISplitter_Disconnect(pAviSplit);
- return E_FAIL;
- }
-
- TRACE("AVI File ok\n");
-
- return hr;
-}
-
-static HRESULT AVISplitter_Flush(LPVOID iface)
-{
- AVISplitterImpl *This = iface;
- DWORD x;
- ULONG ref;
-
- TRACE("(%p)->()\n", This);
-
- for (x = 0; x < This->Parser.cStreams; ++x)
- {
- StreamData *stream = This->streams + x;
-
- if (stream->sample)
- {
- ref = IMediaSample_Release(stream->sample);
- assert(ref == 0);
- }
- stream->sample = NULL;
-
- ResetEvent(stream->packet_queued);
- assert(!stream->thread);
- }
-
- return S_OK;
-}
-
-static HRESULT AVISplitter_Disconnect(LPVOID iface)
-{
- AVISplitterImpl *This = iface;
- ULONG x;
-
- /* TODO: Remove other memory that's allocated during connect */
- CoTaskMemFree(This->oldindex);
- This->oldindex = NULL;
-
- for (x = 0; x < This->Parser.cStreams; ++x)
- {
- DWORD i;
-
- StreamData *stream = &This->streams[x];
-
- for (i = 0; i < stream->entries; ++i)
- CoTaskMemFree(stream->stdindex[i]);
-
- CoTaskMemFree(stream->stdindex);
- CloseHandle(stream->packet_queued);
- }
- CoTaskMemFree(This->streams);
- This->streams = NULL;
- return S_OK;
-}
-
-static HRESULT WINAPI AVISplitter_seek(IMediaSeeking *iface)
-{
- AVISplitterImpl *This = impl_from_IMediaSeeking(iface);
- PullPin *pPin = This->Parser.pInputPin;
- LONGLONG newpos, endpos;
- DWORD x;
-
- newpos = This->Parser.sourceSeeking.llCurrent;
- endpos = This->Parser.sourceSeeking.llDuration;
-
- if (newpos > endpos)
- {
- WARN("Requesting position %x%08x beyond end of stream %x%08x\n", (DWORD)(newpos>>32), (DWORD)newpos, (DWORD)(endpos>>32), (DWORD)endpos);
- return E_INVALIDARG;
- }
-
- FIXME("Moving position to %u.%03u s!\n", (DWORD)(newpos / 10000000), (DWORD)((newpos / 10000)%1000));
-
- EnterCriticalSection(&pPin->thread_lock);
- /* Send a flush to all output pins */
- IPin_BeginFlush(&pPin->pin.IPin_iface);
-
- /* Make sure this is done while stopped, BeginFlush takes care of this */
- EnterCriticalSection(&This->Parser.filter.csFilter);
- for (x = 0; x < This->Parser.cStreams; ++x)
- {
- Parser_OutputPin *pin = This->Parser.sources[x];
- StreamData *stream = This->streams + x;
- LONGLONG wanted_frames;
- DWORD last_keyframe = 0, last_keyframeidx = 0, preroll = 0;
-
- wanted_frames = newpos;
- wanted_frames *= stream->streamheader.dwRate;
- wanted_frames /= 10000000;
- wanted_frames /= stream->streamheader.dwScale;
-
- pin->dwSamplesProcessed = 0;
- stream->index = 0;
- stream->pos = 0;
- stream->seek = TRUE;
- if (stream->stdindex)
- {
- DWORD y, z = 0;
-
- for (y = 0; y < stream->entries; ++y)
- {
- for (z = 0; z < stream->stdindex[y]->nEntriesInUse; ++z)
- {
- if (stream->streamheader.dwSampleSize)
- {
- ULONG len = stream->stdindex[y]->aIndex[z].dwSize & ~(1u << 31);
- ULONG size = stream->streamheader.dwSampleSize;
-
- pin->dwSamplesProcessed += len / size;
- if (len % size)
- ++pin->dwSamplesProcessed;
- }
- else ++pin->dwSamplesProcessed;
-
- if (!(stream->stdindex[y]->aIndex[z].dwSize >> 31))
- {
- last_keyframe = z;
- last_keyframeidx = y;
- preroll = 0;
- }
- else
- ++preroll;
-
- if (pin->dwSamplesProcessed >= wanted_frames)
- break;
- }
- if (pin->dwSamplesProcessed >= wanted_frames)
- break;
- }
- stream->index = last_keyframeidx;
- stream->pos = last_keyframe;
- }
- else
- {
- DWORD nMax, n;
- nMax = This->oldindex->cb / sizeof(This->oldindex->aIndex[0]);
-
- for (n = 0; n < nMax; ++n)
- {
- DWORD streamId = StreamFromFOURCC(This->oldindex->aIndex[n].dwChunkId);
- if (streamId != x)
- continue;
-
- if (stream->streamheader.dwSampleSize)
- {
- ULONG len = This->oldindex->aIndex[n].dwSize;
- ULONG size = stream->streamheader.dwSampleSize;
-
- pin->dwSamplesProcessed += len / size;
- if (len % size)
- ++pin->dwSamplesProcessed;
- }
- else ++pin->dwSamplesProcessed;
-
- if (This->oldindex->aIndex[n].dwFlags & AVIIF_KEYFRAME)
- {
- last_keyframe = n;
- preroll = 0;
- }
- else
- ++preroll;
-
- if (pin->dwSamplesProcessed >= wanted_frames)
- break;
- }
- assert(n < nMax);
- stream->pos = last_keyframe;
- stream->index = 0;
- }
- stream->preroll = preroll;
- stream->seek = TRUE;
- }
- LeaveCriticalSection(&This->Parser.filter.csFilter);
-
- TRACE("Done flushing\n");
- IPin_EndFlush(&pPin->pin.IPin_iface);
- LeaveCriticalSection(&pPin->thread_lock);
-
- return S_OK;
-}
-
-static const IBaseFilterVtbl AVISplitterImpl_Vtbl =
-{
- BaseFilterImpl_QueryInterface,
- BaseFilterImpl_AddRef,
- BaseFilterImpl_Release,
- BaseFilterImpl_GetClassID,
- Parser_Stop,
- Parser_Pause,
- Parser_Run,
- Parser_GetState,
- Parser_SetSyncSource,
- BaseFilterImpl_GetSyncSource,
- BaseFilterImpl_EnumPins,
- BaseFilterImpl_FindPin,
- BaseFilterImpl_QueryFilterInfo,
- BaseFilterImpl_JoinFilterGraph,
- BaseFilterImpl_QueryVendorInfo,
-};
-
-static void avi_splitter_destroy(struct strmbase_filter *iface)
-{
- AVISplitterImpl *filter = impl_from_IBaseFilter(&iface->IBaseFilter_iface);
- AVISplitter_Flush(filter);
- Parser_Destroy(&filter->Parser);
-}
-
-static const struct strmbase_filter_ops filter_ops =
-{
- .filter_get_pin = parser_get_pin,
- .filter_destroy = avi_splitter_destroy,
-};
-
-HRESULT AVISplitter_create(IUnknown *outer, void **out)
-{
- static const WCHAR sink_name[] = {'i','n','p','u','t',' ','p','i','n',0};
- HRESULT hr;
- AVISplitterImpl * This;
-
- *out = NULL;
-
- /* Note: This memory is managed by the transform filter once created */
- This = CoTaskMemAlloc(sizeof(AVISplitterImpl));
-
- This->streams = NULL;
- This->oldindex = NULL;
-
- hr = Parser_Create(&This->Parser, &AVISplitterImpl_Vtbl, outer, &CLSID_AviSplitter,
- &filter_ops, sink_name, AVISplitter_Sample, AVISplitter_QueryAccept,
- AVISplitter_InputPin_PreConnect, AVISplitter_Flush,
- AVISplitter_Disconnect, AVISplitter_first_request,
- AVISplitter_done_process, NULL, AVISplitter_seek, NULL);
-
- if (FAILED(hr))
- return hr;
-
- *out = &This->Parser.filter.IUnknown_inner;
-
- return hr;
-}
diff --git a/dlls/quartz/main.c b/dlls/quartz/main.c
index 115fc806fc7..fbb124a4a63 100644
--- a/dlls/quartz/main.c
+++ b/dlls/quartz/main.c
@@ -69,7 +69,6 @@ static const struct object_creation_info object_creation[] =
{ &CLSID_FilterMapper2, FilterMapper2_create },
{ &CLSID_AsyncReader, AsyncReader_create },
{ &CLSID_MemoryAllocator, StdMemAllocator_create },
- { &CLSID_AviSplitter, AVISplitter_create },
{ &CLSID_MPEG1Splitter, MPEGSplitter_create },
{ &CLSID_VideoRenderer, VideoRenderer_create },
{ &CLSID_VideoMixingRenderer, VMR7Impl_create },
diff --git a/dlls/quartz/quartz_private.h b/dlls/quartz/quartz_private.h
index d43e445bd04..4014b6fb0d9 100644
--- a/dlls/quartz/quartz_private.h
+++ b/dlls/quartz/quartz_private.h
@@ -55,7 +55,6 @@ HRESULT FilterMapper2_create(IUnknown *pUnkOuter, LPVOID *ppObj) DECLSPEC_HIDDEN
HRESULT FilterMapper_create(IUnknown *pUnkOuter, LPVOID *ppObj) DECLSPEC_HIDDEN;
HRESULT AsyncReader_create(IUnknown * pUnkOuter, LPVOID * ppv) DECLSPEC_HIDDEN;
HRESULT StdMemAllocator_create(IUnknown * pUnkOuter, LPVOID * ppv) DECLSPEC_HIDDEN;
-HRESULT AVISplitter_create(IUnknown * pUnkOuter, LPVOID * ppv) DECLSPEC_HIDDEN;
HRESULT MPEGSplitter_create(IUnknown * pUnkOuter, LPVOID * ppv) DECLSPEC_HIDDEN;
HRESULT AVIDec_create(IUnknown * pUnkOuter, LPVOID * ppv) DECLSPEC_HIDDEN;
HRESULT DSoundRender_create(IUnknown * pUnkOuter, LPVOID * ppv) DECLSPEC_HIDDEN;
diff --git a/dlls/quartz/quartz_strmif.idl b/dlls/quartz/quartz_strmif.idl
index c84e3aea5cb..0ecaeb08c54 100644
--- a/dlls/quartz/quartz_strmif.idl
+++ b/dlls/quartz/quartz_strmif.idl
@@ -84,13 +84,6 @@ coclass SeekingPassThru { interface ISeekingPassThru; }
]
coclass AsyncReader { interface IBaseFilter; }
-[
- helpstring("AVI Splitter"),
- threading(both),
- uuid(1b544c20-fd0b-11ce-8c63-00aa0044b51e)
-]
-coclass AviSplitter { interface IBaseFilter; }
-
[
helpstring("MPEG-I Stream Splitter"),
threading(both),
diff --git a/dlls/quartz/regsvr.c b/dlls/quartz/regsvr.c
index eebca1c00a6..07348936307 100644
--- a/dlls/quartz/regsvr.c
+++ b/dlls/quartz/regsvr.c
@@ -178,23 +178,6 @@ static HRESULT unregister_filters(struct regsvr_filter const *list)
*/
static struct regsvr_filter const filter_list[] = {
- { &CLSID_AviSplitter,
- &CLSID_LegacyAmFilterCategory,
- {'A','V','I',' ','S','p','l','i','t','t','e','r',0},
- 0x5ffff0,
- { { 0,
- { { &MEDIATYPE_Stream, &MEDIASUBTYPE_Avi },
- { NULL }
- },
- },
- { REG_PINFLAG_B_OUTPUT,
- { { &MEDIATYPE_Video, &GUID_NULL },
- { NULL }
- },
- },
- { 0xFFFFFFFF },
- }
- },
{ &CLSID_MPEG1Splitter,
&CLSID_LegacyAmFilterCategory,
{'M','P','E','G','-','I',' ','S','t','r','e','a','m',' ','S','p','l','i','t','t','e','r',0},
diff --git a/dlls/quartz/tests/avisplit.c b/dlls/quartz/tests/avisplit.c
index a1dadd481ad..ce0ea16a8e0 100644
--- a/dlls/quartz/tests/avisplit.c
+++ b/dlls/quartz/tests/avisplit.c
@@ -151,7 +151,7 @@ static void test_interfaces(void)
check_interface(pin, &IID_IKsPropertySet, FALSE);
check_interface(pin, &IID_IMemInputPin, FALSE);
check_interface(pin, &IID_IMediaPosition, FALSE);
- todo_wine check_interface(pin, &IID_IMediaSeeking, FALSE);
+ check_interface(pin, &IID_IMediaSeeking, FALSE);
IPin_Release(pin);
@@ -160,6 +160,7 @@ static void test_interfaces(void)
todo_wine check_interface(pin, &IID_IMediaPosition, TRUE);
check_interface(pin, &IID_IMediaSeeking, TRUE);
check_interface(pin, &IID_IPin, TRUE);
+ check_interface(pin, &IID_IQualityControl, TRUE);
check_interface(pin, &IID_IUnknown, TRUE);
check_interface(pin, &IID_IAsyncReader, FALSE);
@@ -1016,8 +1017,18 @@ fail:
START_TEST(avisplit)
{
+ IBaseFilter *filter;
+
CoInitialize(NULL);
+ if (FAILED(CoCreateInstance(&CLSID_AviSplitter, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IBaseFilter, (void **)&filter)))
+ {
+ skip("Failed to create AVI splitter.\n");
+ return;
+ }
+ IBaseFilter_Release(filter);
+
test_interfaces();
test_aggregation();
test_enum_pins();
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 392cfa849d3..d42c8dbf0b4 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -38,6 +38,7 @@
/* enum media */
void dump_AM_MEDIA_TYPE(const AM_MEDIA_TYPE * pmt);
+IUnknown * CALLBACK avi_splitter_create(IUnknown *outer, HRESULT *phr) DECLSPEC_HIDDEN;
IUnknown * CALLBACK Gstreamer_AudioConvert_create(IUnknown *pUnkOuter, HRESULT *phr);
IUnknown * CALLBACK Gstreamer_Mp3_create(IUnknown *pUnkOuter, HRESULT *phr);
IUnknown * CALLBACK Gstreamer_YUV2RGB_create(IUnknown *pUnkOuter, HRESULT *phr);
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c
index 22fb8ef4c84..6109d6be66e 100644
--- a/dlls/winegstreamer/gstdemux.c
+++ b/dlls/winegstreamer/gstdemux.c
@@ -769,6 +769,7 @@ out:
static void init_new_decoded_pad(GstElement *bin, GstPad *pad, struct gstdemux *This)
{
+ static const WCHAR formatW[] = {'S','t','r','e','a','m',' ','%','0','2','u',0};
const char *typename;
char *name;
GstCaps *caps;
@@ -779,9 +780,9 @@ static void init_new_decoded_pad(GstElement *bin, GstPad *pad, struct gstdemux *
TRACE("%p %p %p\n", This, bin, pad);
+ sprintfW(nameW, formatW, This->cStreams);
+
name = gst_pad_get_name(pad);
- MultiByteToWideChar(CP_UNIXCP, 0, name, -1, nameW, ARRAY_SIZE(nameW) - 1);
- nameW[ARRAY_SIZE(nameW) - 1] = 0;
TRACE("Name: %s\n", name);
g_free(name);
@@ -2277,3 +2278,110 @@ IUnknown * CALLBACK wave_parser_create(IUnknown *outer, HRESULT *phr)
TRACE("Created WAVE parser %p.\n", object);
return &object->filter.IUnknown_inner;
}
+
+static HRESULT WINAPI avi_splitter_sink_CheckMediaType(BasePin *iface, const AM_MEDIA_TYPE *mt)
+{
+ if (IsEqualGUID(&mt->majortype, &MEDIATYPE_Stream)
+ && IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_Avi))
+ return S_OK;
+ return S_FALSE;
+}
+
+static const BasePinFuncTable avi_splitter_sink_ops =
+{
+ .pfnCheckMediaType = avi_splitter_sink_CheckMediaType,
+ .pfnGetMediaType = BasePinImpl_GetMediaType,
+};
+
+static BOOL avi_splitter_init_gst(struct gstdemux *filter)
+{
+ GstElement *element = gst_element_factory_make("avidemux", NULL);
+ LONGLONG duration;
+ unsigned int i;
+ int ret;
+
+ if (!element)
+ {
+ ERR("Failed to create avidemux; are %u-bit GStreamer \"good\" plugins installed?\n",
+ 8 * (int)sizeof(void*));
+ return FALSE;
+ }
+
+ gst_bin_add(GST_BIN(filter->container), element);
+
+ g_signal_connect(element, "pad-added", G_CALLBACK(existing_new_pad_wrapper), filter);
+ g_signal_connect(element, "pad-removed", G_CALLBACK(removed_decoded_pad_wrapper), filter);
+ g_signal_connect(element, "no-more-pads", G_CALLBACK(no_more_pads_wrapper), filter);
+
+ filter->their_sink = gst_element_get_static_pad(element, "sink");
+ ResetEvent(filter->no_more_pads_event);
+
+ if ((ret = gst_pad_link(filter->my_src, filter->their_sink)) < 0)
+ {
+ ERR("Failed to link pads, error %d.\n", ret);
+ return FALSE;
+ }
+
+ gst_element_set_state(filter->container, GST_STATE_PLAYING);
+ ret = gst_element_get_state(filter->container, NULL, NULL, -1);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ {
+ ERR("Failed to play stream.\n");
+ return FALSE;
+ }
+
+ WaitForSingleObject(filter->no_more_pads_event, INFINITE);
+
+ gst_pad_query_duration(filter->ppPins[0]->their_src, GST_FORMAT_TIME, &duration);
+ for (i = 0; i < filter->cStreams; ++i)
+ {
+ struct gstdemux_source *pin = filter->ppPins[i];
+
+ pin->seek.llDuration = pin->seek.llStop = duration / 100;
+ pin->seek.llCurrent = 0;
+ if (!pin->seek.llDuration)
+ pin->seek.dwCapabilities = 0;
+ WaitForSingleObject(pin->caps_event, INFINITE);
+ }
+
+ filter->ignore_flush = TRUE;
+ gst_element_set_state(filter->container, GST_STATE_READY);
+ gst_element_get_state(filter->container, NULL, NULL, -1);
+ filter->ignore_flush = FALSE;
+
+ return TRUE;
+}
+
+IUnknown * CALLBACK avi_splitter_create(IUnknown *outer, HRESULT *phr)
+{
+ static const WCHAR sink_name[] = {'i','n','p','u','t',' ','p','i','n',0};
+ struct gstdemux *object;
+
+ if (!init_gstreamer())
+ {
+ *phr = E_FAIL;
+ return NULL;
+ }
+
+ mark_wine_thread();
+
+ if (!(object = heap_alloc_zero(sizeof(*object))))
+ {
+ *phr = E_OUTOFMEMORY;
+ return NULL;
+ }
+
+ strmbase_filter_init(&object->filter, &GST_Vtbl, outer, &CLSID_AviSplitter, &filter_ops);
+
+ object->no_more_pads_event = CreateEventW(NULL, FALSE, FALSE, NULL);
+ object->sink.dir = PINDIR_INPUT;
+ object->sink.filter = &object->filter;
+ lstrcpynW(object->sink.name, sink_name, ARRAY_SIZE(object->sink.name));
+ object->sink.IPin_iface.lpVtbl = &GST_InputPin_Vtbl;
+ object->sink.pFuncsTable = &avi_splitter_sink_ops;
+ object->init_gst = avi_splitter_init_gst;
+ *phr = S_OK;
+
+ TRACE("Created AVI splitter %p.\n", object);
+ return &object->filter.IUnknown_inner;
+}
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c
index 1056d0d6674..80c76c33dd4 100644
--- a/dlls/winegstreamer/main.c
+++ b/dlls/winegstreamer/main.c
@@ -48,6 +48,8 @@ static const WCHAR wGstreamer_AudioConvert[] =
{'G','S','t','r','e','a','m','e','r',' ','A','u','d','i','o','C','o','n','v','e','r','t',' ','f','i','l','t','e','r',0};
static const WCHAR wave_parserW[] =
{'W','a','v','e',' ','P','a','r','s','e','r',0};
+static const WCHAR avi_splitterW[] =
+{'A','V','I',' ','S','p','l','i','t','t','e','r',0};
static WCHAR wNull[] = {'\0'};
@@ -223,6 +225,40 @@ static const AMOVIESETUP_FILTER wave_parser_filter_data =
wave_parser_pin_data,
};
+static const AMOVIESETUP_MEDIATYPE avi_splitter_sink_type_data[] =
+{
+ {&MEDIATYPE_Stream, &MEDIASUBTYPE_Avi},
+};
+
+static const AMOVIESETUP_PIN avi_splitter_pin_data[] =
+{
+ {
+ NULL,
+ FALSE, FALSE, FALSE, FALSE,
+ &GUID_NULL,
+ NULL,
+ ARRAY_SIZE(avi_splitter_sink_type_data),
+ avi_splitter_sink_type_data,
+ },
+ {
+ NULL,
+ FALSE, TRUE, FALSE, FALSE,
+ &GUID_NULL,
+ NULL,
+ ARRAY_SIZE(amfMTvideo),
+ amfMTvideo,
+ },
+};
+
+static const AMOVIESETUP_FILTER avi_splitter_filter_data =
+{
+ &CLSID_AviSplitter,
+ avi_splitterW,
+ 0x5ffff0,
+ ARRAY_SIZE(avi_splitter_pin_data),
+ avi_splitter_pin_data,
+};
+
FactoryTemplate const g_Templates[] = {
{
wGstreamer_Splitter,
@@ -266,6 +302,13 @@ FactoryTemplate const g_Templates[] = {
NULL,
&wave_parser_filter_data,
},
+ {
+ avi_splitterW,
+ &CLSID_AviSplitter,
+ avi_splitter_create,
+ NULL,
+ &avi_splitter_filter_data,
+ },
};
const int g_cTemplates = ARRAY_SIZE(g_Templates);
--
2.23.0
More information about the wine-devel
mailing list