2012-09-24 43 views
6

Tôi đang cố gắng sử dụng ffmpeg để chụp khung hình từ một tệp video, nhưng tôi thậm chí không thể nhận được thời lượng của video. Mỗi khi tôi cố gắng truy cập nó với pFormatCtx->duration Tôi nhận được 0. Tôi biết con trỏ được khởi tạo và chứa thời lượng chính xác vì nếu tôi sử dụng av_dump_format(pFormatCtx, 0, videoName, 0); thì tôi thực sự nhận được dữ liệu thời lượng cùng với thông tin khác về video. Đây là những gì tôi nhận được khi tôi sử dụng av_dump_format(pFormatCtx, 0, videoName, 0);:FFMPEG không thể hiển thị thời lượng của video

Input # 0, avi, từ 'futurama.avi':

Thời gian: 00: 21: 36,28, hãy bắt đầu: 0,000000, bitrate: 1135 kb/s

Suối # 0.0: video: mpeg4 (Advanced Simple Profile), yuv420p, 512x384

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

Suối # 0.1: Âm thanh: ac3, 48000 Hz, âm thanh nổi, s16, 192 kb/s

Tôi không hiểu tại sao av_dum_format có thể hiển thị thời lượng và tôi không thể. Tôi đã kiểm tra định nghĩa hàm, để hiển thị thời lượng, hàm cũng sử dụng thời gian pFormatCtx->. Nó không chỉ là thời gian biến thành viên khác cũng không hiển thị dữ liệu thích hợp khi tôi gọi cho họ trong main.cpp

Dưới đây là main.cpp tôi:

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; 
} 

Tôi không biết nếu nó giúp nhưng , Tôi sử dụng QtCreator trên Ubuntu và liên kết các thư viện tĩnh.

Cảm ơn sự giúp đỡ của bạn.

+1

Có một cái gì đó mà 'av_dump_format' làm trước khi nó đọc 'pFormatCtv-> duration' mà làm cho lĩnh vực này có hiệu lực. Nói cách khác, có mã bổ sung phải được thực thi trước khi thời hạn trở thành hợp lệ. Theo dõi một số mã hoạt động và bạn sẽ tìm thấy phần còn thiếu. BTW, bạn vẫn quan tâm đến một câu trả lời cho điều này? –

+0

Tôi đã cố gắng sử dụng các tính năng của ffmpeg trên dự án cá nhân của tôi nhưng tôi đã kết thúc bằng cách sử dụng các quy trình mới với ffmpeg.exe. Tôi muốn tìm ra câu trả lời, ffmpeg là một công cụ rất mạnh, tôi chắc chắn tôi sẽ sử dụng nó trong tương lai và sẽ hiệu quả hơn nhiều nếu tôi biết cách sử dụng thư viện thay vì sử dụng tệp thực thi trong các quy trình mới. – Malkavian

+0

Tôi có thể sẽ không thể thử phương pháp của bạn bất cứ lúc nào sớm, tôi khá bận rộn những ngày này, tôi cho bạn phiếu bầu, tôi sẽ cho bạn biết nếu nó hoạt động. Cảm ơn một lần nữa! – Malkavian

Trả lời

1

Làm thế nào để có được thông tin thời gian (và nhiều hơn nữa) từ ffmpeg

Tôi sai lầm xung quanh với ffmpeg một thời gian trước và tìm thấy những đường cong học tập được khá dốc. Vì vậy, mặc dù OP đã hỏi câu hỏi này vài tháng trước, tôi sẽ đăng một số mã trong trường hợp những người khác ở đây trên SO đang tìm cách làm một cái gì đó tương tự. Hàm Open() bên dưới hoàn tất nhưng có nhiều xác nhận và thiếu cách xử lý lỗi thích hợp.

Right off, một sự khác biệt ngay lập tức tôi thấy là tôi đã sử dụng av_open_input_file thay vì avformat_open_input. Tôi cũng không sử dụng av_dump_format.

Tính thời lượng có thể phức tạp, đặc biệt là với H.264 và MPEG-2; xem cách thời gianSec được tính bên dưới.

Lưu ý: Ví dụ này cũng sử dụng số JUCE C++ Utility Library.

Lưu ý2: Mã này là phiên bản đã sửa đổi của 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

Thuộc tính thời lượng là time_base đơn vị không phải là mili giây hoặc giây.Việc chuyển đổi thành mili giây là khá dễ dàng,

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; 

Thời hạn hiện tại bằng msec, chỉ cần lấy sàn hoặc ceil để nhận được toàn bộ số msec, tùy theo bạn muốn.

+1

Lưu ý rằng thậm chí có một hàm nội tuyến trong các tiêu đề ffmpeg để chuyển đổi: 'av_q2d' trong' rational.h'. – Sam

1

Sự khác biệt giữa av_open_input_file()avformat_open_input() có thể là sau này không đọc thông tin luồng - do đó duration không được khởi tạo. Gọi số avformat_find_stream_info() đã khắc phục sự cố cho tôi.

Tôi lấy đoạn mã tính/hiển thị từ http://ffmpeg.org/doxygen/trunk/dump_8c_source.html#l00480 (lưu ý rằng số dòng có thể và có thể sẽ thay đổi trong các phiên bản mới hơn). Và thêm một số mã khởi tạo, 'nó hoạt động cho tôi'. Hy vọng nó giúp.

#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; 
} 

Để biên dịch,

gcc -o duration -lavutil -lavformat duration.c 
Các vấn đề liên quan