ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

avisplit.c
Go to the documentation of this file.
00001 /*
00002  * AVI Splitter Filter
00003  *
00004  * Copyright 2003 Robert Shearman
00005  * Copyright 2004-2005 Christian Costa
00006  * Copyright 2008 Maarten Lankhorst
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00021  */
00022 /* FIXME:
00023  * - Reference leaks, if they still exist
00024  * - Files without an index are not handled correctly yet.
00025  * - When stopping/starting, a sample is lost. This should be compensated by
00026  *   keeping track of previous index/position.
00027  * - Debugging channels are noisy at the moment, especially with thread
00028  *   related messages, however this is the only correct thing to do right now,
00029  *   since wine doesn't correctly handle all messages yet.
00030  */
00031 
00032 #include "quartz_private.h"
00033 #include "control_private.h"
00034 #include "pin.h"
00035 
00036 #include "uuids.h"
00037 #include "vfw.h"
00038 #include "aviriff.h"
00039 #include "vfwmsgs.h"
00040 #include "amvideo.h"
00041 
00042 #include "wine/unicode.h"
00043 #include "wine/debug.h"
00044 
00045 #include <math.h>
00046 #include <assert.h>
00047 
00048 #include "parser.h"
00049 
00050 #define TWOCCFromFOURCC(fcc) HIWORD(fcc)
00051 
00052 /* four character codes used in AVI files */
00053 #define ckidINFO       mmioFOURCC('I','N','F','O')
00054 #define ckidREC        mmioFOURCC('R','E','C',' ')
00055 
00056 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
00057 
00058 typedef struct StreamData
00059 {
00060     DWORD dwSampleSize;
00061     FLOAT fSamplesPerSec;
00062     DWORD dwLength;
00063 
00064     AVISTREAMHEADER streamheader;
00065     DWORD entries;
00066     AVISTDINDEX **stdindex;
00067     DWORD frames;
00068     DWORD seek;
00069 
00070     /* Position, in index units */
00071     DWORD pos, pos_next, index, index_next;
00072 
00073     /* Packet handling: a thread is created and waits on the packet event handle
00074      * On an event acquire the sample lock, addref the sample and set it to NULL,
00075      * then queue a new packet.
00076      */
00077     HANDLE thread, packet_queued;
00078     IMediaSample *sample;
00079 
00080     /* Amount of preroll samples for this stream */
00081     DWORD preroll;
00082 } StreamData;
00083 
00084 typedef struct AVISplitterImpl
00085 {
00086     ParserImpl Parser;
00087     RIFFCHUNK CurrentChunk;
00088     LONGLONG CurrentChunkOffset; /* in media time */
00089     LONGLONG EndOfFile;
00090     AVIMAINHEADER AviHeader;
00091     AVIEXTHEADER ExtHeader;
00092 
00093     AVIOLDINDEX *oldindex;
00094     DWORD offset;
00095 
00096     StreamData *streams;
00097 } AVISplitterImpl;
00098 
00099 struct thread_args {
00100     AVISplitterImpl *This;
00101     DWORD stream;
00102 };
00103 
00104 /* The threading stuff cries for an explanation
00105  *
00106  * PullPin starts processing and calls AVISplitter_first_request
00107  * AVISplitter_first_request creates a thread for each stream
00108  * A stream can be audio, video, subtitles or something undefined.
00109  *
00110  * AVISplitter_first_request loads a single packet to each but one stream,
00111  * and queues it for that last stream. This is to prevent WaitForNext to time
00112  * out badly.
00113  *
00114  * The processing loop is entered. It calls IAsyncReader_WaitForNext in the
00115  * PullPin. Every time it receives a packet, it will call AVISplitter_Sample
00116  * AVISplitter_Sample will signal the relevant thread that a new sample is
00117  * arrived, when that thread is ready it will read the packet and transmits
00118  * it downstream with AVISplitter_Receive
00119  *
00120  * Threads terminate upon receiving NULL as packet or when ANY error code
00121  * != S_OK occurs. This means that any error is fatal to processing.
00122  */
00123 
00124 static HRESULT AVISplitter_SendEndOfFile(AVISplitterImpl *This, DWORD streamnumber)
00125 {
00126     IPin* ppin = NULL;
00127     HRESULT hr;
00128 
00129     TRACE("End of file reached\n");
00130 
00131     hr = IPin_ConnectedTo(This->Parser.ppPins[streamnumber+1], &ppin);
00132     if (SUCCEEDED(hr))
00133     {
00134         hr = IPin_EndOfStream(ppin);
00135         IPin_Release(ppin);
00136     }
00137     TRACE("--> %x\n", hr);
00138 
00139     /* Force the pullpin thread to stop */
00140     return S_FALSE;
00141 }
00142 
00143 /* Thread worker horse */
00144 static HRESULT AVISplitter_next_request(AVISplitterImpl *This, DWORD streamnumber)
00145 {
00146     StreamData *stream = This->streams + streamnumber;
00147     PullPin *pin = This->Parser.pInputPin;
00148     IMediaSample *sample = NULL;
00149     HRESULT hr;
00150 
00151     TRACE("(%p, %u)->()\n", This, streamnumber);
00152 
00153     hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0);
00154     if (hr != S_OK)
00155         ERR("... %08x?\n", hr);
00156 
00157     if (SUCCEEDED(hr))
00158     {
00159         LONGLONG rtSampleStart;
00160         /* Add 4 for the next header, which should hopefully work */
00161         LONGLONG rtSampleStop;
00162 
00163         stream->pos = stream->pos_next;
00164         stream->index = stream->index_next;
00165 
00166         IMediaSample_SetDiscontinuity(sample, stream->seek);
00167         stream->seek = FALSE;
00168         if (stream->preroll)
00169         {
00170             --stream->preroll;
00171             IMediaSample_SetPreroll(sample, TRUE);
00172         }
00173         else
00174             IMediaSample_SetPreroll(sample, FALSE);
00175         IMediaSample_SetSyncPoint(sample, TRUE);
00176 
00177         if (stream->stdindex)
00178         {
00179             AVISTDINDEX *index = stream->stdindex[stream->index];
00180             AVISTDINDEX_ENTRY *entry = &index->aIndex[stream->pos];
00181 
00182             /* End of file */
00183             if (stream->index >= stream->entries)
00184             {
00185                 TRACE("END OF STREAM ON %u\n", streamnumber);
00186                 IMediaSample_Release(sample);
00187                 return S_FALSE;
00188             }
00189 
00190             rtSampleStart = index->qwBaseOffset;
00191             rtSampleStart += entry->dwOffset;
00192             rtSampleStart = MEDIATIME_FROM_BYTES(rtSampleStart);
00193 
00194             ++stream->pos_next;
00195             if (index->nEntriesInUse == stream->pos_next)
00196             {
00197                 stream->pos_next = 0;
00198                 ++stream->index_next;
00199             }
00200 
00201             rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(entry->dwSize & ~(1 << 31));
00202 
00203             TRACE("offset(%u) size(%u)\n", (DWORD)BYTES_FROM_MEDIATIME(rtSampleStart), (DWORD)BYTES_FROM_MEDIATIME(rtSampleStop - rtSampleStart));
00204         }
00205         else if (This->oldindex)
00206         {
00207             DWORD flags = This->oldindex->aIndex[stream->pos].dwFlags;
00208             DWORD size = This->oldindex->aIndex[stream->pos].dwSize;
00209 
00210             /* End of file */
00211             if (stream->index)
00212             {
00213                 TRACE("END OF STREAM ON %u\n", streamnumber);
00214                 IMediaSample_Release(sample);
00215                 return S_FALSE;
00216             }
00217 
00218             rtSampleStart = MEDIATIME_FROM_BYTES(This->offset);
00219             rtSampleStart += MEDIATIME_FROM_BYTES(This->oldindex->aIndex[stream->pos].dwOffset);
00220             rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(size);
00221             if (flags & AVIIF_MIDPART)
00222             {
00223                 FIXME("Only stand alone frames are currently handled correctly!\n");
00224             }
00225             if (flags & AVIIF_LIST)
00226             {
00227                 FIXME("Not sure if this is handled correctly\n");
00228                 rtSampleStart += MEDIATIME_FROM_BYTES(sizeof(RIFFLIST));
00229                 rtSampleStop += MEDIATIME_FROM_BYTES(sizeof(RIFFLIST));
00230             }
00231             else
00232             {
00233                 rtSampleStart += MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK));
00234                 rtSampleStop += MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK));
00235             }
00236 
00237             /* Slow way of finding next index */
00238             do {
00239                 stream->pos_next++;
00240             } while (stream->pos_next * sizeof(This->oldindex->aIndex[0]) < This->oldindex->cb
00241                      && StreamFromFOURCC(This->oldindex->aIndex[stream->pos_next].dwChunkId) != streamnumber);
00242 
00243             /* End of file soon */
00244             if (stream->pos_next * sizeof(This->oldindex->aIndex[0]) >= This->oldindex->cb)
00245             {
00246                 stream->pos_next = 0;
00247                 ++stream->index_next;
00248             }
00249         }
00250         else /* TODO: Generate an index automagically */
00251         {
00252             ERR("CAN'T PLAY WITHOUT AN INDEX! SOS! SOS! SOS!\n");
00253             assert(0);
00254         }
00255 
00256         if (rtSampleStart != rtSampleStop)
00257         {
00258             hr = IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop);
00259 
00260             hr = IAsyncReader_Request(pin->pReader, sample, streamnumber);
00261 
00262             if (FAILED(hr))
00263                 assert(IMediaSample_Release(sample) == 0);
00264         }
00265         else
00266         {
00267             stream->sample = sample;
00268             IMediaSample_SetActualDataLength(sample, 0);
00269             SetEvent(stream->packet_queued);
00270         }
00271     }
00272     else
00273     {
00274         if (sample)
00275         {
00276             ERR("There should be no sample!\n");
00277             assert(IMediaSample_Release(sample) == 0);
00278         }
00279     }
00280     TRACE("--> %08x\n", hr);
00281 
00282     return hr;
00283 }
00284 
00285 static HRESULT AVISplitter_Receive(AVISplitterImpl *This, IMediaSample *sample, DWORD streamnumber)
00286 {
00287     Parser_OutputPin *pin = (Parser_OutputPin *)This->Parser.ppPins[1+streamnumber];
00288     HRESULT hr;
00289     LONGLONG start, stop;
00290     StreamData *stream = &This->streams[streamnumber];
00291 
00292     start = pin->dwSamplesProcessed;
00293     start *= stream->streamheader.dwScale;
00294     start *= 10000000;
00295     start /= stream->streamheader.dwRate;
00296 
00297     if (stream->streamheader.dwSampleSize)
00298     {
00299         ULONG len = IMediaSample_GetActualDataLength(sample);
00300         ULONG size = stream->streamheader.dwSampleSize;
00301 
00302         pin->dwSamplesProcessed += len / size;
00303     }
00304     else
00305         ++pin->dwSamplesProcessed;
00306 
00307     stop = pin->dwSamplesProcessed;
00308     stop *= stream->streamheader.dwScale;
00309     stop *= 10000000;
00310     stop /= stream->streamheader.dwRate;
00311 
00312     IMediaSample_SetTime(sample, &start, &stop);
00313 
00314     hr = OutputPin_SendSample(&pin->pin, sample);
00315 
00316 /* Uncomment this if you want to debug the time differences between the
00317  * different streams, it is useful for that
00318  *
00319     FIXME("stream %u, hr: %08x, Start: %u.%03u, Stop: %u.%03u\n", streamnumber, hr,
00320            (DWORD)(start / 10000000), (DWORD)((start / 10000)%1000),
00321            (DWORD)(stop / 10000000), (DWORD)((stop / 10000)%1000));
00322 */
00323     return hr;
00324 }
00325 
00326 static DWORD WINAPI AVISplitter_thread_reader(LPVOID data)
00327 {
00328     struct thread_args *args = data;
00329     AVISplitterImpl *This = args->This;
00330     DWORD streamnumber = args->stream;
00331     HRESULT hr = S_OK;
00332 
00333     do
00334     {
00335         HRESULT nexthr = S_FALSE;
00336         IMediaSample *sample;
00337 
00338         WaitForSingleObject(This->streams[streamnumber].packet_queued, INFINITE);
00339         sample = This->streams[streamnumber].sample;
00340         This->streams[streamnumber].sample = NULL;
00341         if (!sample)
00342             break;
00343 
00344         nexthr = AVISplitter_next_request(This, streamnumber);
00345 
00346         hr = AVISplitter_Receive(This, sample, streamnumber);
00347         if (hr != S_OK)
00348             FIXME("Receiving error: %08x\n", hr);
00349 
00350         IMediaSample_Release(sample);
00351         if (hr == S_OK)
00352             hr = nexthr;
00353         if (nexthr == S_FALSE)
00354             AVISplitter_SendEndOfFile(This, streamnumber);
00355     } while (hr == S_OK);
00356 
00357     if (hr != S_FALSE)
00358         FIXME("Thread %u terminated with hr %08x!\n", streamnumber, hr);
00359     else
00360         TRACE("Thread %u terminated properly\n", streamnumber);
00361     return hr;
00362 }
00363 
00364 static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample, DWORD_PTR cookie)
00365 {
00366     AVISplitterImpl *This = iface;
00367     StreamData *stream = This->streams + cookie;
00368     HRESULT hr = S_OK;
00369 
00370     if (!IMediaSample_GetActualDataLength(pSample))
00371     {
00372         ERR("Received empty sample\n");
00373         return S_OK;
00374     }
00375 
00376     /* Send the sample to whatever thread is appropiate
00377      * That thread should also not have a sample queued at the moment
00378      */
00379     /* Debugging */
00380     TRACE("(%p)->(%p size: %u, %lu)\n", This, pSample, IMediaSample_GetActualDataLength(pSample), cookie);
00381     assert(cookie < This->Parser.cStreams);
00382     assert(!stream->sample);
00383     assert(WaitForSingleObject(stream->packet_queued, 0) == WAIT_TIMEOUT);
00384 
00385     IMediaSample_AddRef(pSample);
00386 
00387     stream->sample = pSample;
00388     SetEvent(stream->packet_queued);
00389 
00390     return hr;
00391 }
00392 
00393 static HRESULT AVISplitter_done_process(LPVOID iface);
00394 
00395 /* On the first request we have to be sure that (cStreams-1) samples have
00396  * already been processed, because otherwise some pins might not ever finish
00397  * a Pause state change
00398  */
00399 static HRESULT AVISplitter_first_request(LPVOID iface)
00400 {
00401     AVISplitterImpl *This = iface;
00402     HRESULT hr = S_OK;
00403     DWORD x;
00404     IMediaSample *sample = NULL;
00405     BOOL have_sample = FALSE;
00406 
00407     TRACE("(%p)->()\n", This);
00408 
00409     for (x = 0; x < This->Parser.cStreams; ++x)
00410     {
00411         StreamData *stream = This->streams + x;
00412 
00413         /* Nothing should be running at this point */
00414         assert(!stream->thread);
00415 
00416         assert(!sample);
00417         /* It could be we asked the thread to terminate, and the thread
00418          * already terminated before receiving the deathwish */
00419         ResetEvent(stream->packet_queued);
00420 
00421         stream->pos_next = stream->pos;
00422         stream->index_next = stream->index;
00423 
00424         /* There should be a packet queued from AVISplitter_next_request last time
00425          * It needs to be done now because this is the only way to ensure that every
00426          * stream will have at least 1 packet processed
00427          * If this is done after the threads start it could go all awkward and we
00428          * would have no guarantees that it's successful at all
00429          */
00430 
00431         if (have_sample)
00432         {
00433             DWORD_PTR dwUser = ~0;
00434             hr = IAsyncReader_WaitForNext(This->Parser.pInputPin->pReader, 10000, &sample, &dwUser);
00435             assert(hr == S_OK);
00436             assert(sample);
00437 
00438             AVISplitter_Sample(iface, sample, dwUser);
00439             IMediaSample_Release(sample);
00440         }
00441 
00442         hr = AVISplitter_next_request(This, x);
00443         TRACE("-->%08x\n", hr);
00444 
00445         /* Could be an EOF instead */
00446         have_sample = (hr == S_OK);
00447         if (hr == S_FALSE)
00448             AVISplitter_SendEndOfFile(This, x);
00449 
00450         if (FAILED(hr) && hr != VFW_E_NOT_CONNECTED)
00451             break;
00452         hr = S_OK;
00453     }
00454 
00455     /* FIXME: Don't do this for each pin that sent an EOF */
00456     for (x = 0; x < This->Parser.cStreams && SUCCEEDED(hr); ++x)
00457     {
00458         struct thread_args *args;
00459         DWORD tid;
00460 
00461         if ((This->streams[x].stdindex && This->streams[x].index_next >= This->streams[x].entries) ||
00462             (!This->streams[x].stdindex && This->streams[x].index_next))
00463         {
00464             This->streams[x].thread = NULL;
00465             continue;
00466         }
00467 
00468         args = CoTaskMemAlloc(sizeof(*args));
00469         args->This = This;
00470         args->stream = x;
00471         This->streams[x].thread = CreateThread(NULL, 0, AVISplitter_thread_reader, args, 0, &tid);
00472         TRACE("Created stream %u thread 0x%08x\n", x, tid);
00473     }
00474 
00475     if (FAILED(hr))
00476         ERR("Horsemen of the apocalypse came to bring error 0x%08x\n", hr);
00477 
00478     return hr;
00479 }
00480 
00481 static HRESULT AVISplitter_done_process(LPVOID iface)
00482 {
00483     AVISplitterImpl *This = iface;
00484 
00485     DWORD x;
00486 
00487     for (x = 0; x < This->Parser.cStreams; ++x)
00488     {
00489         StreamData *stream = This->streams + x;
00490 
00491         TRACE("Waiting for %u to terminate\n", x);
00492         /* Make the thread return first */
00493         SetEvent(stream->packet_queued);
00494         assert(WaitForSingleObject(stream->thread, 100000) != WAIT_TIMEOUT);
00495         CloseHandle(stream->thread);
00496         stream->thread = NULL;
00497 
00498         if (stream->sample)
00499             assert(IMediaSample_Release(stream->sample) == 0);
00500         stream->sample = NULL;
00501 
00502         ResetEvent(stream->packet_queued);
00503     }
00504     TRACE("All threads are now terminated\n");
00505 
00506     return S_OK;
00507 }
00508 
00509 static HRESULT AVISplitter_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
00510 {
00511     if (IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream) && IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_Avi))
00512         return S_OK;
00513     return S_FALSE;
00514 }
00515 
00516 static HRESULT AVISplitter_ProcessIndex(AVISplitterImpl *This, AVISTDINDEX **index, LONGLONG qwOffset, DWORD cb)
00517 {
00518     AVISTDINDEX *pIndex;
00519     DWORD x;
00520     int rest;
00521 
00522     *index = NULL;
00523     if (cb < sizeof(AVISTDINDEX))
00524     {
00525         FIXME("size %u too small\n", cb);
00526         return E_INVALIDARG;
00527     }
00528 
00529     pIndex = CoTaskMemAlloc(cb);
00530     if (!pIndex)
00531         return E_OUTOFMEMORY;
00532 
00533     IAsyncReader_SyncRead(((PullPin *)This->Parser.ppPins[0])->pReader, qwOffset, cb, (BYTE *)pIndex);
00534     rest = cb - sizeof(AVISUPERINDEX) + sizeof(RIFFCHUNK) + sizeof(pIndex->aIndex);
00535 
00536     TRACE("FOURCC: %s\n", debugstr_an((char *)&pIndex->fcc, 4));
00537     TRACE("wLongsPerEntry: %hd\n", pIndex->wLongsPerEntry);
00538     TRACE("bIndexSubType: %hd\n", pIndex->bIndexSubType);
00539     TRACE("bIndexType: %hd\n", pIndex->bIndexType);
00540     TRACE("nEntriesInUse: %u\n", pIndex->nEntriesInUse);
00541     TRACE("dwChunkId: %.4s\n", (char *)&pIndex->dwChunkId);
00542     TRACE("qwBaseOffset: %x%08x\n", (DWORD)(pIndex->qwBaseOffset >> 32), (DWORD)pIndex->qwBaseOffset);
00543     TRACE("dwReserved_3: %u\n", pIndex->dwReserved_3);
00544 
00545     if (pIndex->bIndexType != AVI_INDEX_OF_CHUNKS
00546         || pIndex->wLongsPerEntry != 2
00547         || rest < (pIndex->nEntriesInUse * sizeof(DWORD) * pIndex->wLongsPerEntry)
00548         || (pIndex->bIndexSubType != AVI_INDEX_SUB_DEFAULT))
00549     {
00550         FIXME("Invalid index chunk encountered: %u/%u, %u/%u, %u/%u, %u/%u\n",
00551               pIndex->bIndexType, AVI_INDEX_OF_CHUNKS, pIndex->wLongsPerEntry, 2,
00552               rest, (DWORD)(pIndex->nEntriesInUse * sizeof(DWORD) * pIndex->wLongsPerEntry),
00553               pIndex->bIndexSubType, AVI_INDEX_SUB_DEFAULT);
00554         *index = NULL;
00555         return E_INVALIDARG;
00556     }
00557 
00558     for (x = 0; x < pIndex->nEntriesInUse; ++x)
00559     {
00560         BOOL keyframe = !(pIndex->aIndex[x].dwSize >> 31);
00561         DWORDLONG offset = pIndex->qwBaseOffset + pIndex->aIndex[x].dwOffset;
00562         TRACE("dwOffset: %x%08x\n", (DWORD)(offset >> 32), (DWORD)offset);
00563         TRACE("dwSize: %u\n", (pIndex->aIndex[x].dwSize & ~(1<<31)));
00564         TRACE("Frame is a keyframe: %s\n", keyframe ? "yes" : "no");
00565     }
00566 
00567     *index = pIndex;
00568     return S_OK;
00569 }
00570 
00571 static HRESULT AVISplitter_ProcessOldIndex(AVISplitterImpl *This)
00572 {
00573     ULONGLONG mov_pos = BYTES_FROM_MEDIATIME(This->CurrentChunkOffset) - sizeof(DWORD);
00574     AVIOLDINDEX *pAviOldIndex = This->oldindex;
00575     int relative = -1;
00576     DWORD x;
00577 
00578     for (x = 0; x < pAviOldIndex->cb / sizeof(pAviOldIndex->aIndex[0]); ++x)
00579     {
00580         DWORD temp, temp2 = 0, offset, chunkid;
00581         PullPin *pin = This->Parser.pInputPin;
00582 
00583         offset = pAviOldIndex->aIndex[x].dwOffset;
00584         chunkid = pAviOldIndex->aIndex[x].dwChunkId;
00585 
00586         TRACE("dwChunkId: %.4s\n", (char *)&chunkid);
00587         TRACE("dwFlags: %08x\n", pAviOldIndex->aIndex[x].dwFlags);
00588         TRACE("dwOffset (%s): %08x\n", relative ? "relative" : "absolute", offset);
00589         TRACE("dwSize: %08x\n", pAviOldIndex->aIndex[x].dwSize);
00590 
00591         /* Only scan once, or else this will take too long */
00592         if (relative == -1)
00593         {
00594             IAsyncReader_SyncRead(pin->pReader, offset, sizeof(DWORD), (BYTE *)&temp);
00595             relative = (chunkid != temp);
00596 
00597             if (chunkid == mmioFOURCC('7','F','x','x')
00598                 && ((char *)&temp)[0] == 'i' && ((char *)&temp)[1] == 'x')
00599                 relative = FALSE;
00600 
00601             if (relative)
00602             {
00603                 if (offset + mov_pos < BYTES_FROM_MEDIATIME(This->EndOfFile))
00604                     IAsyncReader_SyncRead(pin->pReader, offset + mov_pos, sizeof(DWORD), (BYTE *)&temp2);
00605 
00606                 if (chunkid == mmioFOURCC('7','F','x','x')
00607                     && ((char *)&temp2)[0] == 'i' && ((char *)&temp2)[1] == 'x')
00608                 {
00609                     /* Do nothing, all is great */
00610                 }
00611                 else if (temp2 != chunkid)
00612                 {
00613                     ERR("Faulty index or bug in handling: Wanted FCC: %s, Abs FCC: %s (@ %x), Rel FCC: %s (@ %.0x%08x)\n",
00614                         debugstr_an((char *)&chunkid, 4), debugstr_an((char *)&temp, 4), offset,
00615                         debugstr_an((char *)&temp2, 4), (DWORD)((mov_pos + offset) >> 32), (DWORD)(mov_pos + offset));
00616                     relative = -1;
00617                 }
00618                 else
00619                     TRACE("Scanned dwChunkId: %s\n", debugstr_an((char *)&temp2, 4));
00620             }
00621             else if (!relative)
00622                 TRACE("Scanned dwChunkId: %s\n", debugstr_an((char *)&temp, 4));
00623         }
00624         /* Only dump one packet */
00625         else break;
00626     }
00627 
00628     if (relative == -1)
00629     {
00630         FIXME("Dropping index: no idea whether it is relative or absolute\n");
00631         CoTaskMemFree(This->oldindex);
00632         This->oldindex = NULL;
00633     }
00634     else if (!relative)
00635         This->offset = 0;
00636     else
00637         This->offset = (DWORD)mov_pos;
00638 
00639     return S_OK;
00640 }
00641 
00642 static HRESULT AVISplitter_ProcessStreamList(AVISplitterImpl * This, const BYTE * pData, DWORD cb, ALLOCATOR_PROPERTIES *props)
00643 {
00644     PIN_INFO piOutput;
00645     const RIFFCHUNK * pChunk;
00646     HRESULT hr;
00647     AM_MEDIA_TYPE amt;
00648     float fSamplesPerSec = 0.0f;
00649     DWORD dwSampleSize = 0;
00650     DWORD dwLength = 0;
00651     DWORD nstdindex = 0;
00652     static const WCHAR wszStreamTemplate[] = {'S','t','r','e','a','m',' ','%','0','2','d',0};
00653     StreamData *stream;
00654 
00655     ZeroMemory(&amt, sizeof(amt));
00656     piOutput.dir = PINDIR_OUTPUT;
00657     piOutput.pFilter = (IBaseFilter *)This;
00658     wsprintfW(piOutput.achName, wszStreamTemplate, This->Parser.cStreams);
00659     This->streams = CoTaskMemRealloc(This->streams, sizeof(StreamData) * (This->Parser.cStreams+1));
00660     stream = This->streams + This->Parser.cStreams;
00661     ZeroMemory(stream, sizeof(*stream));
00662 
00663     for (pChunk = (const RIFFCHUNK *)pData; 
00664          ((const BYTE *)pChunk >= pData) && ((const BYTE *)pChunk + sizeof(RIFFCHUNK) < pData + cb) && (pChunk->cb > 0); 
00665          pChunk = (const RIFFCHUNK *)((const BYTE*)pChunk + sizeof(RIFFCHUNK) + pChunk->cb)     
00666         )
00667     {
00668         switch (pChunk->fcc)
00669         {
00670         case ckidSTREAMHEADER:
00671             {
00672                 const AVISTREAMHEADER * pStrHdr = (const AVISTREAMHEADER *)pChunk;
00673                 TRACE("processing stream header\n");
00674                 stream->streamheader = *pStrHdr;
00675 
00676                 fSamplesPerSec = (float)pStrHdr->dwRate / (float)pStrHdr->dwScale;
00677                 CoTaskMemFree(amt.pbFormat);
00678                 amt.pbFormat = NULL;
00679                 amt.cbFormat = 0;
00680 
00681                 switch (pStrHdr->fccType)
00682                 {
00683                 case streamtypeVIDEO:
00684                     amt.formattype = FORMAT_VideoInfo;
00685                     break;
00686                 case streamtypeAUDIO:
00687                     amt.formattype = FORMAT_WaveFormatEx;
00688                     break;
00689                 default:
00690                     FIXME("fccType %.4s not handled yet\n", (const char *)&pStrHdr->fccType);
00691                     amt.formattype = FORMAT_None;
00692                 }
00693                 amt.majortype = MEDIATYPE_Video;
00694                 amt.majortype.Data1 = pStrHdr->fccType;
00695                 amt.subtype = MEDIATYPE_Video;
00696                 amt.subtype.Data1 = pStrHdr->fccHandler;
00697                 TRACE("Subtype FCC: %.04s\n", (LPCSTR)&pStrHdr->fccHandler);
00698                 amt.lSampleSize = pStrHdr->dwSampleSize;
00699                 amt.bFixedSizeSamples = (amt.lSampleSize != 0);
00700 
00701                 /* FIXME: Is this right? */
00702                 if (!amt.lSampleSize)
00703                 {
00704                     amt.lSampleSize = 1;
00705                     dwSampleSize = 1;
00706                 }
00707 
00708                 amt.bTemporalCompression = IsEqualGUID(&amt.majortype, &MEDIATYPE_Video); /* FIXME? */
00709                 dwSampleSize = pStrHdr->dwSampleSize;
00710                 dwLength = pStrHdr->dwLength;
00711                 if (!dwLength)
00712                     dwLength = This->AviHeader.dwTotalFrames;
00713 
00714                 if (pStrHdr->dwSuggestedBufferSize && pStrHdr->dwSuggestedBufferSize > props->cbBuffer)
00715                     props->cbBuffer = pStrHdr->dwSuggestedBufferSize;
00716 
00717                 break;
00718             }
00719         case ckidSTREAMFORMAT:
00720             TRACE("processing stream format data\n");
00721             if (IsEqualIID(&amt.formattype, &FORMAT_VideoInfo))
00722             {
00723                 VIDEOINFOHEADER * pvi;
00724                 /* biCompression member appears to override the value in the stream header.
00725                  * i.e. the stream header can say something completely contradictory to what
00726                  * is in the BITMAPINFOHEADER! */
00727                 if (pChunk->cb < sizeof(BITMAPINFOHEADER))
00728                 {
00729                     ERR("Not enough bytes for BITMAPINFOHEADER\n");
00730                     return E_FAIL;
00731                 }
00732                 amt.cbFormat = sizeof(VIDEOINFOHEADER) - sizeof(BITMAPINFOHEADER) + pChunk->cb;
00733                 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
00734                 ZeroMemory(amt.pbFormat, amt.cbFormat);
00735                 pvi = (VIDEOINFOHEADER *)amt.pbFormat;
00736                 pvi->AvgTimePerFrame = (LONGLONG)(10000000.0 / fSamplesPerSec);
00737 
00738                 CopyMemory(&pvi->bmiHeader, pChunk + 1, pChunk->cb);
00739                 if (pvi->bmiHeader.biCompression)
00740                     amt.subtype.Data1 = pvi->bmiHeader.biCompression;
00741             }
00742             else if (IsEqualIID(&amt.formattype, &FORMAT_WaveFormatEx))
00743             {
00744                 amt.cbFormat = pChunk->cb;
00745                 if (amt.cbFormat < sizeof(WAVEFORMATEX))
00746                     amt.cbFormat = sizeof(WAVEFORMATEX);
00747                 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
00748                 ZeroMemory(amt.pbFormat, amt.cbFormat);
00749                 CopyMemory(amt.pbFormat, pChunk + 1, pChunk->cb);
00750             }
00751             else
00752             {
00753                 amt.cbFormat = pChunk->cb;
00754                 amt.pbFormat = CoTaskMemAlloc(amt.cbFormat);
00755                 CopyMemory(amt.pbFormat, pChunk + 1, amt.cbFormat);
00756             }
00757             break;
00758         case ckidSTREAMNAME:
00759             TRACE("processing stream name\n");
00760             /* FIXME: this doesn't exactly match native version (we omit the "##)" prefix), but hey... */
00761             MultiByteToWideChar(CP_ACP, 0, (LPCSTR)(pChunk + 1), pChunk->cb, piOutput.achName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0]));
00762             break;
00763         case ckidSTREAMHANDLERDATA:
00764             FIXME("process stream handler data\n");
00765             break;
00766         case ckidAVIPADDING:
00767             TRACE("JUNK chunk ignored\n");
00768             break;
00769         case ckidAVISUPERINDEX:
00770         {
00771             const AVISUPERINDEX *pIndex = (const AVISUPERINDEX *)pChunk;
00772             DWORD x;
00773             UINT rest = pIndex->cb - sizeof(AVISUPERINDEX) + sizeof(RIFFCHUNK) + sizeof(pIndex->aIndex[0]) * ANYSIZE_ARRAY;
00774 
00775             if (pIndex->cb < sizeof(AVISUPERINDEX) - sizeof(RIFFCHUNK))
00776             {
00777                 FIXME("size %u\n", pIndex->cb);
00778                 break;
00779             }
00780 
00781             if (nstdindex++ > 0)
00782             {
00783                 ERR("Stream %d got more than 1 superindex?\n", This->Parser.cStreams);
00784                 break;
00785             }
00786 
00787             TRACE("wLongsPerEntry: %hd\n", pIndex->wLongsPerEntry);
00788             TRACE("bIndexSubType: %hd\n", pIndex->bIndexSubType);
00789             TRACE("bIndexType: %hd\n", pIndex->bIndexType);
00790             TRACE("nEntriesInUse: %u\n", pIndex->nEntriesInUse);
00791             TRACE("dwChunkId: %.4s\n", (const char *)&pIndex->dwChunkId);
00792             if (pIndex->dwReserved[0])
00793                 TRACE("dwReserved[0]: %u\n", pIndex->dwReserved[0]);
00794             if (pIndex->dwReserved[2])
00795                 TRACE("dwReserved[1]: %u\n", pIndex->dwReserved[1]);
00796             if (pIndex->dwReserved[2])
00797                 TRACE("dwReserved[2]: %u\n", pIndex->dwReserved[2]);
00798 
00799             if (pIndex->bIndexType != AVI_INDEX_OF_INDEXES
00800                 || pIndex->wLongsPerEntry != 4
00801                 || rest < (pIndex->nEntriesInUse * sizeof(DWORD) * pIndex->wLongsPerEntry)
00802                 || (pIndex->bIndexSubType != AVI_INDEX_SUB_2FIELD && pIndex->bIndexSubType != AVI_INDEX_SUB_DEFAULT))
00803             {
00804                 FIXME("Invalid index chunk encountered\n");
00805                 break;
00806             }
00807 
00808             stream->entries = pIndex->nEntriesInUse;
00809             stream->stdindex = CoTaskMemRealloc(stream->stdindex, sizeof(*stream->stdindex) * stream->entries);
00810             for (x = 0; x < pIndex->nEntriesInUse; ++x)
00811             {
00812                 TRACE("qwOffset: %x%08x\n", (DWORD)(pIndex->aIndex[x].qwOffset >> 32), (DWORD)pIndex->aIndex[x].qwOffset);
00813                 TRACE("dwSize: %u\n", pIndex->aIndex[x].dwSize);
00814                 TRACE("dwDuration: %u (unreliable)\n", pIndex->aIndex[x].dwDuration);
00815 
00816                 AVISplitter_ProcessIndex(This, &stream->stdindex[x], pIndex->aIndex[x].qwOffset, pIndex->aIndex[x].dwSize);
00817             }
00818             break;
00819         }
00820         default:
00821             FIXME("unknown chunk type \"%.04s\" ignored\n", (LPCSTR)&pChunk->fcc);
00822         }
00823     }
00824 
00825     if (IsEqualGUID(&amt.formattype, &FORMAT_WaveFormatEx))
00826     {
00827         amt.subtype = MEDIATYPE_Video;
00828         amt.subtype.Data1 = ((WAVEFORMATEX *)amt.pbFormat)->wFormatTag;
00829     }
00830 
00831     dump_AM_MEDIA_TYPE(&amt);
00832     TRACE("fSamplesPerSec = %f\n", (double)fSamplesPerSec);
00833     TRACE("dwSampleSize = %x\n", dwSampleSize);
00834     TRACE("dwLength = %x\n", dwLength);
00835 
00836     stream->fSamplesPerSec = fSamplesPerSec;
00837     stream->dwSampleSize = dwSampleSize;
00838     stream->dwLength = dwLength; /* TODO: Use this for mediaseeking */
00839     stream->packet_queued = CreateEventW(NULL, 0, 0, NULL);
00840 
00841     hr = Parser_AddPin(&(This->Parser), &piOutput, props, &amt);
00842     CoTaskMemFree(amt.pbFormat);
00843 
00844 
00845     return hr;
00846 }
00847 
00848 static HRESULT AVISplitter_ProcessODML(AVISplitterImpl * This, const BYTE * pData, DWORD cb)
00849 {
00850     const RIFFCHUNK * pChunk;
00851 
00852     for (pChunk = (const RIFFCHUNK *)pData;
00853          ((const BYTE *)pChunk >= pData) && ((const BYTE *)pChunk + sizeof(RIFFCHUNK) < pData + cb) && (pChunk->cb > 0);
00854          pChunk = (const RIFFCHUNK *)((const BYTE*)pChunk + sizeof(RIFFCHUNK) + pChunk->cb)
00855         )
00856     {
00857         switch (pChunk->fcc)
00858         {
00859         case ckidAVIEXTHEADER:
00860             {
00861                 int x;
00862                 const AVIEXTHEADER * pExtHdr = (const AVIEXTHEADER *)pChunk;
00863 
00864                 TRACE("processing extension header\n");
00865                 if (pExtHdr->cb != sizeof(AVIEXTHEADER) - sizeof(RIFFCHUNK))
00866                 {
00867                     FIXME("Size: %u\n", pExtHdr->cb);
00868                     break;
00869                 }
00870                 TRACE("dwGrandFrames: %u\n", pExtHdr->dwGrandFrames);
00871                 for (x = 0; x < 61; ++x)
00872                     if (pExtHdr->dwFuture[x])
00873                         FIXME("dwFuture[%i] = %u (0x%08x)\n", x, pExtHdr->dwFuture[x], pExtHdr->dwFuture[x]);
00874                 This->ExtHeader = *pExtHdr;
00875                 break;
00876             }
00877         default:
00878             FIXME("unknown chunk type \"%.04s\" ignored\n", (LPCSTR)&pChunk->fcc);
00879         }
00880     }
00881 
00882     return S_OK;
00883 }
00884 
00885 static HRESULT AVISplitter_InitializeStreams(AVISplitterImpl *This)
00886 {
00887     unsigned int x;
00888 
00889     if (This->oldindex)
00890     {
00891         DWORD nMax, n;
00892 
00893         for (x = 0; x < This->Parser.cStreams; ++x)
00894         {
00895             This->streams[x].frames = 0;
00896             This->streams[x].pos = ~0;
00897             This->streams[x].index = 0;
00898         }
00899 
00900         nMax = This->oldindex->cb / sizeof(This->oldindex->aIndex[0]);
00901 
00902         /* Ok, maybe this is more of an excercise to see if I interpret everything correctly or not, but that is useful for now. */
00903         for (n = 0; n < nMax; ++n)
00904         {
00905             DWORD streamId = StreamFromFOURCC(This->oldindex->aIndex[n].dwChunkId);
00906             if (streamId >= This->Parser.cStreams)
00907             {
00908                 FIXME("Stream id %s ignored\n", debugstr_an((char*)&This->oldindex->aIndex[n].dwChunkId, 4));
00909                 continue;
00910             }
00911             if (This->streams[streamId].pos == ~0U)
00912                 This->streams[streamId].pos = n;
00913 
00914             if (This->streams[streamId].streamheader.dwSampleSize)
00915                 This->streams[streamId].frames += This->oldindex->aIndex[n].dwSize / This->streams[streamId].streamheader.dwSampleSize;
00916             else
00917                 ++This->streams[streamId].frames;
00918         }
00919 
00920         for (x = 0; x < This->Parser.cStreams; ++x)
00921         {
00922             if ((DWORD)This->streams[x].frames != This->streams[x].streamheader.dwLength)
00923             {
00924                 FIXME("stream %u: frames found: %u, frames meant to be found: %u\n", x, (DWORD)This->streams[x].frames, This->streams[x].streamheader.dwLength);
00925             }
00926         }
00927 
00928     }
00929     else if (!This->streams[0].entries)
00930     {
00931         for (x = 0; x < This->Parser.cStreams; ++x)
00932         {
00933             This->streams[x].frames = This->streams[x].streamheader.dwLength;
00934         }
00935         /* MS Avi splitter does seek through the whole file, we should! */
00936         ERR("We should be manually seeking through the entire file to build an index, because the index is missing!!!\n");
00937         return E_NOTIMPL;
00938     }
00939 
00940     /* Not much here yet */
00941     for (x = 0; x < This->Parser.cStreams; ++x)
00942     {
00943         StreamData *stream = This->streams + x;
00944         DWORD y;
00945         DWORD64 frames = 0;
00946 
00947         stream->seek = 1;
00948 
00949         if (stream->stdindex)
00950         {
00951             stream->index = 0;
00952             stream->pos = 0;
00953             for (y = 0; y < stream->entries; ++y)
00954             {
00955                 if (stream->streamheader.dwSampleSize)
00956                 {
00957                     DWORD z;
00958 
00959                     for (z = 0; z < stream->stdindex[y]->nEntriesInUse; ++z)
00960                     {
00961                         UINT len = stream->stdindex[y]->aIndex[z].dwSize & ~(1 << 31);
00962                         frames += len / stream->streamheader.dwSampleSize + !!(len % stream->streamheader.dwSampleSize);
00963                     }
00964                 }
00965                 else
00966                     frames += stream->stdindex[y]->nEntriesInUse;
00967             }
00968         }
00969         else frames = stream->frames;
00970 
00971         frames *= stream->streamheader.dwScale;
00972         /* Keep accuracy as high as possible for duration */
00973         This->Parser.mediaSeeking.llDuration = frames * 10000000;
00974         This->Parser.mediaSeeking.llDuration /= stream->streamheader.dwRate;
00975         This->Parser.mediaSeeking.llStop = This->Parser.mediaSeeking.llDuration;
00976         This->Parser.mediaSeeking.llCurrent = 0;
00977 
00978         frames /= stream->streamheader.dwRate;
00979 
00980         TRACE("Duration: %d days, %d hours, %d minutes and %d.%03u seconds\n", (DWORD)(frames / 86400),
00981         (DWORD)((frames % 86400) / 3600), (DWORD)((frames % 3600) / 60), (DWORD)(frames % 60),
00982         (DWORD)(This->Parser.mediaSeeking.llDuration/10000) % 1000);
00983     }
00984 
00985     return S_OK;
00986 }
00987 
00988 static HRESULT AVISplitter_Disconnect(LPVOID iface);
00989 
00990 /* FIXME: fix leaks on failure here */
00991 static HRESULT AVISplitter_InputPin_PreConnect(IPin * iface, IPin * pConnectPin, ALLOCATOR_PROPERTIES *props)
00992 {
00993     PullPin *This = (PullPin *)iface;
00994     HRESULT hr;
00995     RIFFLIST list;
00996     LONGLONG pos = 0; /* in bytes */
00997     BYTE * pBuffer;
00998     RIFFCHUNK * pCurrentChunk;
00999     LONGLONG total, avail;
01000     ULONG x;
01001     DWORD indexes;
01002 
01003     AVISplitterImpl * pAviSplit = (AVISplitterImpl *)This->pin.pinInfo.pFilter;
01004 
01005     hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
01006     pos += sizeof(list);
01007 
01008     if (list.fcc != FOURCC_RIFF)
01009     {
01010         ERR("Input stream not a RIFF file\n");
01011         return E_FAIL;
01012     }
01013     if (list.fccListType != formtypeAVI)
01014     {
01015         ERR("Input stream not an AVI RIFF file\n");
01016         return E_FAIL;
01017     }
01018 
01019     hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
01020     if (list.fcc != FOURCC_LIST)
01021     {
01022         ERR("Expected LIST chunk, but got %.04s\n", (LPSTR)&list.fcc);
01023         return E_FAIL;
01024     }
01025     if (list.fccListType != listtypeAVIHEADER)
01026     {
01027         ERR("Header list expected. Got: %.04s\n", (LPSTR)&list.fccListType);
01028         return E_FAIL;
01029     }
01030 
01031     pBuffer = HeapAlloc(GetProcessHeap(), 0, list.cb - sizeof(RIFFLIST) + sizeof(RIFFCHUNK));
01032     hr = IAsyncReader_SyncRead(This->pReader, pos + sizeof(list), list.cb - sizeof(RIFFLIST) + sizeof(RIFFCHUNK), pBuffer);
01033 
01034     pAviSplit->AviHeader.cb = 0;
01035 
01036     /* Stream list will set the buffer size here, so set a default and allow an override */
01037     props->cbBuffer = 0x20000;
01038 
01039     for (pCurrentChunk = (RIFFCHUNK *)pBuffer; (BYTE *)pCurrentChunk + sizeof(*pCurrentChunk) < pBuffer + list.cb; pCurrentChunk = (RIFFCHUNK *)(((BYTE *)pCurrentChunk) + sizeof(*pCurrentChunk) + pCurrentChunk->cb))
01040     {
01041         RIFFLIST * pList;
01042 
01043         switch (pCurrentChunk->fcc)
01044         {
01045         case ckidMAINAVIHEADER:
01046             /* AVIMAINHEADER includes the structure that is pCurrentChunk at the moment */
01047             memcpy(&pAviSplit->AviHeader, pCurrentChunk, sizeof(pAviSplit->AviHeader));
01048             break;
01049         case FOURCC_LIST:
01050             pList = (RIFFLIST *)pCurrentChunk;
01051             switch (pList->fccListType)
01052             {
01053             case ckidSTREAMLIST:
01054                 hr = AVISplitter_ProcessStreamList(pAviSplit, (BYTE *)pCurrentChunk + sizeof(RIFFLIST), pCurrentChunk->cb + sizeof(RIFFCHUNK) - sizeof(RIFFLIST), props);
01055                 break;
01056             case ckidODML:
01057                 hr = AVISplitter_ProcessODML(pAviSplit, (BYTE *)pCurrentChunk + sizeof(RIFFLIST), pCurrentChunk->cb + sizeof(RIFFCHUNK) - sizeof(RIFFLIST));
01058                 break;
01059             }
01060             break;
01061         case ckidAVIPADDING:
01062             /* ignore */
01063             break;
01064         default:
01065             FIXME("unrecognised header list type: %.04s\n", (LPSTR)&pCurrentChunk->fcc);
01066         }
01067     }
01068     HeapFree(GetProcessHeap(), 0, pBuffer);
01069 
01070     if (pAviSplit->AviHeader.cb != sizeof(pAviSplit->AviHeader) - sizeof(RIFFCHUNK))
01071     {
01072         ERR("Avi Header wrong size!\n");
01073         return E_FAIL;
01074     }
01075 
01076     pos += sizeof(RIFFCHUNK) + list.cb;
01077     hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
01078 
01079     while (list.fcc == ckidAVIPADDING || (list.fcc == FOURCC_LIST && list.fccListType != listtypeAVIMOVIE))
01080     {
01081         pos += sizeof(RIFFCHUNK) + list.cb;
01082 
01083         hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
01084     }
01085 
01086     if (list.fcc != FOURCC_LIST)
01087     {
01088         ERR("Expected LIST, but got %.04s\n", (LPSTR)&list.fcc);
01089         return E_FAIL;
01090     }
01091     if (list.fccListType != listtypeAVIMOVIE)
01092     {
01093         ERR("Expected AVI movie list, but got %.04s\n", (LPSTR)&list.fccListType);
01094         return E_FAIL;
01095     }
01096 
01097     IAsyncReader_Length(This->pReader, &total, &avail);
01098 
01099     /* FIXME: AVIX files are extended beyond the FOURCC chunk "AVI ", and thus won't be played here,
01100      * once I get one of the files I'll try to fix it */
01101     if (hr == S_OK)
01102     {
01103         This->rtStart = pAviSplit->CurrentChunkOffset = MEDIATIME_FROM_BYTES(pos + sizeof(RIFFLIST));
01104         pos += list.cb + sizeof(RIFFCHUNK);
01105 
01106         pAviSplit->EndOfFile = This->rtStop = MEDIATIME_FROM_BYTES(pos);
01107         if (pos > total)
01108         {
01109             ERR("File smaller (%x%08x) then EndOfFile (%x%08x)\n", (DWORD)(total >> 32), (DWORD)total, (DWORD)(pAviSplit->EndOfFile >> 32), (DWORD)pAviSplit->EndOfFile);
01110             return E_FAIL;
01111         }
01112 
01113         hr = IAsyncReader_SyncRead(This->pReader, BYTES_FROM_MEDIATIME(pAviSplit->CurrentChunkOffset), sizeof(pAviSplit->CurrentChunk), (BYTE *)&pAviSplit->CurrentChunk);
01114     }
01115 
01116     props->cbAlign = 1;
01117     props->cbPrefix = 0;
01118     /* Comrades, prevent shortage of buffers, or you will feel the consequences! DA! */
01119     props->cBuffers = 2 * pAviSplit->Parser.cStreams;
01120 
01121     /* Now peek into the idx1 index, if available */
01122     if (hr == S_OK && (total - pos) > sizeof(RIFFCHUNK))
01123     {
01124         memset(&list, 0, sizeof(list));
01125 
01126         hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list);
01127         if (list.fcc == ckidAVIOLDINDEX)
01128         {
01129             pAviSplit->oldindex = CoTaskMemRealloc(pAviSplit->oldindex, list.cb + sizeof(RIFFCHUNK));
01130             if (pAviSplit->oldindex)
01131             {
01132                 hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(RIFFCHUNK) + list.cb, (BYTE *)pAviSplit->oldindex);
01133                 if (hr == S_OK)
01134                 {
01135                     hr = AVISplitter_ProcessOldIndex(pAviSplit);
01136                 }
01137                 else
01138                 {
01139                     CoTaskMemFree(pAviSplit->oldindex);
01140                     pAviSplit->oldindex = NULL;
01141                     hr = S_OK;
01142                 }
01143             }
01144         }
01145     }
01146 
01147     indexes = 0;
01148     for (x = 0; x < pAviSplit->Parser.cStreams; ++x)
01149         if (pAviSplit->streams[x].entries)
01150             ++indexes;
01151 
01152     if (indexes)
01153     {
01154         CoTaskMemFree(pAviSplit->oldindex);
01155         pAviSplit->oldindex = NULL;
01156         if (indexes < pAviSplit->Parser.cStreams)
01157         {
01158             /* This error could possible be survived by switching to old type index,
01159              * but I would rather find out why it doesn't find everything here
01160              */
01161             ERR("%d indexes expected, but only have %d\n", indexes, pAviSplit->Parser.cStreams);
01162             indexes = 0;
01163         }
01164     }
01165     else if (!indexes && pAviSplit->oldindex)
01166         indexes = pAviSplit->Parser.cStreams;
01167 
01168     if (!indexes && pAviSplit->AviHeader.dwFlags & AVIF_MUSTUSEINDEX)
01169     {
01170         FIXME("No usable index was found!\n");
01171         hr = E_FAIL;
01172     }
01173 
01174     /* Now, set up the streams */
01175     if (hr == S_OK)
01176         hr = AVISplitter_InitializeStreams(pAviSplit);
01177 
01178     if (hr != S_OK)
01179     {
01180         AVISplitter_Disconnect(pAviSplit);
01181         return E_FAIL;
01182     }
01183 
01184     TRACE("AVI File ok\n");
01185 
01186     return hr;
01187 }
01188 
01189 static HRESULT AVISplitter_Flush(LPVOID iface)
01190 {
01191     AVISplitterImpl *This = iface;
01192     DWORD x;
01193 
01194     TRACE("(%p)->()\n", This);
01195 
01196     for (x = 0; x < This->Parser.cStreams; ++x)
01197     {
01198         StreamData *stream = This->streams + x;
01199 
01200         if (stream->sample)
01201             assert(IMediaSample_Release(stream->sample) == 0);
01202         stream->sample = NULL;
01203 
01204         ResetEvent(stream->packet_queued);
01205         assert(!stream->thread);
01206     }
01207 
01208     return S_OK;
01209 }
01210 
01211 static HRESULT AVISplitter_Disconnect(LPVOID iface)
01212 {
01213     AVISplitterImpl *This = iface;
01214     ULONG x;
01215 
01216     /* TODO: Remove other memory that's allocated during connect */
01217     CoTaskMemFree(This->oldindex);
01218     This->oldindex = NULL;
01219 
01220     for (x = 0; x < This->Parser.cStreams; ++x)
01221     {
01222         DWORD i;
01223 
01224         StreamData *stream = &This->streams[x];
01225 
01226         for (i = 0; i < stream->entries; ++i)
01227             CoTaskMemFree(stream->stdindex[i]);
01228 
01229         CoTaskMemFree(stream->stdindex);
01230         CloseHandle(stream->packet_queued);
01231     }
01232     CoTaskMemFree(This->streams);
01233     This->streams = NULL;
01234     return S_OK;
01235 }
01236 
01237 static ULONG WINAPI AVISplitter_Release(IBaseFilter *iface)
01238 {
01239     AVISplitterImpl *This = (AVISplitterImpl *)iface;
01240     ULONG ref;
01241 
01242     ref = InterlockedDecrement(&This->Parser.refCount);
01243 
01244     TRACE("(%p)->() Release from %d\n", This, ref + 1);
01245 
01246     if (!ref)
01247     {
01248         AVISplitter_Flush(This);
01249         Parser_Destroy(&This->Parser);
01250     }
01251 
01252     return ref;
01253 }
01254 
01255 static HRESULT AVISplitter_seek(IBaseFilter *iface)
01256 {
01257     AVISplitterImpl *This = (AVISplitterImpl *)iface;
01258     PullPin *pPin = This->Parser.pInputPin;
01259     LONGLONG newpos, endpos;
01260     DWORD x;
01261 
01262     newpos = This->Parser.mediaSeeking.llCurrent;
01263     endpos = This->Parser.mediaSeeking.llDuration;
01264 
01265     if (newpos > endpos)
01266     {
01267         WARN("Requesting position %x%08x beyond end of stream %x%08x\n", (DWORD)(newpos>>32), (DWORD)newpos, (DWORD)(endpos>>32), (DWORD)endpos);
01268         return E_INVALIDARG;
01269     }
01270 
01271     FIXME("Moving position to %u.%03u s!\n", (DWORD)(newpos / 10000000), (DWORD)((newpos / 10000)%1000));
01272 
01273     EnterCriticalSection(&pPin->thread_lock);
01274     /* Send a flush to all output pins */
01275     IPin_BeginFlush((IPin *)pPin);
01276 
01277     /* Make sure this is done while stopped, BeginFlush takes care of this */
01278     EnterCriticalSection(&This->Parser.csFilter);
01279     for (x = 0; x < This->Parser.cStreams; ++x)
01280     {
01281         Parser_OutputPin *pin = (Parser_OutputPin *)This->Parser.ppPins[1+x];
01282         StreamData *stream = This->streams + x;
01283         IPin *victim = NULL;
01284         LONGLONG wanted_frames;
01285         DWORD last_keyframe = 0, last_keyframeidx = 0, preroll = 0;
01286 
01287         wanted_frames = newpos;
01288         wanted_frames *= stream->streamheader.dwRate;
01289         wanted_frames /= 10000000;
01290         wanted_frames /= stream->streamheader.dwScale;
01291 
01292         IPin_ConnectedTo((IPin *)pin, &victim);
01293         if (victim)
01294         {
01295             IPin_NewSegment(victim, newpos, endpos, pPin->dRate);
01296             IPin_Release(victim);
01297         }
01298 
01299         pin->dwSamplesProcessed = 0;
01300         stream->index = 0;
01301         stream->pos = 0;
01302         stream->seek = 1;
01303         if (stream->stdindex)
01304         {
01305             DWORD y, z = 0;
01306 
01307             for (y = 0; y < stream->entries; ++y)
01308             {
01309                 for (z = 0; z < stream->stdindex[y]->nEntriesInUse; ++z)
01310                 {
01311                     if (stream->streamheader.dwSampleSize)
01312                     {
01313                         ULONG len = stream->stdindex[y]->aIndex[z].dwSize & ~(1 << 31);
01314                         ULONG size = stream->streamheader.dwSampleSize;
01315 
01316                         pin->dwSamplesProcessed += len / size;
01317                         if (len % size)
01318                             ++pin->dwSamplesProcessed;
01319                     }
01320                     else ++pin->dwSamplesProcessed;
01321 
01322                     if (!(stream->stdindex[y]->aIndex[z].dwSize >> 31))
01323                     {
01324                         last_keyframe = z;
01325                         last_keyframeidx = y;
01326                         preroll = 0;
01327                     }
01328                     else
01329                         ++preroll;
01330 
01331                     if (pin->dwSamplesProcessed >= wanted_frames)
01332                         break;
01333                 }
01334                 if (pin->dwSamplesProcessed >= wanted_frames)
01335                     break;
01336             }
01337             stream->index = last_keyframeidx;
01338             stream->pos = last_keyframe;
01339         }
01340         else
01341         {
01342             DWORD nMax, n;
01343             nMax = This->oldindex->cb / sizeof(This->oldindex->aIndex[0]);
01344 
01345             for (n = 0; n < nMax; ++n)
01346             {
01347                 DWORD streamId = StreamFromFOURCC(This->oldindex->aIndex[n].dwChunkId);
01348                 if (streamId != x)
01349                     continue;
01350 
01351                 if (stream->streamheader.dwSampleSize)
01352                 {
01353                     ULONG len = This->oldindex->aIndex[n].dwSize;
01354                     ULONG size = stream->streamheader.dwSampleSize;
01355 
01356                     pin->dwSamplesProcessed += len / size;
01357                     if (len % size)
01358                         ++pin->dwSamplesProcessed;
01359                 }
01360                 else ++pin->dwSamplesProcessed;
01361 
01362                 if (This->oldindex->aIndex[n].dwFlags & AVIIF_KEYFRAME)
01363                 {
01364                     last_keyframe = n;
01365                     preroll = 0;
01366                 }
01367                 else
01368                     ++preroll;
01369 
01370                 if (pin->dwSamplesProcessed >= wanted_frames)
01371                     break;
01372             }
01373             assert(n < nMax);
01374             stream->pos = last_keyframe;
01375             stream->index = 0;
01376         }
01377         stream->preroll = preroll;
01378         stream->seek = 1;
01379     }
01380     LeaveCriticalSection(&This->Parser.csFilter);
01381 
01382     TRACE("Done flushing\n");
01383     IPin_EndFlush((IPin *)pPin);
01384     LeaveCriticalSection(&pPin->thread_lock);
01385 
01386     return S_OK;
01387 }
01388 
01389 static const IBaseFilterVtbl AVISplitterImpl_Vtbl =
01390 {
01391     Parser_QueryInterface,
01392     Parser_AddRef,
01393     AVISplitter_Release,
01394     Parser_GetClassID,
01395     Parser_Stop,
01396     Parser_Pause,
01397     Parser_Run,
01398     Parser_GetState,
01399     Parser_SetSyncSource,
01400     Parser_GetSyncSource,
01401     Parser_EnumPins,
01402     Parser_FindPin,
01403     Parser_QueryFilterInfo,
01404     Parser_JoinFilterGraph,
01405     Parser_QueryVendorInfo
01406 };
01407 
01408 HRESULT AVISplitter_create(IUnknown * pUnkOuter, LPVOID * ppv)
01409 {
01410     HRESULT hr;
01411     AVISplitterImpl * This;
01412 
01413     TRACE("(%p, %p)\n", pUnkOuter, ppv);
01414 
01415     *ppv = NULL;
01416 
01417     if (pUnkOuter)
01418         return CLASS_E_NOAGGREGATION;
01419 
01420     /* Note: This memory is managed by the transform filter once created */
01421     This = CoTaskMemAlloc(sizeof(AVISplitterImpl));
01422 
01423     This->streams = NULL;
01424     This->oldindex = NULL;
01425 
01426     hr = Parser_Create(&(This->Parser), &AVISplitterImpl_Vtbl, &CLSID_AviSplitter, AVISplitter_Sample, AVISplitter_QueryAccept, AVISplitter_InputPin_PreConnect, AVISplitter_Flush, AVISplitter_Disconnect, AVISplitter_first_request, AVISplitter_done_process, NULL, AVISplitter_seek, NULL);
01427 
01428     if (FAILED(hr))
01429         return hr;
01430 
01431     *ppv = This;
01432 
01433     return hr;
01434 }

Generated on Sat May 26 2012 04:20:27 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.