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