FFMPEG无法显示video的持续时间

我正在尝试使用ffmpeg从video文件中捕获帧,但我甚至无法获得video的持续时间。 每次当我尝试使用pFormatCtx->duration来访问它时我得到0.我知道指针初始化并包含正确的持续时间,因为如果我使用av_dump_format(pFormatCtx, 0, videoName, 0); 那么我实际上得到了持续时间数据以及有关video的其他信息。 这是我使用av_dump_format(pFormatCtx, 0, videoName, 0);

输入#0,avi,来自’futurama.avi’:

持续时间:00:21:36.28,开始:0.000000,比特率:1135 kb / s

流#0.0:video:mpeg4(高级简单配置文件),yuv420p,512×384

[PAR 1:1 DAR 4:3],25 tbr,25 tbn,25 tbc

流#0.1:音频:ac3,48000Hz,立体声,s16,192kb / s

我不明白为什么av_dum_format可以显示持续时间而我不能。 我检查了函数定义,显示持续时间,该函数也使用pFormatCtx-> duration。 当我在main.cpp中调用它们时,其他成员变量也不会显示正确的数据

这是我的main.cpp:

 extern "C" { #include #include #include } int main(int argc, char *argv[]) { AVFormatContext *pFormatCtx = NULL; const char videoName[] = "futurama.avi"; // Register all formats and codecs. av_register_all(); cout << "Opening the video file"; // Open video file int ret = avformat_open_input(&pFormatCtx, videoName, NULL, NULL) != 0; if (ret != 0) { cout << "Couldn't open the video file." << ret ; return -1; } if(avformat_find_stream_info(pFormatCtx, 0) < 0) { cout << "problem with stream info"; return -1; } av_dump_format(pFormatCtx, 0, videoName, 0); cout <bit_rate << endl; // different value each time, not initialized properly. cout <duration << endl; // 0 return 0; } 

我不知道它是否有帮助但是,我在Ubuntu上使用QtCreator并静态链接这些库。

谢谢您的帮助。

duration属性以time_base单位,而不是毫秒或秒。 转换为毫秒非常简单,

 double time_base = (double)video_stream->time_base.num / (double)video_stream->time_base.den; double duration = (double)video_stream->duration * time_base * 1000.0; 

持续时间现在以毫秒为单位,只需占用地板或ceil就可以得到整数毫秒,无论你喜欢什么。

如何从ffmpeg获取持续时间信息(以及更多)

我刚才和ffmpeg搞砸了,发现学习曲线非常陡峭。 因此,即使OP在几个月前提出这个问题,我也会发布一些代码,以防其他人在SO上寻找类似的东西。 下面的Open()函数已经完成,但有很多断言,缺乏正确的error handling方式。

马上,我看到的一个直接差异是我使用了av_open_input_file而不是avformat_open_input 。 我也没有使用av_dump_format

计算持续时间可能很棘手,特别是对于H.264和MPEG-2; 看看下面如何计算durationSec

注意:此示例还使用JUCE C ++ Utility Library 。

注意2:此代码是ffmpeg教程的修改版本。

 void VideoCanvas::Open(const char* videoFileName) { Logger::writeToLog(String(L"Opening video file ") + videoFileName); Close(); AVCodec *pCodec; // register all formats and codecs av_register_all(); // open video file int ret = av_open_input_file(&pFormatCtx, videoFileName, NULL, 0, NULL); if (ret != 0) { Logger::writeToLog("Unable to open video file: " + String(videoFileName)); Close(); return; } // Retrieve stream information ret = av_find_stream_info(pFormatCtx); jassert(ret >= 0); // Find the first video stream videoStream = -1; audioStream = -1; for(int i=0; inb_streams; i++) { if (pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && videoStream < 0) { videoStream = i; } if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audioStream < 0) { audioStream = i; } } // end for i jassert(videoStream != -1); jassert(audioStream != -1); // Get a pointer to the codec context for the video stream pCodecCtx=pFormatCtx->streams[videoStream]->codec; jassert(pCodecCtx != nullptr); /** * This is the fundamental unit of time (in seconds) in terms * of which frame timestamps are represented. For fixed-fps content, * timebase should be 1/framerate and timestamp increments should be * identically 1. * - encoding: MUST be set by user. * - decoding: Set by libavcodec. */ AVRational avr = pCodecCtx->time_base; Logger::writeToLog("time_base = " + String(avr.num) + "/" + String(avr.den)); /** * For some codecs, the time base is closer to the field rate than the frame rate. * Most notably, H.264 and MPEG-2 specify time_base as half of frame duration * if no telecine is used ... * * Set to time_base ticks per frame. Default 1, eg, H.264/MPEG-2 set it to 2. */ ticksPerFrame = pCodecCtx->ticks_per_frame; Logger::writeToLog("ticks_per_frame = " + String(pCodecCtx->ticks_per_frame)); durationSec = static_cast(pFormatCtx->streams[videoStream]->duration) * static_cast(ticksPerFrame) / static_cast(avr.den); double fH = durationSec / 3600.; int H = static_cast(fH); double fM = (fH - H) * 60.; int M = static_cast(fM); double fS = (fM - M) * 60.; int S = static_cast(fS); Logger::writeToLog("Video stream duration = " + String(H) + "H " + String(M) + "M " + String(fS, 3) + "S"); // calculate frame rate based on time_base and ticks_per_frame frameRate = static_cast(avr.den) / static_cast(avr.num * pCodecCtx->ticks_per_frame); Logger::writeToLog("Frame rate = " + String(frameRate) ); // audio codec context if (audioStream != -1) { aCodecCtx = pFormatCtx->streams[audioStream]->codec; Logger::writeToLog("Audio sample rate = " + String(aCodecCtx->sample_rate)); Logger::writeToLog("Audio channels = " + String(aCodecCtx->channels)); } jassert(aCodecCtx != nullptr); // format: // The "S" in "S16SYS" stands for "signed", the 16 says that each sample is 16 bits long, // and "SYS" means that the endian-order will depend on the system you are on. This is the // format that avcodec_decode_audio2 will give us the audio in. // open the audio codec if (audioStream != -1) { aCodec = avcodec_find_decoder(aCodecCtx->codec_id); if (!aCodec) { Logger::writeToLog(L"Unsupported codec ID = " + String(aCodecCtx->codec_id) ); Close(); return; // TODO: should we just play video if audio codec doesn't work? } avcodec_open(aCodecCtx, aCodec); } // Find the decoder for the video stream pCodec=avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec == nullptr) { jassert(false); // fprintf(stderr, "Unsupported codec!\n"); //return -1; // Codec not found } // Open video codec ret = avcodec_open(pCodecCtx, pCodec); jassert(ret >= 0); // Allocate video frame pFrame=avcodec_alloc_frame(); jassert(pFrame != nullptr); // Allocate an AVFrame structure pFrameRGB=avcodec_alloc_frame(); jassert(pFrameRGB != nullptr); int numBytes = avpicture_get_size(PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height); jassert(numBytes != 0); buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t)); jassert(buffer != nullptr); // note: the pixel format here is RGB, but sws_getContext() needs to be PIX_FMT_BGR24 to match (BGR) // this might have to do w/ endian-ness....make sure this is platform independent if (m_image != nullptr) delete m_image; m_image = new Image(Image::ARGB, pCodecCtx->width, pCodecCtx->height, true); int dstW = pCodecCtx->width; // don't rescale int dstH = pCodecCtx->height; Logger::writeToLog(L"Video width = " + String(dstW)); Logger::writeToLog(L"Video height = " + String(dstH)); // this should only have to be done once img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, dstW, dstH, PIX_FMT_RGB32, SWS_FAST_BILINEAR, NULL, NULL, NULL); jassert(img_convert_ctx != nullptr); setSize(pCodecCtx->width, pCodecCtx->height); } // Open() 

av_open_input_file()avformat_open_input()之间的差异可能是后者不读取流信息 – 因此duration未初始化。 调用avformat_find_stream_info()为我解决了这个问题。

我从http://ffmpeg.org/doxygen/trunk/dump_8c_source.html#l00480获取了计算/显示的代码片段(请注意,行号可能并且可能会在较新版本中更改)。 并添加了一些初始化代码,“它适合我”。 希望能帮助到你。

 #include  #include  int main() { const char const* file = "sample.mpg"; AVFormatContext* formatContext = NULL; av_register_all(); // Open video file avformat_open_input(&formatContext, file, NULL, NULL); avformat_find_stream_info(formatContext, NULL); // Lower log level since av_log() prints at AV_LOG_ERROR by default av_log_set_level(AV_LOG_INFO); av_log(NULL, AV_LOG_INFO, " Duration: "); if (formatContext->duration != AV_NOPTS_VALUE) { int hours, mins, secs, us; int64_t duration = formatContext->duration + 5000; secs = duration / AV_TIME_BASE; us = duration % AV_TIME_BASE; mins = secs / 60; secs %= 60; hours = mins / 60; mins %= 60; av_log(NULL, AV_LOG_INFO, "%02d:%02d:%02d.%02d\n", hours, mins, secs, (100 * us) / AV_TIME_BASE); } return 0; } 

编译,

 gcc -o duration -lavutil -lavformat duration.c