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