Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenavisplit.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
1.7.6.1
|