From 5de712b5d8b8865c35b1907520c17dddf49b623d Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Thu, 19 Sep 2019 18:59:38 -0500 Subject: [PATCH] winegstreamer: Reimplement the AVI splitter on top of the avidemux plugin. Signed-off-by: Zebediah Figura Signed-off-by: Alexandre Julliard --- 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 -#include - -#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);