00001 #ifndef Impala_Core_Stream_RgbDataSrcLavc_h
00002 #define Impala_Core_Stream_RgbDataSrcLavc_h
00003
00004 #include "Basis/CmdOptions.h"
00005 #include "Util/Database.h"
00006
00007 #include "Core/Stream/RgbDataSrcInfo.h"
00008
00009 #include "Core/Stream/LavcProtocolDataServer.h"
00010
00011 #include "Core/Stream/Lavc/VideoAccessStrategyFactory.h"
00012 #include "Core/Stream/Lavc/VideoAccessStrategy.h"
00013 #include "Core/Stream/Lavc/VideoIndex.h"
00014
00015 namespace Impala
00016 {
00017 namespace Core
00018 {
00019 namespace Stream
00020 {
00021
00027 class RgbDataSrcLavc : public RgbDataSrcInfo
00028 {
00029
00030 typedef Array::Array2dVec3UInt8 Array2dVec3UInt8;
00031
00032 public:
00033
00034 #ifndef REPOSITORY_USED // Here comes the deprecated stuff
00035 RgbDataSrcLavc(int src, CString srcName, Util::Database* db,
00036 IndexMode mode)
00037 : RgbDataSrcInfo(src, srcName, db,
00038 DetermineInfoName(srcName, mode, db), true)
00039 {
00040 #else // REPOSITORY_USED
00041 RgbDataSrcLavc(int src, const Persistency::RgbDataSrcLocator& loc,
00042 IndexMode mode)
00043 : RgbDataSrcInfo(src, loc, "", true)
00044 {
00045 mLoc = loc;
00046 String srcName = loc.GetName();
00047 #endif // REPOSITORY_USED
00048 mSrcName = StringReplaceAll(srcName, "\\", "/", false);
00049 mMode = mode;
00050
00051 mValid = false;
00052 mBufferedFrameNr = -1;
00053 mLastDecodedFrameNr = -1;
00054
00055 mFixCnt = 0;
00056 mLastFixNr = 0;
00057
00058 mVideoAccessor = 0;
00059
00060 Init();
00061 }
00062
00063 virtual
00064 ~RgbDataSrcLavc()
00065 {
00066 if (mVideoAccessor != 0)
00067 delete mVideoAccessor;
00068 }
00069
00070 virtual char
00071 FrameType() const
00072 {
00073 return mVideoAccessor->FrameType();
00074 }
00075
00076 virtual String
00077 FrameHashBeforeConversion() const
00078 {
00079 if (FrameValid(FrameNr()))
00080 return mVideoAccessor->CurrentFrameHashBeforeConversion();
00081 else
00082 return INVALID_FRAME_HASH;
00083 }
00084
00085 virtual bool
00086 CurIsIFrame() const
00087 {
00088 return FrameType() == 'I';
00089 }
00090
00091 virtual bool
00092 Valid() const
00093 {
00094 return mValid;
00095 }
00096
00097
00098 protected:
00099
00100 virtual String
00101 CalcHash() const
00102 {
00103 return mVideoAccessor->CurrentFrameHash();
00104 }
00105
00106
00107 private:
00108
00109 bool
00110 IncurExtraLavcInitialization()
00111 {
00112 Reset();
00113 const int firstValidFrame = LeadingBadFrames();
00114 if (!SeekFrame(firstValidFrame))
00115 return false;
00116 mLastDecodedFrameNr = mBufferedFrameNr = mCurrentFrameNr = firstValidFrame;
00117 return true;
00118 }
00119
00120 bool
00121 ReadIndex(bool silent)
00122 {
00123 const int currentFrameCount = mLastFrame + 1;
00124 const int currentBadFramesCount = BadFrames();
00125 const int currentLeadingBadCount = LeadingBadFrames();
00126 const bool wasRead = ReadInfo(silent);
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155 return wasRead;
00156 }
00157
00158 bool
00159 SeekFrame(int seekableFrame)
00160 {
00161 const UInt64 targetPos = GetPos(seekableFrame);
00162 ILOG_DEBUG("Seeking frame " << seekableFrame << " at position " <<
00163 targetPos);
00164 if (!mVideoAccessor->JumpToFramePosition(targetPos))
00165 {
00166 ILOG_ERROR("Jump to frame position " << targetPos << " failed");
00167 return false;
00168 }
00169 return true;
00170 }
00171
00172 void
00173 Init()
00174 {
00175 CmdOptions& options = CmdOptions::GetInstance();
00176 #ifndef REPOSITORY_USED // Here comes the deprecated stuff
00177 String dataServer = options.GetString("dataServer");
00178 #else // REPOSITORY_USED
00179 String dataServer = (mLoc.GetProtocol() == "dataServer") ? "dataserver"
00180 : "";
00181 #endif // REPOSITORY_USED
00182 const String dataSrcAddress =
00183 dataServer.empty() ? mSrcName : "dataserver:" + mSrcName;
00184 Lavc::VideoAccessStrategyFactory factory;
00185 mVideoAccessor = factory.Construct(dataSrcAddress);
00186 if (mVideoAccessor == 0)
00187 {
00188 mValid = false;
00189 return;
00190 }
00191
00192 if (mVideoAccessor->NrOfFrames() >= 0)
00193 mLastFrame = mVideoAccessor->NrOfFrames() - 1;
00194
00195 InitStaticMetadata(*mVideoAccessor);
00196
00197 mData = mVideoAccessor->RgbDataPtr();
00198
00199 if (mMode == SCANONLY)
00200 {
00201 if (!Scan(*mVideoAccessor))
00202 return;
00203 }
00204 else if (mMode == READIDX)
00205 {
00206 int nrOfFramesNow = -1;
00207 bool checkFrameCount = options.GetBool("checkstoredframecount", false);
00208 if (checkFrameCount)
00209 {
00210 if (!Scan(*mVideoAccessor))
00211 return;
00212 nrOfFramesNow = mVideoAccessor->NrOfFrames();
00213 }
00214
00215 if (ReadIndex(false))
00216 {
00217 if (!IncurExtraLavcInitialization())
00218 return;
00219 if (checkFrameCount && (nrOfFramesNow != (LastFrame() + 1)))
00220 {
00221 ILOG_ERROR("Freshly established frame count (" <<
00222 nrOfFramesNow << ") different " <<
00223 "from stored frame count (" << LastFrame() + 1 <<
00224 ") for: " << mSrcName);
00225 return;
00226 }
00227 }
00228 else
00229 {
00230 ILOG_ERROR("Failed while reading index info for: " <<
00231 mSrcName);
00232 return;
00233 }
00234 }
00235 else if (mMode == WITHIDX)
00236 {
00237 if (ReadIndex(true))
00238 {
00239 if (!IncurExtraLavcInitialization())
00240 return;
00241 }
00242 else
00243 {
00244 if (!PopulateIndex(*mVideoAccessor))
00245 return;
00246 }
00247 }
00248 else if (mMode == ADHOCIDX)
00249 {
00250 if (!PopulateIndex(*mVideoAccessor))
00251 return;
00252 }
00253 else if (mMode == WRITEIDX)
00254 {
00255 if (!PopulateIndex(*mVideoAccessor))
00256 return;
00257 WriteInfo();
00258 }
00259
00260 Reset();
00261 mValid = true;
00262 ILOG_DEBUG("Initialization succeeded");
00263 }
00264
00265
00266 void
00267 InitStaticMetadata(const Lavc::VideoAccessStrategy& videoAccessStrategy)
00268 {
00269
00270
00271 const Lavc::VideoAccessObject* const vao =
00272 videoAccessStrategy.VideoAccessObj();
00273
00274
00275 mBitRate = vao->BitRate();
00276 mFormatName = vao->FormatNameShort();
00277 mFormatLName = vao->FormatNameLong();
00278 ILOG_INFO("Format name is: " << mFormatLName << " (" <<
00279 mFormatName << ")");
00280
00281
00282 mStreamCount = vao->StreamCount();
00283 mVideoDuration = vao->VideoDuration();
00284 mTimeBaseNum = vao->VideoTimeBaseNumerator();
00285 mTimeBaseDen = vao->VideoTimeBaseDenominator();
00286 mDuration = ceil(
00287 double(mVideoDuration) * mTimeBaseNum / mTimeBaseDen
00288 );
00289 mDurationSec = mDuration;
00290 mDurationHour = mDurationSec / 3600;
00291 mDurationSec %= 3600;
00292 mDurationMin = mDurationSec / 60;
00293 mDurationSec %= 60;
00294
00295
00296 mFrameRateNum = vao->FrameRateNumerator();
00297 mFrameRateDen = vao->FrameRateDenominator();
00298 const double fractionsPerSec = double(mTimeBaseDen) / mTimeBaseNum;
00299 const double secondsPerFrame = double(mFrameRateDen) / mFrameRateNum;
00300 const double fractionsPerFrame = fractionsPerSec * secondsPerFrame;
00301 const double frameCount = mVideoDuration / fractionsPerFrame;
00302 mFrameCountCalculated = ceil(frameCount);
00303 ILOG_INFO("Frame count estimate based on video duration: " <<
00304 mFrameCountCalculated);
00305
00306 mFrameWidth = vao->FrameWidth();
00307 mFrameHeight = vao->FrameHeight();
00308 mAspectRatioNum = vao->AspectRatioNum();
00309 mAspectRatioDen = vao->AspectRatioDen();
00310
00311 mGopSize = vao->GopSize();
00312
00313 mCodecTag = vao->VideoCodecTag();
00314 mCodecName = vao->VideoCodecName();
00315
00316 ILOG_INFO("Opened codec (id=" << vao->VideoCodecId() <<
00317 ", name=" << mCodecName << ") for: " << mSrcName);
00318 }
00319
00320 void
00321 Reset()
00322 {
00323 mVideoAccessor->ResetVideoSrc();
00324 mLastDecodedFrameNr = mBufferedFrameNr = mCurrentFrameNr = -1;
00325 }
00326
00327 bool PopulateIndex(Lavc::VideoAccessStrategy& accessStrategy)
00328 {
00329 if (!Scan(accessStrategy))
00330 return false;
00331
00332 ILOG_INFO("Starting index population...");
00333 ILOG_INFO("Constructing intermediate index...");
00334 Lavc::VideoIndex* tempIndex = accessStrategy.ConstructIndex();
00335 if (!tempIndex)
00336 {
00337 ILOG_ERROR("Construction of intermediate index failed; " <<
00338 "aborting video population");
00339 return false;
00340 }
00341
00342
00343 const int nrOfFrames = accessStrategy.NrOfFrames();
00344 String frameHash;
00345 int seekableFrameNr;
00346 UInt64 seekableFramePos = 0;
00347 for (int f = 0; f < nrOfFrames; f++)
00348 {
00349 if (accessStrategy.FrameIsValid(f))
00350 {
00351 frameHash = accessStrategy.FrameHash(f);
00352 tempIndex->GetSeekInfo(f, &seekableFrameNr, &seekableFramePos);
00353 }
00354 else
00355 {
00356 MarkBadFrame(f);
00357 frameHash = DUMMY_FRAME_HASH;
00358 seekableFrameNr = -1;
00359 seekableFramePos = -1;
00360 }
00361
00362 ILOG_DEBUG("Adding to index: " << f << ", " << seekableFrameNr <<
00363 ", " << seekableFramePos << ", " << frameHash);
00364 AddIndex(f, seekableFrameNr, seekableFramePos, frameHash);
00365 }
00366
00367 delete tempIndex;
00368 accessStrategy.CleanUp();
00369 mIndexExists = true;
00370 ILOG_INFO("Finished index population");
00371 return true;
00372 }
00373
00374 bool
00375 Scan(Lavc::VideoAccessStrategy& accessStrategy)
00376 {
00377 if (!accessStrategy.Scan())
00378 {
00379 ILOG_ERROR("Failed while scanning: " << mSrcName);
00380 return false;
00381 }
00382
00383 const int frameCnt = accessStrategy.NrOfFrames();
00384 mLastFrame = frameCnt - 1;
00385 for (int f = 0; f < frameCnt; f++)
00386 if (!accessStrategy.FrameIsValid(f))
00387 MarkBadFrame(f);
00388 SetLeadingBadFrames(accessStrategy.LeadingBadFrames());
00389 return true;
00390 }
00391
00392 virtual bool
00393 ReadFrameData()
00394 {
00395 if (mTargetFrameNr == mCurrentFrameNr)
00396 return true;
00397
00398 if (!FrameValid(mTargetFrameNr) || mTargetFrameNr == mBufferedFrameNr)
00399 {
00400 mCurrentFrameNr = mTargetFrameNr;
00401 return true;
00402 }
00403
00404 if (mTargetFrameNr < mBufferedFrameNr)
00405 Reset();
00406
00407 const bool videoSeekable = mIndexExists;
00408 if (videoSeekable)
00409 {
00410 int sFrameNr = GetSeekableFrame(mTargetFrameNr);
00411
00412 if (mBufferedFrameNr < 0 || sFrameNr > mBufferedFrameNr + 1)
00413
00414 {
00415 ILOG_DEBUG("Targeting frame " << mTargetFrameNr <<
00416 " by first seeking to frame " << sFrameNr);
00417 if (!SeekFrame(sFrameNr))
00418 return false;
00419 mLastDecodedFrameNr = mBufferedFrameNr = mCurrentFrameNr =
00420 sFrameNr;
00421 }
00422 }
00423
00424 const bool lastDecodedFrameValid = ReadToTarget();
00425 mCurrentFrameNr = mLastDecodedFrameNr;
00426 if (lastDecodedFrameValid)
00427 {
00428 int result = mVideoAccessor->CurrentFrameToRgb();
00429 mBufferedFrameNr = mCurrentFrameNr;
00430
00431 if (mFormatName != "asf")
00432 if (!CheckMD5Hash(mBufferedFrameNr))
00433 {
00434 ILOG_ERROR("Frame hash failure going to frame " <<
00435 mTargetFrameNr);
00436 return false;
00437 }
00438 }
00439
00440 return true;
00441 }
00442
00443
00444 bool
00445 ReadToTarget()
00446 {
00447 bool lastDecodedFrameIsValid = true;
00448 while (mLastDecodedFrameNr < mTargetFrameNr)
00449 {
00450 const int nextFrameNr = mLastDecodedFrameNr + 1;
00451
00452 if (mVideoAccessor->FrameCanBeRead(nextFrameNr))
00453 {
00454 bool foundValidFrame;
00455 bool isKeyFrame;
00456 const int decodingResult = mVideoAccessor->
00457 DecodeNextFrame(&foundValidFrame, &isKeyFrame, 0);
00458 const bool gotFrame = (decodingResult == 0);
00459 if (!gotFrame)
00460 {
00461
00462 if (mLastFrame == LASTFRAME_UNKNOWN)
00463 {
00464 mLastFrame = mLastDecodedFrameNr;
00465 ILOG_DEBUG("Assuming we arrived at eof; " <<
00466 "frame count established is: " << mLastFrame + 1);
00467 }
00468 return false;
00469 }
00470
00471 lastDecodedFrameIsValid = FrameValid(nextFrameNr);
00472 if (lastDecodedFrameIsValid && !foundValidFrame)
00473 {
00474
00475 MarkBadFrame(nextFrameNr);
00476 lastDecodedFrameIsValid = false;
00477 }
00478 else if (!lastDecodedFrameIsValid && foundValidFrame)
00479 {
00480 ILOG_WARN("Did not expect to read a valid frame for " <<
00481 nextFrameNr);
00482 }
00483 }
00484 else
00485 {
00486 if (FrameValid(nextFrameNr))
00487 {
00488
00489 MarkBadFrame(nextFrameNr);
00490 }
00491 lastDecodedFrameIsValid = false;
00492 }
00493
00494 mLastDecodedFrameNr = nextFrameNr;
00495 }
00496 return lastDecodedFrameIsValid;
00497 }
00498
00499 bool
00500 CheckMD5Hash(int frameNr) const
00501 {
00502 if (!mIndexExists)
00503 return true;
00504
00505 if (!FrameIsStable(frameNr))
00506 return true;
00507
00508 const String refHash = FrameHash(frameNr);
00509 const String hash = CalcHash();
00510 mIsFrameAccurate = (hash == refHash);
00511 if (mIsFrameAccurate)
00512 {
00513
00514 return true;
00515 }
00516
00517 ILOG_ERROR("No hash match for frame " << frameNr << "; expected " <<
00518 refHash << " but found " << hash);
00519 return false;
00520 }
00521
00522 bool
00523 FrameIsStable(int frameNr) const
00524 {
00525 return FrameHash(frameNr) != mVideoAccessor->UNSTABLE_FRAME_HASH;
00526 }
00527
00528 #ifndef REPOSITORY_USED // Here comes the deprecated stuff
00529 String
00530 DetermineInfoName(CString srcName, IndexMode mode, Util::Database* db) const
00531 {
00532 String infoName = "";
00533 if (Link::DiskImage::DiskImageUsed())
00534 {
00535 String subPath = FileNamePath(srcName);
00536 if (subPath.substr(0, 10) == "diskimage:")
00537 {
00538 int posAfterDiskImageFileName = subPath.find("://", 10);
00539
00540
00541 const String uptoDiskImageToken =
00542 subPath.substr(0, posAfterDiskImageFileName);
00543 int diskImageFileNamePos =
00544 FileNameLastPathSepPos(uptoDiskImageToken) + 1;
00545 if (diskImageFileNamePos == String::npos)
00546 ILOG_ERROR("Disk image file was expected to be prefixed "
00547 << "with a path: " << uptoDiskImageToken);
00548
00549 String subSubPath =
00550 subPath.substr(posAfterDiskImageFileName + 1);
00551 subSubPath = StringReplaceAll(subSubPath,
00552 "0x", "0x30x", false);
00553 subSubPath = StringReplaceAll(subSubPath,
00554 "//", "/0x2F", false);
00555 subSubPath = subSubPath.substr(0, subSubPath.size() - 1);
00556 subPath = subPath.substr(diskImageFileNamePos,
00557 posAfterDiskImageFileName-diskImageFileNamePos)+subSubPath;
00558
00559 String dir = "FramePosIndex/" + subPath;
00560
00561 bool toWrite = (mode == WRITEIDX);
00562 bool silent = (mode != READIDX);
00563
00564 if (toWrite)
00565 db->MakeDir(dir);
00566
00567 String fname = FileNameTail(srcName) + ".info";
00568 infoName = db->GetFilePath(dir, fname, toWrite, silent);
00569 }
00570 else
00571 infoName = srcName + ".info";
00572 }
00573 return infoName;
00574 }
00575 #endif // REPOSITORY_USED
00576
00577 Lavc::VideoAccessStrategy* mVideoAccessor;
00578 Persistency::RgbDataSrcLocator mLoc;
00579
00580 int mBufferedFrameNr;
00581 int mLastDecodedFrameNr;
00582 bool mValid;
00583 uint32_t mFrameCountCalculated;
00584 IndexMode mMode;
00585
00586 int mLastFixNr;
00587 int mFixCnt;
00588
00589 ILOG_VAR_DECL;
00590
00591 };
00592
00593 ILOG_VAR_INIT(RgbDataSrcLavc, Impala.Core.Stream);
00594
00595 }}}
00596
00597 #endif