Home || Architecture || Video Search || Visual Search || Scripts || Applications || Important Messages || OGL || Src

VideoAccessObject.h

Go to the documentation of this file.
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 // include our one and only entry point into libavcodec:
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 //#ifdef USE_IFILE
00016 //#include "Core/Stream/LavcProtocolLocalFile.h"
00017 //#endif
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 // size | flags | file position upon read | is video 
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         // number of (time) fractions
00254         return mVideoStream->duration; 
00255     }
00256 
00257     int
00258     VideoTimeBaseNumerator() const
00259     {
00260         const AVRational videoTimeBase = 
00261             mVideoStream->time_base; // time fraction duration (seconds)
00262         return videoTimeBase.num;
00263     }
00264 
00265     int
00266     VideoTimeBaseDenominator() const
00267     {
00268         const AVRational videoTimeBase = 
00269             mVideoStream->time_base; // time fraction duration (seconds)
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         // note that this method's name is correct since in fact it reads a packet
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         // from avcodec.h: 
00446         //   resultCode : 0 if no frame could be decompressed, 
00447         //                otherwise, it is nonzero.
00448         //   len        : On error a negative value is returned, otherwise the 
00449         //                number of bytes used or zero if no frame could be 
00450         //                decompressed.
00451 
00452         if (len < 0)
00453         {
00454             // indicates an error condition according to avcodec.h
00455 
00456             //if (len < -1)
00457             //{
00458                 ILOG_DEBUG("avcodec_decode_video returned " << len <<
00459                     " (negative value) indicating an error occurred"); 
00460             //}
00461             //else
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     // Convert the image from native format to RGB
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     // For dev purposes only;
00507     void
00508     DumpState() const
00509     {
00510         //std::cout << "------------------------------------" << std::endl;
00511         AVFormatContext* const s = mFormatCtx;
00512         //const UInt8 invalidPtr = 0;
00513         //const UInt8* curPtr = &invalidPtr;
00514         //if (s->cur_ptr)
00515         //    curPtr = (s->cur_ptr);
00516         //std::cout << "Current AVFormatContext: " <<
00517         //    "data_offset=" << s->data_offset << ", " <<
00518         //    "cur_ptr=" << curPtr << ", " <<
00519         //    "cur_len=" << s->cur_len << ", " <<
00520         //    "packet_size=" << s->packet_size << 
00521         //    std::endl;
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             //"total.bytes=" << url_fsize(&(s->pb)) << ", " <<
00531             //"buffer_size=" << b->buffer_size << ", " <<
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             //"checksum=" << b->checksum << ", " <<
00538             //"must_flush=" << b->must_flush <<
00539             //"write_flag=" << b->write_flag << ", " <<
00540             //"is_streamed=" << b->is_streamed << 
00541             std::endl;
00542     }
00543 
00544 
00545 private:
00546     
00547     bool 
00548     InitFFMPEG()
00549     {
00550 
00551         // Set libavcodec's log output level
00552         //av_log_set_level(AV_LOG_DEBUG);
00553         //av_log_set_level(AV_LOG_ERROR);
00554         //av_log_set_level(AV_LOG_FATAL);
00555         av_log_set_level(AV_LOG_QUIET);
00556 
00557 
00558         // Register all formats and codecs 
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             // if flag not initially cleared, may result in invalid data;
00594             // mFormatCtx is not available until input file is opened
00595             mFormatCtx->iformat->flags -= AVFMT_GENERIC_INDEX;
00596             ILOG_DEBUG("Flag cleared: mFormatCtx->iformat->flags -= " <<
00597                 "AVFMT_GENERIC_INDEX");
00598 
00599             // re-open the file
00600             result = av_open_input_file(&mFormatCtx, 
00601                                  mSrcAddress.c_str(), NULL, 0, NULL);
00602         }
00603 #endif
00604 
00605         // Retrieve stream information
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         // Dump information about file onto standard error
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         // find the first video stream 
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                     // Note that this obfuscates the fact that more video data 
00637                     // is present which is inappropriate in forensic settings.
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         // Allocate frame for reading
00707         mFrame = avcodec_alloc_frame();
00708 
00709         // Allocate frame for RGB conversion
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         // allocate buffer to hold frame data converted to RGB
00719         const int numBytes = avpicture_get_size(
00720             PIX_FMT_RGB24, mVideoCodecCtx->width, mVideoCodecCtx->height);
00721         mRgbFrameData = new UInt8[numBytes];
00722 
00723         // Assign appropriate parts of buffer to image planes in mRgbFrame
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             //mPacket->data = 0;
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 }; //class
00757 
00758 ILOG_VAR_INIT(VideoAccessObject, Impala.Core.Stream.Lavc);
00759 
00760 
00761 }}}} // namespace Impala::Core::Stream::Lavc
00762 
00763 #endif

Generated on Fri Mar 19 09:31:17 2010 for ImpalaSrc by  doxygen 1.5.1