2012-09-24 18 views
6

Sto provando a utilizzare ffmpeg per acquisire fotogrammi da un file video, ma non riesco nemmeno a ottenere la durata di un video. ogni volta che provo ad accedervi con pFormatCtx->duration sto diventando 0. So che il puntatore è inizializzato e contiene la durata corretta perché se uso av_dump_format(pFormatCtx, 0, videoName, 0); allora effettivamente ottengo i dati di durata insieme ad altre informazioni sul video. Questo è ciò che ottengo quando uso av_dump_format(pFormatCtx, 0, videoName, 0);:FFMPEG non può visualizzare la durata di un video

Input # 0, avi, da 'futurama.avi':

Durata: 00: 21: 36.28, avviare: 0.000000, bitrate: 1.135 kb/s

flusso # 0.0: video: MPEG4 (Advanced Simple Profile), YUV420P, 512x384

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

flusso # 0.1: Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s

non capisco il motivo per cui av_dum_format in grado di visualizzare la durata e non posso. Ho controllato la definizione della funzione, per visualizzare la durata, la funzione utilizza anche pFormatCtx-> duration. E 'non solo le altre variabili membro di durata anche non visualizzano i dati appropriati quando li chiamo in main.cpp

Ecco il mio 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; 
} 

Non so se aiuta, ma , Uso QtCreator su Ubuntu e linko le librerie staticamente.

Grazie per il vostro aiuto.

+1

C'è qualcosa che 'av_dump_format' fa prima di leggere' pFormatCtv-> durata' che rende il campo valido. In altre parole, c'è un codice aggiuntivo che deve essere eseguito prima che la durata diventi valida. Traccia attraverso un codice che funziona e dovresti trovare il pezzo mancante. A proposito, sei ancora interessato a una risposta per questo? –

+0

Stavo cercando di utilizzare le funzionalità di ffmpeg sul mio progetto personale, ma alla fine ho utilizzato nuovi processi con ffmpeg.exe. Vorrei trovare una risposta, ffmpeg è uno strumento molto potente, sono sicuro che lo userò in futuro, e sarebbe molto più efficiente se sapessi usare la libreria piuttosto che usare l'eseguibile in nuovi processi. – Malkavian

+0

Probabilmente non sarò in grado di provare il tuo metodo in qualsiasi momento presto, sono abbastanza occupato in questi giorni, ti sto dando il voto, ti farò sapere se funziona. Grazie ancora! – Malkavian

risposta

1

Come ottenere informazioni durata (e non solo) da ffmpeg

Ho sbagliato in giro con ffmpeg qualche tempo fa e ho trovato la curva di apprendimento per essere piuttosto ripida. Quindi, anche se l'OP ha posto questa domanda mesi fa, inserirò del codice nel caso in cui gli altri qui su SO stiano cercando di fare qualcosa di simile. La funzione Open() qui sotto è completa ma ha molte asserzioni e mancanze sulla corretta gestione degli errori.

Diritto al largo, una differenza immediata che vedo è che ho usato av_open_input_file invece di avformat_open_input. Inoltre, non ho utilizzato av_dump_format.

Il calcolo della durata può essere complicato, specialmente con H.264 e MPEG-2; vedere come durationSec è calcolato di seguito.

Nota: questo esempio utilizza anche lo JUCE C++ Utility Library.

Nota2: questo codice è una versione modificata di 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

La proprietà durata è in time_base unità non millisecondi o secondi.La conversione in millisecondi è abbastanza facile,

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; 

La durata è ora in msec, basta prendere la parola o ceil per ottenere un numero intero di millisecondi, a seconda di quale ti piace.

+1

Nota che c'è anche una funzione inline nelle intestazioni ffmpeg da convertire: 'av_q2d' in' rational.h'. – Sam

1

Differenza tra av_open_input_file() e avformat_open_input() è probabilmente che quest'ultimo non legge le informazioni sul flusso - quindi duration non è inizializzato. La chiamata a avformat_find_stream_info() ha risolto il problema per me.

Ho preso lo snippet di codice che calcola/visualizza da http://ffmpeg.org/doxygen/trunk/dump_8c_source.html#l00480 (nota che il numero di riga può e probabilmente cambierà nelle versioni più recenti). E ha aggiunto un codice di inizializzazione, "funziona per me". Spero che sia d'aiuto.

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

Per compilare,

gcc -o duration -lavutil -lavformat duration.c