2012-09-24 47 views
6

我試圖使用ffmpeg從視頻文件中捕獲幀,但我甚至無法獲取視頻的持續時間。每次當我嘗試訪問pFormatCtx->duration我越來越0.我知道指針初始化,幷包含正確的持續時間,因爲如果我使用av_dump_format(pFormatCtx, 0, videoName, 0);,那麼我實際上得到持續時間數據以及有關視頻的其他信息。 這是我所得到的,當我使用av_dump_format(pFormatCtx, 0, videoName, 0);FFMPEG無法顯示視頻的持續時間

輸入#0,AVI,從 'futurama.avi':

時間:00:21:36.28,啓動:0.000000,比特率:1135 KB/s的

流#0.0:視頻:MPEG4(高級簡單),YUV420P,512x384

[PAR 1:1 DAR 4:3],25 TBR,25 TBN,25 TBC

流#0.1:音頻:ac3,48000Hz,立體聲,s16,192 kb/s

我不明白爲什麼av_dum_format可以顯示持續時間,我不能。我檢查了函數定義,顯示持續時間,函數也使用pFormatCtx-> duration。這不只是時間的其他成員變量也沒有,當我打電話給他們在main.cpp中

這裏顯示正確的數據是我的main.cpp:

extern "C" { 
    #include<libavcodec/avcodec.h> 
    #include<libavformat/avformat.h> 
    #include<libswscale/swscale.h> 
} 


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 << pFormatCtx->bit_rate << endl; // different value each time, not initialized properly. 
    cout << pFormatCtx->duration << endl; // 0 
    return 0; 
} 

我不知道是否有幫助,但,我在Ubuntu上使用QtCreator並靜態鏈接這些庫。

謝謝你的幫助。

+1

有一些東西'av_dump_format'不讀取'pFormatCtv->之前duration',使得該領域有效。換句話說,在持續時間變爲有效之前,必須執行額外的代碼。通過一些有效的代碼跟蹤,你應該找到缺失的部分。順便說一句,你仍然對這個問題的答案感興趣嗎? –

+0

我試圖在我的個人項目中使用ffmpeg的功能,但我最終使用了ffmpeg.exe的新進程。我想找出一個答案,ffmpeg是一個非常強大的工具,我相信我將在未來使用它,如果我知道如何使用庫而不是使用可執行文件在新的流程中。 – Malkavian

+0

我很可能不會很快嘗試你的方法,我現在很忙,我給你的投票,如果它有效,我會告訴你。再次感謝! – Malkavian

回答

1

如何從FFmpeg的

我以前的ffmpeg一陣亂了一圈,發現學習曲線是相當陡峭獲取時間信息(及以上)。所以,即使OP在幾個月前提出了這個問題,我會發布一些代碼,以防其他人在這裏尋找類似的東西。下面的Open()函數是完整的,但有很多斷言,缺少正確的錯誤處理方式。

右鍵關閉,一個直接的區別我看到的是,我用av_open_input_file,而不是avformat_open_input。我也沒有使用av_dump_format

計算持續時間可能會非常棘手,特別是對於H.264和MPEG-2;請參閱下面的計算方法durationSec

注意:本示例還使用JUCE C++ Utility Library

注2:此代碼是ffmpeg tutorial的修改版本。

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; i<pFormatCtx->nb_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, e.g., 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<double>(pFormatCtx->streams[videoStream]->duration) * static_cast<double>(ticksPerFrame)/static_cast<double>(avr.den); 
    double fH = durationSec/3600.; 
    int  H = static_cast<int>(fH); 
    double fM = (fH - H) * 60.; 
    int  M = static_cast<int>(fM); 
    double fS = (fM - M) * 60.; 
    int  S = static_cast<int>(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<double>(avr.den)/static_cast<double>(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() 
3

持續時間屬性在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; 

持續時間是毫秒,只是把地板或小區得到一個整數毫秒,無論你喜歡。

+1

請注意,ffmpeg頭文件中甚至還有一個內聯函數用於轉換:'rational.h'中的'av_q2d'。 – Sam

1

av_open_input_file()avformat_open_input()之間的區別可能是後者不讀取流信息 - 因此duration未初始化。致電avformat_find_stream_info()解決了我的問題。

我拿了從http://ffmpeg.org/doxygen/trunk/dump_8c_source.html#l00480計算/顯示的代碼片段(注意行號可以並且可能會在新版本中改變)。並添加了一些初始化代碼,'它適用於我'。希望能幫助到你。

#include <libavutil/avutil.h> 
#include <libavformat/avformat.h> 

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