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