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
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 mRgbFrame = 0;
00053 mRgbFrameData = 0;
00054 mSwsContext = 0;
00055
00056 mSrcAddress = srcAddress;
00057
00058 ILOG_INFO("libavcodec version : " <<
00059 (int)(LIBAVCODEC_VERSION_INT>>16) << "." <<
00060 (int)((LIBAVCODEC_VERSION_INT>>8)&0xFF) << "." <<
00061 (int)(LIBAVCODEC_VERSION_INT&0xFF));
00062
00063 ILOG_INFO("libavformat version : " <<
00064 (int)(LIBAVFORMAT_VERSION_INT>>16) << "." <<
00065 (int)((LIBAVFORMAT_VERSION_INT>>8)&0xFF) << "." <<
00066 (int)(LIBAVFORMAT_VERSION_INT&0xFF));
00067
00068 ILOG_INFO("libavutil version : " <<
00069 (int)(LIBAVUTIL_VERSION_INT>>16) << "." <<
00070 (int)((LIBAVUTIL_VERSION_INT>>8)&0xFF) << "." <<
00071 (int)(LIBAVUTIL_VERSION_INT&0xFF));
00072
00073 ILOG_INFO("libswscale version : " <<
00074 (int)(LIBSWSCALE_VERSION_INT>>16) << "." <<
00075 (int)((LIBSWSCALE_VERSION_INT>>8)&0xFF) << "." <<
00076 (int)(LIBSWSCALE_VERSION_INT&0xFF));
00077
00078 ILOG_INFO("Accessing video source: " << srcAddress);
00079
00080 if (!InitFFMPEG() ||
00081 !InitFormatContext() ||
00082 !InitStreams() ||
00083 !InitCodecContext() ||
00084 !InitFrames())
00085 {
00086 return;
00087 }
00088
00089 mIsValid = true;
00090 }
00091
00092 ~VideoAccessObject()
00093 {
00094 if (mRgbFrameData != 0)
00095 delete [] mRgbFrameData;
00096
00097 if (mRgbFrame != 0)
00098 av_free(mRgbFrame);
00099 if (mFrame != 0)
00100 av_free(mFrame);
00101
00102 if (mPacket != 0)
00103 {
00104 FreePacket();
00105 delete mPacket;
00106 }
00107
00108 if (mSwsContext != 0)
00109 sws_freeContext(mSwsContext);
00110
00111 if (mVideoCodecCtx != 0)
00112 avcodec_close(mVideoCodecCtx);
00113
00114 if (mFormatCtx != 0)
00115 av_close_input_file(mFormatCtx);
00116 }
00117
00118 bool
00119 IsValid() const
00120 {
00121 return mIsValid;
00122 }
00123
00124 String
00125 VideoSrcAddress() const
00126 {
00127 return mSrcAddress;
00128 }
00129
00130 int
00131 BitRate() const
00132 {
00133 return mFormatCtx->bit_rate;
00134 }
00135
00136 String
00137 FormatNameShort() const
00138 {
00139 return mFormatCtx->iformat->name;
00140 }
00141
00142 String
00143 FormatNameLong() const
00144 {
00145 return mFormatCtx->iformat->long_name;
00146 }
00147
00148 int
00149 StreamCount() const
00150 {
00151 return mFormatCtx->nb_streams;
00152 }
00153
00154 UInt64
00155 FirstPacketPosition() const
00156 {
00157 const Int64& firstPosSigned = mFormatCtx->data_offset;
00158 if (firstPosSigned < 0)
00159 {
00160 ILOG_ERROR("Negative stream start offset unexpected");
00161 return 0;
00162 }
00163 return mFormatCtx->data_offset;
00164 }
00165
00166 bool
00167 NativeIndexValid() const
00168 {
00169 AVIndexEntry *idx = mVideoStream->index_entries;
00170 if (idx && (mFormatCtx->flags & AVFMT_FLAG_IGNIDX))
00171 idx = 0;
00172 return idx != 0;
00173 }
00174
00175 int
00176 NativeIndexSize() const
00177 {
00178 return mVideoStream->nb_index_entries;
00179 }
00180
00181 const AVIndexEntry*
00182 NativeIndexEntries() const
00183 {
00184 AVIndexEntry *idx = mVideoStream->index_entries;
00185 if (idx && (mFormatCtx->flags & AVFMT_FLAG_IGNIDX))
00186 idx = 0;
00187 return idx;
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 FreePacket();
00296 FlushCodecContext();
00297
00298 if (!Seek(FirstPacketPosition(), AVSEEK_FLAG_BYTE))
00299 {
00300 ILOG_ERROR("Video source reset failed");
00301 return false;
00302 }
00303
00304 ILOG_DEBUG("Video source has been reset");
00305 return true;
00306 }
00307
00308 bool
00309 AtEof() const
00310 {
00311 #ifdef FFMPEG_52
00312 return (url_feof(mFormatCtx->pb) != 0);
00313 #else
00314 return (url_feof(&mFormatCtx->pb) != 0);
00315 #endif
00316 }
00317
00318 UInt8*
00319 RgbDataPtr() const
00320 {
00321 return mRgbFrameData;
00322 }
00323
00324 char
00325 FrameType() const
00326 {
00327 switch (mFrame->pict_type)
00328 {
00329 case FF_I_TYPE : return 'I';
00330 case FF_P_TYPE : return 'P';
00331 case FF_B_TYPE : return 'B';
00332 }
00333 return '?';
00334 }
00335
00342 bool
00343 Seek(const UInt64& target, int flags) const
00344 {
00345 if ((Int64) target < 0)
00346 {
00347 ILOG_ERROR("File position value " << target <<
00348 " not supported (should fit in Int64)");
00349 return false;
00350 }
00351
00352 avcodec_flush_buffers(mVideoCodecCtx);
00353
00354 const int result =
00355 av_seek_frame(mFormatCtx, mVideoStreamIndex, (Int64) target, flags);
00356
00357 if (result < 0)
00358 {
00359 ILOG_ERROR("av_seek_frame returned a negative value: " << result <<
00360 " (target=" << target << ", flags=" << flags << ")");
00361 return false;
00362 }
00363 return true;
00364 }
00365
00370 int
00371 ReadPacket() const
00372 {
00373 FreePacket();
00374
00375 return av_read_frame(mFormatCtx, mPacket);
00376 }
00377
00378 int
00379 CurrentPacketSize() const
00380 {
00381 return mPacket->size;
00382 }
00383
00384 int
00385 CurrentPacketFlags() const
00386 {
00387 return mPacket->flags;
00388 }
00389
00390 bool
00391 CurrentPacketIsVideo() const
00392 {
00393 return mPacket->stream_index == mVideoStreamIndex;
00394 }
00395
00396 UInt64
00397 CurrentFilePosition() const
00398 {
00399 const ByteIOContext* const b =
00400 #ifdef FFMPEG_52
00401 mFormatCtx->pb;
00402 #else
00403 &(mFormatCtx->pb);
00404 #endif
00405 Int64 pos = b->pos - (b->buf_end - b->buf_ptr);
00406 if (pos < 0)
00407 {
00408 ILOG_ERROR("Negative file position " << pos <<
00409 " unexpected (should fit in UInt64)");
00410 return 0;
00411 }
00412 return pos;
00413 }
00414
00420 String
00421 CurrentHash() const
00422 {
00423 Array::Array2dVec3UInt8* arr = new Array::Array2dVec3UInt8(
00424 mVideoCodecCtx->width, mVideoCodecCtx->height, 0, 0,
00425 RgbDataPtr(), true, false);
00426 String hash = Array::MD5Hash(arr);
00427 delete arr;
00428 return hash;
00429 }
00430
00434 int
00435 DecodeFrame(int* resultCode) const
00436 {
00437 if (!CurrentPacketIsVideo())
00438 {
00439 ILOG_ERROR("Attempt to decode video frame from non-video packet");
00440 return -1;
00441 }
00442
00443 const int len = avcodec_decode_video(mVideoCodecCtx,
00444 mFrame, resultCode, mPacket->data, mPacket->size);
00445
00446
00447
00448
00449
00450
00451
00452 if (len < 0)
00453 {
00454
00455
00456
00457
00458 ILOG_DEBUG("avcodec_decode_video returned " << len <<
00459 " (negative value) indicating an error occurred");
00460
00461
00462
00463
00464 }
00465 else if (len == 0)
00466 {
00467 if (*resultCode != 0)
00468 ILOG_DEBUG("Failed assumption on avcodec_decode_video: " <<
00469 "len=0 (no frame decompressed, but not an error) " <<
00470 "but resultCode=" << *resultCode <<" (non-zero)");
00471 }
00472 else if (*resultCode == 0)
00473 {
00474 ILOG_DEBUG("Failed assumption on avcodec_decode_video: " <<
00475 "len>0 (bytes used for decompressed frame, no error occurred) " <<
00476 "but resultCode=0 (no frame could be decompressed)");
00477 }
00478
00479 return len;
00480 }
00481
00482 bool
00483 FrameIsKey() const
00484 {
00485 return mFrame->key_frame == 1;
00486 }
00487
00488
00489 int
00490 CurrentFrameToRgb() const
00491 {
00492 const int result = sws_scale(mSwsContext,
00493 mFrame->data, mFrame->linesize,
00494 0, mVideoCodecCtx->height,
00495 mRgbFrame->data, mRgbFrame->linesize);
00496 return result;
00497 }
00498
00499 void
00500 FlushCodecContext() const
00501 {
00502 if (mVideoCodecCtx->codec->flush != NULL)
00503 mVideoCodecCtx->codec->flush(mVideoCodecCtx);
00504 }
00505
00506
00507 void
00508 DumpState() const
00509 {
00510
00511 AVFormatContext* const s = mFormatCtx;
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523 const ByteIOContext* const b =
00524 #ifdef FFMPEG_52
00525 s->pb;
00526 #else
00527 &(s->pb);
00528 #endif
00529 std::cout << "Current ByteIOContext: " <<
00530
00531
00532 "act.buffer.size=" << b->buf_end - b->buffer << ", " <<
00533 "pos.of.buffer=" << b->pos - (b->buf_end - b->buffer) << ", " <<
00534 "pos.in.buffer=" << b->buf_ptr - b->buffer << ", " <<
00535 "pos.in.file=" << b->pos - (b->write_flag ? 0 : (b->buf_end - b->buf_ptr)) << ", " <<
00536 "eof_reached=" << b->eof_reached << ", " <<
00537
00538
00539
00540
00541 std::endl;
00542 }
00543
00544
00545 private:
00546
00547 bool
00548 InitFFMPEG()
00549 {
00550
00551
00552
00553
00554
00555 av_log_set_level(AV_LOG_QUIET);
00556
00557
00558
00559 av_register_all();
00560 avcodec_register_all();
00561
00562 register_protocol(&LavcProtocolDataServer_protocol);
00563
00564 if (Link::DiskImage::DiskImageUsed())
00565 {
00566 ILOG_DEBUG("Registering disk image protocol..");
00567 Link::DiskImage::RegisterProtocol();
00568 }
00569
00570 #ifdef USE_IFILE
00571 ILOG_DEBUG("Registering Impala file protocol..");
00572 register_protocol(&LavcProtocolLocalFile_protocol);
00573 #endif
00574 return true;
00575 }
00576
00577 bool
00578 InitFormatContext()
00579 {
00580 int result = av_open_input_file(&mFormatCtx,
00581 mSrcAddress.c_str(), NULL, 0, NULL);
00582
00583 if (result != 0)
00584 {
00585 ILOG_ERROR("Unable to open: " << mSrcAddress <<
00586 " (" << result << ")");
00587 return false;
00588 }
00589
00590 #if LIBAVFORMAT_VERSION_INT >= ((51<<16)+(14<<8)+0) // from version 51.14.0 on
00591 if (mFormatCtx->iformat->flags & AVFMT_GENERIC_INDEX)
00592 {
00593
00594
00595 mFormatCtx->iformat->flags -= AVFMT_GENERIC_INDEX;
00596 ILOG_DEBUG("Flag cleared: mFormatCtx->iformat->flags -= " <<
00597 "AVFMT_GENERIC_INDEX");
00598
00599
00600 result = av_open_input_file(&mFormatCtx,
00601 mSrcAddress.c_str(), NULL, 0, NULL);
00602 }
00603 #endif
00604
00605
00606 result = av_find_stream_info(mFormatCtx);
00607 if (result < 0)
00608 {
00609 ILOG_ERROR("Could not find stream info in: " << mSrcAddress <<
00610 " (" << result << ")");
00611 return false;
00612 }
00613
00614
00615 dump_format(mFormatCtx, 0, mSrcAddress.c_str(), false);
00616
00617 mFormatCtx->flags |= AVFMT_FLAG_GENPTS;
00618
00619 return true;
00620 }
00621
00622 bool
00623 InitStreams()
00624 {
00625 mVideoStreamIndex = -1;
00626 mVideoStream = 0;
00627 int videoStreamCount = 0;
00628
00629
00630 for (int i = 0; i < StreamCount(); i++)
00631 {
00632 if (mFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
00633 {
00634 if (++videoStreamCount > 1)
00635 continue;
00636
00637
00638
00639 mVideoStreamIndex = i;
00640 mVideoStream = mFormatCtx->streams[i];
00641 }
00642 }
00643
00644 if (videoStreamCount == 0)
00645 {
00646 ILOG_ERROR("Did not find video stream in: " << mSrcAddress);
00647 return false;
00648 }
00649
00650 if (videoStreamCount > 1)
00651 ILOG_WARN("Found " << videoStreamCount << " video streams; using " <<
00652 "just one from: " << mSrcAddress);
00653
00654 return true;
00655 }
00656
00657 bool
00658 InitCodecContext()
00659 {
00660 mVideoCodecCtx = mVideoStream->codec;
00661
00662 AVCodec* codec = avcodec_find_decoder(mVideoCodecCtx->codec_id);
00663 if (codec == NULL)
00664 {
00665 ILOG_ERROR("Codec not found for: " << mSrcAddress);
00666 return false;
00667 }
00668
00669 int result = avcodec_open(mVideoCodecCtx, codec);
00670 if (result < 0)
00671 {
00672 ILOG_ERROR("Could not open codec " << codec->name << " (id: " <<
00673 codec->id << ") for: " << mSrcAddress << " (" << result << ")");
00674 return false;
00675 }
00676
00677 mSwsContext = sws_getContext(
00678 mVideoCodecCtx->width, mVideoCodecCtx->height, mVideoCodecCtx->pix_fmt,
00679 mVideoCodecCtx->width, mVideoCodecCtx->height, PIX_FMT_RGB24,
00680 SWS_BICUBIC, NULL, NULL, NULL);
00681
00682 if (mSwsContext == NULL)
00683 {
00684 ILOG_ERROR("Failed to initialize image conversion context for: " <<
00685 mSrcAddress);
00686 return false;
00687 }
00688
00689 if (codec != mVideoCodecCtx->codec)
00690 {
00691 ILOG_ERROR("Codec found is not same as codec stored (" <<
00692 MakeString(codec->id) << " vs. " <<
00693 MakeString(mVideoCodecCtx->codec->id) << ") for: " <<
00694 mSrcAddress);
00695 }
00696
00697 return true;
00698 }
00699
00700 bool
00701 InitFrames()
00702 {
00703 mPacket = new AVPacket;
00704 mPacket->data = 0;
00705
00706
00707 mFrame = avcodec_alloc_frame();
00708
00709
00710 mRgbFrame = avcodec_alloc_frame();
00711
00712 if ((mRgbFrame == NULL) || (mFrame == NULL))
00713 {
00714 ILOG_ERROR("Could not allocate frame memory for: " << mSrcAddress);
00715 return false;
00716 }
00717
00718
00719 const int numBytes = avpicture_get_size(
00720 PIX_FMT_RGB24, mVideoCodecCtx->width, mVideoCodecCtx->height);
00721 mRgbFrameData = new UInt8[numBytes];
00722
00723
00724 int result = avpicture_fill( (AVPicture*) mRgbFrame,
00725 mRgbFrameData, PIX_FMT_RGB24, mVideoCodecCtx->width, mVideoCodecCtx->height);
00726
00727 return true;
00728 }
00729
00730 void
00731 FreePacket() const
00732 {
00733 if (mPacket->data != 0)
00734 {
00735 av_free_packet(mPacket);
00736
00737 }
00738 }
00739
00740
00741 bool mIsValid;
00742 String mSrcAddress;
00743
00744 AVFormatContext* mFormatCtx;
00745 int mVideoStreamIndex;
00746 AVStream* mVideoStream;
00747 AVCodecContext* mVideoCodecCtx;
00748 AVPacket* mPacket;
00749 AVFrame* mFrame;
00750 AVFrame* mRgbFrame;
00751 UInt8* mRgbFrameData;
00752 struct SwsContext* mSwsContext;
00753
00754 ILOG_VAR_DECL;
00755
00756 };
00757
00758 ILOG_VAR_INIT(VideoAccessObject, Impala.Core.Stream.Lavc);
00759
00760
00761 }}}}
00762
00763 #endif