00001 #ifndef Impala_Core_Stream_Lavc_VideoAccessStrategy_h
00002 #define Impala_Core_Stream_Lavc_VideoAccessStrategy_h
00003
00004 #include "Core/Table/TableTem.h"
00005 #include "Core/Stream/Lavc/VideoAccessObject.h"
00006 #include "Core/Stream/Lavc/VideoIndex.h"
00007
00008 #include "Util/IOBufferFile.h"
00009 #include "Core/Table/Write.h"
00010
00011 namespace Impala
00012 {
00013 namespace Core
00014 {
00015 namespace Stream
00016 {
00017 namespace Lavc
00018 {
00019
00025 class VideoAccessStrategy
00026 {
00027
00028 protected:
00029
00030
00031 typedef Table::TableTem< Column::ColumnTem<Int32>,
00032 Column::ColumnTem<Int32>,
00033 Column::ColumnTem<Int32>,
00034 Column::ColumnTem<UInt64> > PacketTable;
00035
00036 typedef Column::FixedStringColumn::ColElemType FixedString;
00037
00038
00039 typedef Table::TableTem< Column::ColumnTem<Int32>,
00040 Column::ColumnTem<Int32>,
00041 Column::ColumnTem<Int32>,
00042 Column::ColumnTem<Int32>,
00043 Column::FixedStringColumn > FrameTable;
00044
00045 public:
00046
00047 static const int HASH_SIZE = 32;
00048 static const String INVALID_FRAME_HASH;
00049 static const String UNSTABLE_FRAME_HASH;
00050
00051
00052 VideoAccessStrategy(const Lavc::VideoAccessObject* const vao) : mVao(vao)
00053 {
00054 mVideoIsValid = false;
00055 mFailingKeyFrameNr = -1;
00056 mPackets = 0;
00057 mFrames = 0;
00058 mScanDataPresent = false;
00059 mNrOfFrames = -1;
00060 mBadFrameCount = -1;
00061 mLeadingBadFrameCount = -1;
00062 }
00063
00064 virtual
00065 ~VideoAccessStrategy()
00066 {
00067 CleanUp();
00068 if (mVao != 0)
00069 delete mVao;
00070 }
00071
00075 bool
00076 Scan()
00077 {
00078 if (!ResetVideoSrc())
00079 return false;
00080
00081 ILOG_INFO("Scanning video data...");
00082 mPackets = new PacketTable(1);
00083 mFrames = new FrameTable(Column::ColumnTem<Int32>(1),
00084 Column::ColumnTem<Int32>(1),
00085 Column::ColumnTem<Int32>(1),
00086 Column::ColumnTem<Int32>(1),
00087 Column::FixedStringColumn(HASH_SIZE, 1));
00088 mNrOfFrames = 0;
00089 mBadFrameCount = 0;
00090 mLeadingBadFrameCount = 0;
00091
00092 bool scanSuccess = ScanProtected();
00093 ResetVideoSrc();
00094 if (!scanSuccess)
00095 {
00096 CleanUp();
00097 return false;
00098 }
00099
00100 mScanDataPresent = true;
00101 mNrOfFrames = mFrames->Size();
00102 ILOG_INFO("Finished video scan; frame count established: " <<
00103 NrOfFrames());
00104 return true;
00105 }
00106
00107
00108 const VideoAccessObject* const
00109 VideoAccessObj() const
00110 {
00111 return mVao;
00112 }
00113
00114 virtual bool
00115 ResetVideoSrc() const
00116 {
00117 return mVao->Reset();
00118 }
00119
00120 bool
00121 VideoIsValid() const
00122 {
00123 return mVideoIsValid;
00124 }
00125
00126 virtual int
00127 NrOfFrames() const
00128 {
00129 return mNrOfFrames;
00130 }
00131
00132 int
00133 LeadingBadFrames() const
00134 {
00135 return mLeadingBadFrameCount;
00136 }
00137
00138 bool
00139 FrameIsValid(int frameNr) const
00140 {
00141 return mFrames->Get3(frameNr) != 0;
00142 }
00143
00144 char
00145 FrameType() const
00146 {
00147 return mVao->FrameType();
00148 }
00149
00150 UInt8*
00151 RgbDataPtr() const
00152 {
00153 return mVao->RgbDataPtr();
00154 }
00155
00159 VideoIndex*
00160 ConstructIndex()
00161 {
00162 if (!mScanDataPresent)
00163 {
00164 ILOG_ERROR("[ConstructIndex] No scan data");
00165 return 0;
00166 }
00167
00168 ResetVideoSrc();
00169 VideoIndex* videoIndex = ConstructIndexPrivate();
00170 if (videoIndex == 0)
00171 return 0;
00172
00173 const double percIndexedFrames = 100.0 *
00174 videoIndex->NrOfEntries() / NrOfFrames();
00175 const double percentageSeekable = 100.0 *
00176 videoIndex->NrOfSeekableFrames() / videoIndex->NrOfEntries();
00177 const int maxNonTargetFrameReads = videoIndex->MaxNonTargetFrameReads();
00178
00179 ILOG_INFO("Number of entries in index: " <<
00180 videoIndex->NrOfEntries() << " (" << percIndexedFrames <<
00181 " %)");
00182 ILOG_INFO("Number of seekable entries in index: " <<
00183 videoIndex->NrOfSeekableFrames() << " (" << percentageSeekable <<
00184 " %)");
00185 ILOG_INFO("Max. frame reads required to get to any frame: " <<
00186 maxNonTargetFrameReads + 1);
00187
00188 return videoIndex;
00189 }
00190
00191 virtual void
00192 CleanUp()
00193 {
00194 if (mPackets != 0)
00195 {
00196 delete mPackets;
00197 mPackets = 0;
00198 }
00199 if (mFrames != 0)
00200 {
00201 delete mFrames;
00202 mFrames = 0;
00203 }
00204 mScanDataPresent = false;
00205 mFailingKeyFrameNr = -1;
00206 }
00207
00211 virtual int
00212 DecodeNextFrame(bool* isValidFrame, bool* isKeyFrame, PacketTrace* pktTrace) const
00213 {
00214
00215
00216 const int MAX_READ_FAILURES = Max(15, 5 * mVao->GopSize());
00217 int nrOfReadFailures = 0;
00218 bool atEndOfFile = mVao->AtEof();
00219
00220 bool readPacketFailed = false;
00221 *isValidFrame = false;
00222 *isKeyFrame = false;
00223 int rc = 0;
00224 while (true)
00225 {
00226 int readPacketResult = mVao->ReadPacket();
00227 readPacketFailed = (readPacketResult < 0);
00228 if (readPacketFailed)
00229 {
00230
00231
00232 ILOG_DEBUG("ReadPacket() failed and returned: " <<
00233 readPacketResult);
00234 }
00235 else if (pktTrace)
00236 pktTrace->Add(mVao->CurrentPacketSize(), mVao->CurrentPacketFlags(),
00237 mVao->CurrentFilePosition(), mVao->CurrentPacketIsVideo());
00238
00239 if (mVao->CurrentPacketIsVideo())
00240 {
00241 int frameDecoded = -1;
00242 int len = mVao->DecodeFrame(&frameDecoded);
00243
00244
00245 int doDecodeTwice = false;
00246 if (doDecodeTwice)
00247 {
00248
00249
00250
00251
00252 len = mVao->DecodeFrame(&frameDecoded);
00253 }
00254
00255 if (frameDecoded != 0)
00256 {
00257 *isValidFrame = true;
00258 *isKeyFrame =
00259 (mVao->FrameType() == 'I' && mVao->FrameIsKey());
00260 break;
00261 }
00262 else
00263 {
00264
00265 if (atEndOfFile)
00266 {
00267 rc = -3;
00268 break;
00269 }
00270
00271
00272
00273 if (mVao->CurrentPacketFlags() == 0)
00274 break;
00275 }
00276 }
00277
00278
00279
00280 nrOfReadFailures++;
00281 if ((atEndOfFile || readPacketFailed)
00282 && (nrOfReadFailures > MAX_READ_FAILURES))
00283 {
00284 ILOG_WARN("Max. nr of frame read failures reached (" <<
00285 MAX_READ_FAILURES << "); probably at end of file");
00286 rc = -1;
00287 break;
00288 }
00289 }
00290
00291 return rc;
00292 }
00293
00294 int
00295 CurrentFrameToRgb() const
00296 {
00297 return mVao->CurrentFrameToRgb();
00298 }
00299
00300 String
00301 FrameHash(int frameNr) const
00302 {
00303 if (mScanDataPresent)
00304 return String(mFrames->Get5(frameNr).GetData(), HASH_SIZE);
00305
00306 ILOG_ERROR("[FrameHash] No scan data");
00307 return "";
00308 }
00309
00310 bool
00311 JumpToFramePosition(const UInt64& target) const
00312 {
00313 const bool mustBeKey = true;
00314 const int flushMode = 2;
00315 return Jump(target, GetJumpFlags(), mustBeKey, flushMode);
00316 }
00317
00318 bool
00319 FrameIsStable(int frameNr) const
00320 {
00321 if (mScanDataPresent)
00322 return FrameHash(frameNr) != UNSTABLE_FRAME_HASH;
00323 else
00324 ILOG_ERROR("[FrameHash] No scan data");
00325 return true;
00326 }
00327
00328
00329 virtual bool
00330 FrameCanBeRead(int frameNr) const = 0;
00331
00332
00333 protected:
00334
00335 virtual int
00336 GetJumpFlags() const = 0;
00337
00338 virtual bool
00339 ScanProtected() = 0;
00340
00341
00342 virtual VideoIndex*
00343 ConstructIndexProtected() const = 0;
00344
00353 virtual bool
00354 Jump(const UInt64& target, int flags, bool mustBeKey = false,
00355 int flush = 0, PacketTrace* trace = 0) const
00356 {
00357 if (flush & 1)
00358 mVao->FlushCodecContext();
00359
00360 if (!mVao->Seek(target, flags))
00361 return false;
00362
00363 if (flush & 2)
00364 mVao->FlushCodecContext();
00365
00366 return DecodeNextValidFrame(mustBeKey, trace);
00367 }
00368
00369 virtual bool
00370 DecodeNextValidFrame(bool lookForKeyFrame, PacketTrace* pktTrace = 0) const
00371 {
00372 bool isValidFrame;
00373 bool isKeyFrame;
00374 do
00375 {
00376 if (DecodeNextFrame(&isValidFrame, &isKeyFrame, pktTrace) != 0)
00377
00378 return false;
00379 if (!isValidFrame)
00380 {
00381
00382 continue;
00383 }
00384 if (lookForKeyFrame && !isKeyFrame)
00385 {
00386
00387 continue;
00388 }
00389 break;
00390 } while (true);
00391
00392 return true;
00393 }
00394
00395
00396 void
00397 ProcessFrame(bool isValid, bool isKey, int frameNr, bool validating = false)
00398 {
00399 static const FixedString invalidAsFixedStr(
00400 HASH_SIZE, (char*) INVALID_FRAME_HASH.c_str(), true);
00401
00402 if (validating)
00403 {
00404 if (isValid)
00405 {
00406 if (!FrameIsValid(frameNr) ||
00407 (isKey && !FrameIsKey(frameNr)) ||
00408 (!isKey && FrameIsKey(frameNr)))
00409 {
00410 ILOG_ERROR("Unsupported instability type detected for" <<
00411 " frame " << frameNr << " (" << FrameIsValid(frameNr) <<
00412 "->" << isValid << ", " << FrameIsKey(frameNr) <<
00413 "->" << isKey << ")");
00414 }
00415 else
00416 {
00417 mVao->CurrentFrameToRgb();
00418 if (!CurrentHashMatchesFrame(frameNr))
00419 MarkFrameUnstable(frameNr);
00420 }
00421 }
00422 else if (FrameIsValid(frameNr))
00423 ILOG_ERROR("Unsupported instability type detected for" <<
00424 " frame " << frameNr << " (" << FrameIsValid(frameNr) <<
00425 "->" << isValid << ")");
00426
00427 ILOG_DEBUG("Re-scanned frame " << frameNr);
00428 }
00429
00430 else
00431 {
00432 String hash = INVALID_FRAME_HASH;
00433 if (isValid)
00434 {
00435 mVao->CurrentFrameToRgb();
00436 hash = mVao->CurrentHash();
00437 FixedString hashAsFixedStr(HASH_SIZE, (char*) hash.c_str(), true);
00438 AddFrameToTable(isValid, isKey, hash, hashAsFixedStr);
00439 }
00440 else
00441 AddFrameToTable(false, false, INVALID_FRAME_HASH, invalidAsFixedStr);
00442
00443 ILOG_DEBUG("Scanned frame " << frameNr << " [" <<
00444 (isValid ? mVao->FrameType() : '?') << "] " << hash);
00445 }
00446 }
00447
00448 bool
00449 FrameIsKey(int frameNr) const
00450 {
00451 return mFrames->Get4(frameNr) != 0;
00452 }
00453
00454 bool
00455 CurrentFrameMatchesFrame(int frameNr, const PacketTrace* trace = 0) const
00456 {
00457 if (mPackets != 0 && mPackets->Size() > 0)
00458 {
00459 const int packetNr = mFrames->Get2(frameNr);
00460 const int packetSize = mPackets->Get2(packetNr);
00461 if (mVao->CurrentPacketSize() != packetSize)
00462 return false;
00463 }
00464
00465 if (!CurrentHashMatchesFrame(frameNr))
00466 return false;
00467
00468 if (trace && !PacketTraceMatchesFrame(*trace, frameNr))
00469 {
00470 ILOG_DEBUG("Packet trace does not match for frame " << frameNr);
00471 return false;
00472 }
00473
00474 return true;
00475 }
00476
00477 bool
00478 CurrentHashMatchesFrame(int frameNr) const
00479 {
00480 bool matched = false;
00481 const String currentHash = mVao->CurrentHash();
00482 if (currentHash.size() == HASH_SIZE)
00483 {
00484 const char* currentHashStr = currentHash.c_str();
00485 const char* frameHash =
00486 mFrames->GetColumn5()->GetVectorData(frameNr);
00487 matched = true;
00488 for (int i = 0; i < HASH_SIZE; i++)
00489 if (frameHash[i] != currentHashStr[i])
00490 {
00491 matched = false;
00492 break;
00493 }
00494 }
00495
00496 if (!matched)
00497 ILOG_DEBUG("Hash does not match for frame " <<
00498 frameNr << "; found: " << mVao->CurrentHash());
00499 return matched;
00500 }
00501
00502 virtual bool
00503 NextFrameMatchesFrame(int frameNr, const VideoIndex& videoIndex) const = 0;
00504
00505 bool
00506 ScanDataPresent()
00507 {
00508 return mScanDataPresent;
00509 }
00510
00511 virtual void
00512 MarkFrameUnstable(int frameNr)
00513 {
00514 ILOG_INFO("Instability detected for frame: " << frameNr);
00515 static const FixedString unstableAsFixedStr(
00516 HASH_SIZE, (char*) UNSTABLE_FRAME_HASH.c_str(), true);
00517 mFrames->Set5(frameNr, unstableAsFixedStr);
00518 }
00519
00520
00521 void
00522 WriteScanData(CString fileName)
00523 {
00524 Util::IOBufferFile buf(fileName, false, false);
00525 Table::Write(mPackets, &buf, true);
00526 ILOG_INFO("Wrote " << mPackets->Size() << " packets to file");
00527 Table::Write(mFrames, &buf, true);
00528 ILOG_INFO("Wrote " << mFrames->Size() << " frames to file");
00529 }
00530
00531
00532 void
00533 ReadScanData(CString fileName)
00534 {
00535 Util::IOBufferFile buf(fileName, true, false);
00536
00537 if (mPackets)
00538 mPackets->SetEmpty();
00539 else
00540 mPackets = new PacketTable(1);
00541 Table::Read(mPackets, &buf);
00542 ILOG_INFO("Read " << mPackets->Size() << " packets from file");
00543
00544 if (mFrames)
00545 mFrames->SetEmpty();
00546 else
00547 mFrames = new FrameTable(Column::ColumnTem<Int32>(1),
00548 Column::ColumnTem<Int32>(1),
00549 Column::ColumnTem<Int32>(1),
00550 Column::ColumnTem<Int32>(1),
00551 Column::FixedStringColumn(HASH_SIZE, 1));
00552 Table::Read(mFrames, &buf);
00553 ILOG_INFO("Read " << mFrames->Size() << " frames from file");
00554
00555 bool oldScanDataPresent = mScanDataPresent;
00556 mScanDataPresent = true;
00557 mNrOfFrames = mFrames->Size();
00558 mBadFrameCount = 0;
00559 mLeadingBadFrameCount = 0;
00560 for (int f = 0; f < mNrOfFrames; f++)
00561 if (!FrameIsValid(f))
00562 {
00563 mBadFrameCount++;
00564 if (mBadFrameCount > f)
00565 mLeadingBadFrameCount++;
00566 }
00567 mVideoIsValid = true;
00568 mScanDataPresent = oldScanDataPresent;
00569 }
00570
00571 void
00572 SetNrOfFrames(int count)
00573 {
00574 mNrOfFrames = count;
00575 }
00576
00577 const VideoAccessObject* const mVao;
00578
00579 PacketTable* mPackets;
00580 FrameTable* mFrames;
00581
00582 int mBadFrameCount;
00583 int mLeadingBadFrameCount;
00584
00585 bool mScanDataPresent;
00586
00587
00588
00589
00590
00591 private:
00592
00593 void
00594 AddFrameToTable(bool isValid, bool isKey, CString hash,
00595 const FixedString& hashAsFixedStr)
00596 {
00597 int frameIdx = mFrames->Size();
00598 const int lastPacketEntry = mPackets->Size() - 1;
00599 mFrames->Add(frameIdx, lastPacketEntry, isValid, isKey,
00600 hashAsFixedStr);
00601 }
00602
00603
00604 VideoIndex*
00605 ConstructIndexPrivate()
00606 {
00607 VideoIndex* videoIndex = ConstructIndexProtected();
00608 if (videoIndex == 0)
00609 return 0;
00610
00611
00612 while (!IndexValid(*videoIndex))
00613 {
00614 int failingFrameNr = mFailingKeyFrameNr;
00615 ILOG_DEBUG("Disabling invalid video index entry for frame " <<
00616 failingFrameNr);
00617 if (!videoIndex->SetNotSeekable(failingFrameNr))
00618 {
00619 ILOG_ERROR("Index entry could not be disabled for frame " <<
00620 failingFrameNr);
00621 delete videoIndex;
00622 return 0;
00623 }
00624 }
00625
00626 ILOG_DEBUG("Index validated");
00627
00628 return videoIndex;
00629 }
00630
00631 bool
00632 IndexValid(const VideoIndex& idx) const
00633 {
00634 ILOG_DEBUG("Validating index...");
00635 return IndexValidTraversingBackwards(idx) &&
00636 IndexValidTraversingForwards(idx);
00637 }
00638
00639 bool
00640 IndexValidTraversingBackwards(const VideoIndex& idx) const
00641 {
00642 const int startFrame =
00643 (mFailingKeyFrameNr < 0) ? NrOfFrames() : mFailingKeyFrameNr;
00644 for (int f = startFrame; f > 0 ; )
00645 if (idx.IsSeekable(--f))
00646 {
00647
00648 if (!JumpValidForFrame(f, idx))
00649 {
00650 mFailingKeyFrameNr = f;
00651 return false;
00652 }
00653 }
00654 return true;
00655 }
00656
00657 bool
00658 IndexValidTraversingForwards(const VideoIndex& idx) const
00659 {
00660 int startFrame = 0;
00661 for (int f = startFrame; f < mFailingKeyFrameNr; f++)
00662 if (idx.IsSeekable(f))
00663 startFrame = f;
00664
00665 ResetVideoSrc();
00666 for (int f = startFrame; f < NrOfFrames(); f++)
00667 if (idx.IsSeekable(f) && !JumpValidForFrame(f, idx))
00668 {
00669 mFailingKeyFrameNr = f;
00670 return false;
00671 }
00672 return true;
00673 }
00674
00679 bool
00680 JumpValidForFrame(int frameNr, const VideoIndex& videoIndex) const
00681 {
00682 int seekableFrame = -1;
00683 UInt64 seekTarget = -1;
00684 videoIndex.GetSeekInfo(frameNr, &seekableFrame, &seekTarget);
00685 static const bool MUST_BE_KEY = true;
00686 const int flushMode = 2;
00687 Jump(seekTarget, GetJumpFlags(), MUST_BE_KEY, flushMode);
00688 mVao->CurrentFrameToRgb();
00689 if (!CurrentFrameMatchesFrame(seekableFrame))
00690 {
00691 ILOG_DEBUG("Index failed for (seekable) frame " << frameNr);
00692 return false;
00693 }
00694 ILOG_DEBUG("Jump validated for (seekable) frame " << frameNr);
00695
00696
00697 bool includedOneSeekable = false;
00698 while (++frameNr < NrOfFrames())
00699 {
00700 if (videoIndex.IsSeekable(frameNr))
00701 if (includedOneSeekable)
00702 break;
00703 else
00704 includedOneSeekable = true;
00705
00706 if (!NextFrameMatchesFrame(frameNr, videoIndex))
00707 return false;
00708 }
00709
00710 return true;
00711 }
00712
00727 bool
00728 PacketTraceMatchesFrame(const PacketTrace& trace, int frameNr) const
00729 {
00730 const int nrOfFrames = mFrames->Size();
00731 if (frameNr < 0 || frameNr >= nrOfFrames)
00732 return false;
00733
00734
00735
00736
00737 const int traceLen = trace.Size();
00738
00739 int nrOfVideoPackets = 0;
00740 for (int t = 0; t < traceLen; t++)
00741 if (trace.Get4(t))
00742 nrOfVideoPackets++;
00743 int nrOfNonVideoPackets = traceLen - nrOfVideoPackets;
00744
00745 int packetIdx = mFrames->Get2(frameNr);
00746 for (int t = traceLen - 1; t >= 0; t--)
00747 {
00748 if (packetIdx < 0)
00749 return false;
00750
00751 const int sizeFromPackets = mPackets->Get2(packetIdx);
00752 const int sizeFromTrace = trace.Get1(t);
00753 const int flagsFromPackets = mPackets->Get3(packetIdx);
00754 const int flagsFromTrace = trace.Get2(t);
00755 bool sizeAndFlagsMatch =
00756 (sizeFromPackets == sizeFromTrace &&
00757 flagsFromPackets == flagsFromTrace);
00758
00759 if (trace.Get4(t))
00760 {
00761 if (--nrOfVideoPackets <= 0)
00762 sizeAndFlagsMatch = true;
00763 }
00764 else
00765 if (--nrOfNonVideoPackets <= 0)
00766 sizeAndFlagsMatch = true;
00767
00768 if (!sizeAndFlagsMatch)
00769 {
00770 ILOG_DEBUG("Size and/or flags don't match at trace pos " << t <<
00771 " of " << traceLen << " : " << sizeFromPackets <<
00772 " vs. " << sizeFromTrace << " ; " << flagsFromPackets <<
00773 " vs. " << flagsFromTrace);
00774 return false;
00775 }
00776
00777 packetIdx--;
00778
00779
00780 break;
00781 }
00782
00783
00784 packetIdx = mFrames->Get2(frameNr);
00785 const UInt64 postReadPosOfFrame = mPackets->Get4(packetIdx);
00786 const UInt64 lastPosOfTrace = trace.Get3(traceLen - 1);
00787 if (lastPosOfTrace > postReadPosOfFrame)
00788 {
00789
00790 int nextKeyFrame = frameNr + 1;
00791 for ( ; nextKeyFrame < nrOfFrames; nextKeyFrame++)
00792 if (mFrames->Get4(nextKeyFrame))
00793 break;
00794 if (nextKeyFrame >= nrOfFrames)
00795 return true;
00796
00797
00798
00799 packetIdx = mFrames->Get2(nextKeyFrame);
00800 const UInt64 nextKeyFramesFilePos = mPackets->Get4(packetIdx);
00801 if (lastPosOfTrace > nextKeyFramesFilePos)
00802 return false;
00803 }
00804
00805 return true;
00806 }
00807
00808
00809 bool mVideoIsValid;
00810 int mNrOfFrames;
00811 mutable int mFailingKeyFrameNr;
00812
00813 ILOG_VAR_DECL;
00814
00815 };
00816
00817 ILOG_VAR_INIT(VideoAccessStrategy, Impala.Core.Stream.Lavc);
00818
00819 const String VideoAccessStrategy::INVALID_FRAME_HASH = "INVALID_FRAME___________________";
00820 const String VideoAccessStrategy::UNSTABLE_FRAME_HASH = "UNSTABLE_FRAME__________________";
00821
00822 }}}}
00823
00824 #endif