00001 #ifndef Impala_Core_Stream_Lavc_VideoAccessObject_h
00002 #define Impala_Core_Stream_Lavc_VideoAccessObject_h
00003
00004 #include "Core/Array/Arrays.h"
00005 #include "Core/Array/MD5Hash.h"
00006
00007
00008 #include "Link/Lavc/Lavc.h"
00009
00010 #include "Link/DiskImage/DiskImageFuncs.h"
00011
00012 #include "Core/Stream/LavcProtocolDataServer.h"
00013 #include "Core/Table/TableTem.h"
00014
00015
00016
00017
00018
00019 #ifdef USE_IFILE
00020 #include "Core/Stream/LavcProtocolLocalFile.h"
00021 #endif
00022
00023 namespace Impala
00024 {
00025 namespace Core
00026 {
00027 namespace Stream
00028 {
00029 namespace Lavc
00030 {
00031
00032
00033 typedef Table::TableTem< Column::ColumnTem<Int32>,
00034 Column::ColumnTem<Int32>,
00035 Column::ColumnTem<UInt64>,
00036 Column::ColumnTem<bool> > PacketTrace;
00037
00038 class VideoAccessObject
00039 {
00040
00041 public:
00042
00043 VideoAccessObject(CString srcAddress)
00044 {
00045 mIsValid = false;
00046
00047 mFormatCtx = 0;
00048 mVideoStream = 0;
00049 mVideoCodecCtx = 0;
00050 mPacket = 0;
00051 mFrame = 0;
00052 mFrameValid = false;
00053 mRgbFrame = 0;
00054 mRgbBuffer = 0;
00055 mSwsContext = 0;
00056
00057 mSrcAddress = srcAddress;
00058
00059 ILOG_INFO("libavcodec version : " <<
00060 (int)(LIBAVCODEC_VERSION_INT>>16) << "." <<
00061 (int)((LIBAVCODEC_VERSION_INT>>8)&0xFF) << "." <<
00062 (int)(LIBAVCODEC_VERSION_INT&0xFF));
00063
00064 ILOG_INFO("libavformat version : " <<
00065 (int)(LIBAVFORMAT_VERSION_INT>>16) << "." <<
00066 (int)((LIBAVFORMAT_VERSION_INT>>8)&0xFF) << "." <<
00067 (int)(LIBAVFORMAT_VERSION_INT&0xFF));
00068
00069
00070
00071
00072
00073
00074 ILOG_INFO("libswscale version : " <<
00075 (int)(LIBSWSCALE_VERSION_INT>>16) << "." <<
00076 (int)((LIBSWSCALE_VERSION_INT>>8)&0xFF) << "." <<
00077 (int)(LIBSWSCALE_VERSION_INT&0xFF));
00078
00079 ILOG_INFO("Accessing video source: " << srcAddress);
00080
00081 if (!InitFFMPEG() ||
00082 !InitFormatContext() ||
00083 !InitStreams() ||
00084 !InitCodecContext() ||
00085 !InitFrames())
00086 {
00087 return;
00088 }
00089
00090 mIsValid = true;
00091 }
00092
00093 ~VideoAccessObject()
00094 {
00095 if (mRgbBuffer != 0)
00096 delete [] mRgbBuffer;
00097
00098 if (mRgbFrame != 0)
00099 av_free(mRgbFrame);
00100 if (mFrame != 0)
00101 av_free(mFrame);
00102
00103 if (mPacket != 0)
00104 {
00105 FreePacket();
00106 delete mPacket;
00107 }
00108
00109 if (mSwsContext != 0)
00110 sws_freeContext(mSwsContext);
00111
00112 if (mVideoCodecCtx != 0)
00113 avcodec_close(mVideoCodecCtx);
00114
00115 if (mFormatCtx != 0)
00116 av_close_input_file(mFormatCtx);
00117 }
00118
00119 bool
00120 IsValid() const
00121 {
00122 return mIsValid;
00123 }
00124
00125 String
00126 VideoSrcAddress() const
00127 {
00128 return mSrcAddress;
00129 }
00130
00131 int
00132 BitRate() const
00133 {
00134 return mFormatCtx->bit_rate;
00135 }
00136
00137 String
00138 FormatNameShort() const
00139 {
00140 return mFormatCtx->iformat->name;
00141 }
00142
00143 String
00144 FormatNameLong() const
00145 {
00146 return mFormatCtx->iformat->long_name;
00147 }
00148
00149 int
00150 StreamCount() const
00151 {
00152 return mFormatCtx->nb_streams;
00153 }
00154
00155 UInt64
00156 FirstPacketPosition() const
00157 {
00158 const Int64& firstPosSigned = mFormatCtx->data_offset;
00159 if (firstPosSigned < 0)
00160 {
00161 ILOG_ERROR("Negative stream start offset unexpected");
00162 return 0;
00163 }
00164 return mFormatCtx->data_offset;
00165 }
00166
00167 bool
00168 NativeIndexValid() const
00169 {
00170 return NativeIndexEntries() != 0;
00171 }
00172
00173 const AVIndexEntry*
00174 NativeIndexEntries() const
00175 {
00176 AVIndexEntry *idx = mVideoStream->index_entries;
00177 if (idx && (mFormatCtx->flags & AVFMT_FLAG_IGNIDX))
00178 idx = 0;
00179 return idx;
00180 }
00181
00182 int
00183 NativeIndexSize() const
00184 {
00185 if (NativeIndexValid())
00186 return mVideoStream->nb_index_entries;
00187 return 0;
00188 }
00189
00190 int
00191 VideoCodecId() const
00192 {
00193 return mVideoCodecCtx->codec_id;
00194 }
00195
00196 String
00197 VideoCodecName() const
00198 {
00199 return mVideoCodecCtx->codec->name;
00200 }
00201
00202 String
00203 VideoCodecTag() const
00204 {
00205 char buf[5];
00206 const unsigned int tagCode = mVideoCodecCtx->codec_tag;
00207 #ifdef WIN32
00208 _snprintf(
00209 #else
00210 snprintf(
00211 #endif
00212 buf, sizeof(buf), "%c%c%c%c",
00213 (tagCode) & 0xff,
00214 (tagCode >> 8) & 0xff,
00215 (tagCode >> 16) & 0xff,
00216 (tagCode >> 24) & 0xff);
00217 return String(buf);
00218 }
00219
00220 int
00221 GopSize() const
00222 {
00223 return mVideoCodecCtx->gop_size;
00224 }
00225
00226 int
00227 FrameWidth() const
00228 {
00229 return mVideoCodecCtx->width;
00230 }
00231
00232 int
00233 FrameHeight() const
00234 {
00235 return mVideoCodecCtx->height;
00236 }
00237
00238 int
00239 AspectRatioNum() const
00240 {
00241 return mVideoCodecCtx->sample_aspect_ratio.num;
00242 }
00243
00244 int
00245 AspectRatioDen() const
00246 {
00247 return mVideoCodecCtx->sample_aspect_ratio.den;
00248 }
00249
00250 int
00251 VideoDuration() const
00252 {
00253
00254 return mVideoStream->duration;
00255 }
00256
00257 int
00258 VideoTimeBaseNumerator() const
00259 {
00260 const AVRational videoTimeBase =
00261 mVideoStream->time_base;
00262 return videoTimeBase.num;
00263 }
00264
00265 int
00266 VideoTimeBaseDenominator() const
00267 {
00268 const AVRational videoTimeBase =
00269 mVideoStream->time_base;
00270 return videoTimeBase.den;
00271 }
00272
00273 int
00274 FrameRateNumerator() const
00275 {
00276 return mVideoStream->r_frame_rate.num;
00277 }
00278
00279 int
00280 FrameRateDenominator() const
00281 {
00282 return mVideoStream->r_frame_rate.den;
00283 }
00284
00285
00286
00287
00288
00292 bool
00293 Reset() const
00294 {
00295 mFrameValid = false;
00296 FreePacket();
00297 FlushCodecContext();
00298
00299 if (!Seek(FirstPacketPosition(), AVSEEK_FLAG_BYTE))
00300 {
00301 ILOG_ERROR("Video source reset failed");
00302 return false;
00303 }
00304
00305 ILOG_DEBUG("Video source has been reset");
00306 return true;
00307 }
00308
00309 bool
00310 AtEof() const
00311 {
00312 #ifdef FFMPEG_52
00313 return (url_feof(mFormatCtx->pb) != 0);
00314 #else
00315 return (url_feof(&mFormatCtx->pb) != 0);
00316 #endif
00317 }
00318
00319 UInt8*
00320 RgbDataPtr() const
00321 {
00322 return mRgbBuffer;
00323 }
00324
00325 char
00326 FrameType() const
00327 {
00328 switch (mFrame->pict_type)
00329 {
00330 case FF_I_TYPE : return 'I';
00331 case FF_P_TYPE : return 'P';
00332 case FF_B_TYPE : return 'B';
00333 }
00334 return '?';
00335 }
00336
00343 bool
00344 Seek(const UInt64& target, int flags) const
00345 {
00346 if ((Int64) target < 0)
00347 {
00348 ILOG_ERROR("File position value " << target <<
00349 " not supported (should fit in Int64)");
00350 return false;
00351 }
00352
00353 avcodec_flush_buffers(mVideoCodecCtx);
00354
00355 const int result =
00356 av_seek_frame(mFormatCtx, mVideoStreamIndex, (Int64) target, flags);
00357
00358 if (result < 0)
00359 {
00360 ILOG_ERROR("av_seek_frame returned a negative value: " << result <<
00361 " (target=" << target << ", flags=" << flags << ")");
00362 return false;
00363 }
00364 return true;
00365 }
00366
00371 int
00372 ReadPacket() const
00373 {
00374 FreePacket();
00375
00376 return av_read_frame(mFormatCtx, mPacket);
00377 }
00378
00379 int
00380 CurrentPacketSize() const
00381 {
00382 return mPacket->size;
00383 }
00384
00385 int
00386 CurrentPacketFlags() const
00387 {
00388 return mPacket->flags;
00389 }
00390
00391 bool
00392 CurrentPacketIsVideo() const
00393 {
00394 return mPacket->stream_index == mVideoStreamIndex;
00395 }
00396
00397 UInt64
00398 CurrentFilePosition() const
00399 {
00400 const ByteIOContext* const b =
00401 #ifdef FFMPEG_52
00402 mFormatCtx->pb;
00403 #else
00404 &(mFormatCtx->pb);
00405 #endif
00406 Int64 pos = b->pos - (b->buf_end - b->buf_ptr);
00407 if (pos < 0)
00408 {
00409 ILOG_ERROR("Negative file position " << pos <<
00410 " unexpected (should fit in UInt64)");
00411 return 0;
00412 }
00413 return pos;
00414 }
00415
00419 int
00420 DecodeFrame(int* resultCode) const
00421 {
00422 mFrameValid = false;
00423
00424 if (!CurrentPacketIsVideo())
00425 {
00426 ILOG_ERROR("Attempt to decode video frame from non-video packet");
00427 return -1;
00428 }
00429
00430 const int len = avcodec_decode_video(mVideoCodecCtx,
00431 mFrame, resultCode, mPacket->data, mPacket->size);
00432
00433
00434
00435
00436
00437
00438
00439 if (len < 0)
00440 {
00441
00442
00443
00444
00445 ILOG_DEBUG("avcodec_decode_video returned " << len <<
00446 " (negative value) indicating an error occurred");
00447
00448
00449
00450
00451 }
00452 else if (len == 0)
00453 {
00454 if (*resultCode != 0)
00455 ILOG_DEBUG("Failed assumption on avcodec_decode_video: " <<
00456 "len=0 (no frame decompressed, but not an error) " <<
00457 "but resultCode=" << *resultCode <<" (non-zero)");
00458 }
00459
00460
00461
00462
00463
00464
00465
00466 mFrameValid = true;
00467 return len;
00468 }
00469
00470 bool
00471 FrameIsKey() const
00472 {
00473 return mFrame->key_frame == 1;
00474 }
00475
00476 bool
00477 CurrentFrameValid() const
00478 {
00479 return mFrameValid;
00480 }
00481
00487 String
00488 CurrentHash() const
00489 {
00490 if (!CurrentFrameValid())
00491 return "DUMMY";
00492
00493 Array::Array2dVec3UInt8* arr = new Array::Array2dVec3UInt8(
00494 mVideoCodecCtx->width, mVideoCodecCtx->height, 0, 0,
00495 mRgbBuffer, true, false);
00496 String hash = Array::MD5Hash(arr);
00497 delete arr;
00498
00499 return hash;
00500 }
00501
00502 String
00503 CurrentHashBeforeConversion() const
00504 {
00505 if (!CurrentFrameValid())
00506 return "DUMMY";
00507
00508 int nrOfPicturePlanes = 0;
00509 int maxLineSize = 1;
00510 for ( ; nrOfPicturePlanes < 4; nrOfPicturePlanes++)
00511 {
00512 int lineSize = mFrame->linesize[nrOfPicturePlanes];
00513 if (lineSize <= 0)
00514 break;
00515 maxLineSize = Max(lineSize, maxLineSize);
00516 }
00517
00518
00519 Array::Array2dScalarUInt8* arr = new Array::Array2dScalarUInt8(
00520 maxLineSize, nrOfPicturePlanes, 0, 0);
00521 size_t numBytes = sizeof(UInt8) * nrOfPicturePlanes * maxLineSize;
00522 memset((void*) arr->PB(), 0, numBytes);
00523 for (int p = 0; p < nrOfPicturePlanes; p++)
00524 {
00525 int lineSize = mFrame->linesize[p];
00526 for (int e = 0; e < lineSize; e++)
00527 {
00528 UInt8 value = mFrame->data[p][e];
00529 arr->SetValue(value, e, p);
00530 }
00531 }
00532 String hash = Array::MD5Hash(arr);
00533 delete arr;
00534 return hash;
00535 }
00536
00537
00538 int
00539 CurrentFrameToRgb() const
00540 {
00541 ClearRgbBuffer();
00542 int result = -1;
00543 if (CurrentFrameValid())
00544 {
00545 result = sws_scale(mSwsContext,
00546 mFrame->data, mFrame->linesize,
00547 0, mVideoCodecCtx->height,
00548 mRgbFrame->data, mRgbFrame->linesize);
00549 #ifdef FFMPEG_52
00550 if (result != mVideoCodecCtx->height)
00551 ILOG_WARN("Image height after scaling (" << result <<
00552 ") is expected to equal the original image's height (" <<
00553 mVideoCodecCtx->height << ").");
00554 #endif
00555 }
00556 return result;
00557 }
00558
00559 void
00560 FlushCodecContext() const
00561 {
00562 if (mVideoCodecCtx->codec->flush != NULL)
00563 mVideoCodecCtx->codec->flush(mVideoCodecCtx);
00564
00565 avcodec_flush_buffers(mVideoCodecCtx);
00566 }
00567
00568
00569 void
00570 DumpState() const
00571 {
00572
00573 AVFormatContext* const s = mFormatCtx;
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585 const ByteIOContext* const b =
00586 #ifdef FFMPEG_52
00587 s->pb;
00588 #else
00589 &(s->pb);
00590 #endif
00591 std::cout << "Current ByteIOContext: " <<
00592
00593
00594 "act.buffer.size=" << b->buf_end - b->buffer << ", " <<
00595 "pos.of.buffer=" << b->pos - (b->buf_end - b->buffer) << ", " <<
00596 "pos.in.buffer=" << b->buf_ptr - b->buffer << ", " <<
00597 "pos.in.file=" << b->pos - (b->write_flag ? 0 : (b->buf_end - b->buf_ptr)) << ", " <<
00598 "eof_reached=" << b->eof_reached << ", " <<
00599
00600
00601
00602
00603 std::endl;
00604 }
00605
00606
00607 private:
00608
00609 bool
00610 InitFFMPEG()
00611 {
00612
00613
00614
00615
00616
00617 av_log_set_level(AV_LOG_QUIET);
00618
00619
00620
00621 av_register_all();
00622 avcodec_register_all();
00623
00624 register_protocol(&LavcProtocolDataServer_protocol);
00625
00626 if (Link::DiskImage::DiskImageUsed())
00627 {
00628 ILOG_DEBUG("Registering disk image protocol..");
00629 Link::DiskImage::RegisterProtocol();
00630 }
00631
00632 #ifdef USE_IFILE
00633 ILOG_DEBUG("Registering Impala file protocol..");
00634 register_protocol(&LavcProtocolLocalFile_protocol);
00635 #endif
00636 return true;
00637 }
00638
00639 bool
00640 InitFormatContext()
00641 {
00642 int result = av_open_input_file(&mFormatCtx,
00643 mSrcAddress.c_str(), NULL, 0, NULL);
00644
00645 if (result != 0)
00646 {
00647 ILOG_ERROR("Unable to open: " << mSrcAddress <<
00648 " (" << result << ")");
00649 return false;
00650 }
00651
00652 #if LIBAVFORMAT_VERSION_INT >= ((51<<16)+(14<<8)+0) // from version 51.14.0 on
00653 if (mFormatCtx->iformat->flags & AVFMT_GENERIC_INDEX)
00654 {
00655
00656
00657 mFormatCtx->iformat->flags -= AVFMT_GENERIC_INDEX;
00658 ILOG_DEBUG("Flag cleared: mFormatCtx->iformat->flags -= " <<
00659 "AVFMT_GENERIC_INDEX");
00660
00661
00662 result = av_open_input_file(&mFormatCtx,
00663 mSrcAddress.c_str(), NULL, 0, NULL);
00664 }
00665 #endif
00666
00667
00668 result = av_find_stream_info(mFormatCtx);
00669 if (result < 0)
00670 {
00671 ILOG_ERROR("Could not find stream info in: " << mSrcAddress <<
00672 " (" << result << ")");
00673 return false;
00674 }
00675
00676
00677 dump_format(mFormatCtx, 0, mSrcAddress.c_str(), false);
00678
00679 mFormatCtx->flags |= AVFMT_FLAG_GENPTS;
00680
00681 return true;
00682 }
00683
00684 bool
00685 InitStreams()
00686 {
00687 mVideoStreamIndex = -1;
00688 mVideoStream = 0;
00689 int videoStreamCount = 0;
00690
00691
00692 for (int i = 0; i < StreamCount(); i++)
00693 {
00694 if (mFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
00695 {
00696 if (++videoStreamCount > 1)
00697 continue;
00698
00699
00700
00701 mVideoStreamIndex = i;
00702 mVideoStream = mFormatCtx->streams[i];
00703 }
00704 }
00705
00706 if (videoStreamCount == 0)
00707 {
00708 ILOG_ERROR("Did not find video stream in: " << mSrcAddress);
00709 return false;
00710 }
00711
00712 if (videoStreamCount > 1)
00713 ILOG_WARN("Found " << videoStreamCount << " video streams; using " <<
00714 "just one from: " << mSrcAddress);
00715
00716 return true;
00717 }
00718
00719 bool
00720 InitCodecContext()
00721 {
00722 mVideoCodecCtx = mVideoStream->codec;
00723
00724 AVCodec* codec = avcodec_find_decoder(mVideoCodecCtx->codec_id);
00725 if (codec == NULL)
00726 {
00727 ILOG_ERROR("Codec not found for: " << mSrcAddress);
00728 return false;
00729 }
00730
00731 int result = avcodec_open(mVideoCodecCtx, codec);
00732 if (result < 0)
00733 {
00734 ILOG_ERROR("Could not open codec " << codec->name << " (id: " <<
00735 codec->id << ") for: " << mSrcAddress << " (" << result << ")");
00736 return false;
00737 }
00738
00739 #ifdef FFMPEG_52
00740 if (sws_isSupportedInput(mVideoCodecCtx->pix_fmt) < 0)
00741 {
00742 ILOG_ERROR("sws conversion does not support pixel format (" <<
00743 mVideoCodecCtx->pix_fmt << ") of: " << mSrcAddress);
00744 return false;
00745 }
00746 #endif
00747
00748 mSwsContext = sws_getContext(
00749 mVideoCodecCtx->width, mVideoCodecCtx->height, mVideoCodecCtx->pix_fmt,
00750 mVideoCodecCtx->width, mVideoCodecCtx->height, PIX_FMT_RGB24,
00751 SWS_BICUBIC, NULL, NULL, NULL);
00752 if (mSwsContext == NULL)
00753 {
00754 ILOG_ERROR("Failed to initialize image conversion context for: " <<
00755 mSrcAddress);
00756 return false;
00757 }
00758
00759 if (codec != mVideoCodecCtx->codec)
00760 {
00761 ILOG_ERROR("Codec found is not same as codec stored (" <<
00762 MakeString(codec->id) << " vs. " <<
00763 MakeString(mVideoCodecCtx->codec->id) << ") for: " <<
00764 mSrcAddress);
00765 }
00766
00767 return true;
00768 }
00769
00770 bool
00771 InitFrames()
00772 {
00773 mPacket = new AVPacket;
00774 mPacket->data = 0;
00775
00776
00777 mFrame = avcodec_alloc_frame();
00778
00779
00780 mRgbFrame = avcodec_alloc_frame();
00781
00782 if ((mFrame == NULL) || (mRgbFrame == NULL))
00783 {
00784 ILOG_ERROR("Could not allocate frame memory for: " << mSrcAddress);
00785 return false;
00786 }
00787
00788
00789
00790 mRgbBufferSize = avpicture_fill( (AVPicture*) mRgbFrame,
00791 NULL, PIX_FMT_RGB24, mVideoCodecCtx->width, mVideoCodecCtx->height);
00792 mRgbBuffer = new UInt8[mRgbBufferSize];
00793
00794
00795
00796 int result = avpicture_fill( (AVPicture*) mRgbFrame,
00797 mRgbBuffer, PIX_FMT_RGB24, mVideoCodecCtx->width, mVideoCodecCtx->height);
00798
00799 return true;
00800 }
00801
00802 void
00803 FreePacket() const
00804 {
00805 if (mPacket->data != 0)
00806 {
00807 av_free_packet(mPacket);
00808
00809 }
00810 }
00811
00812 void
00813 ClearRgbBuffer() const
00814 {
00815 std::memset( (void*) mRgbBuffer, 0, mRgbBufferSize);
00816 }
00817
00818
00819 bool mIsValid;
00820 String mSrcAddress;
00821
00822 AVFormatContext* mFormatCtx;
00823 int mVideoStreamIndex;
00824 AVStream* mVideoStream;
00825 AVCodecContext* mVideoCodecCtx;
00826 AVPacket* mPacket;
00827 AVFrame* mFrame;
00828 mutable bool mFrameValid;
00829 AVFrame* mRgbFrame;
00830 int mRgbBufferSize;
00831 mutable UInt8* mRgbBuffer;
00832 struct SwsContext* mSwsContext;
00833
00834 ILOG_VAR_DECL;
00835
00836 };
00837
00838 ILOG_VAR_INIT(VideoAccessObject, Impala.Core.Stream.Lavc);
00839
00840
00841 }}}}
00842
00843 #endif