2012-09-24 66 views
6

Estoy tratando de usar ffmpeg para capturar fotogramas de un archivo de video, pero ni siquiera puedo obtener la duración de un video. cada vez que intento acceder a él con pFormatCtx->duration obtengo 0. Sé que el puntero se inicializó y contiene la duración correcta porque si uso av_dump_format(pFormatCtx, 0, videoName, 0);, entonces obtengo los datos de duración junto con otra información sobre el video. Esto es lo que me pasa cuando uso av_dump_format(pFormatCtx, 0, videoName, 0);:FFMPEG No se puede mostrar la duración de un video

Entrada # 0, avi, de 'futurama.avi':

Duración: 00: 21: 36.28, inicio: 0,000000, tasa de bits: 1135 KB/s

corriente # 0.0: vídeo: MPEG4 (Advanced simple Profile), yuv420p, 512x384

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

corriente # 0.1: Audio: ac3, 48000 Hz, estéreo, s16, 192 kb/s

No entiendo por qué av_dum_format puede mostrar la duración y no puedo. Comprobé la definición de la función, para mostrar la duración, la función también usa pFormatCtx-> duration. No es sólo las otras variables miembro de duración también no se muestran los datos adecuados cuando los llamo en main.cpp

Aquí está mi 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; 
} 

No sé si ayuda, pero , Uso QtCreator en Ubuntu y conecté las bibliotecas estáticamente.

Gracias por su ayuda.

+1

Hay algo que 'av_dump_format' hace antes de que se lee' pFormatCtv-> duration' que hace que el campo válido. En otras palabras, hay un código adicional que se debe ejecutar antes de que la duración sea válida. Rastrea a través de algún código que funcione y deberías encontrar la pieza que falta. Por cierto, ¿sigues interesado en una respuesta para esto? –

+0

Estaba tratando de usar las características de ffmpeg en mi proyecto personal, pero terminé usando nuevos procesos con ffmpeg.exe. Me gustaría encontrar una respuesta, ffmpeg es una herramienta muy poderosa, estoy seguro de que la usaré en el futuro, y sería mucho más eficiente si sé cómo usar la biblioteca en lugar de usar el ejecutable. en nuevos procesos. – Malkavian

+0

Probablemente no pueda probar su método pronto, estoy muy ocupado estos días, le doy la votación, le informaré si funciona. ¡Gracias de nuevo! – Malkavian

Respuesta

1

¿Cómo obtener información de duración (y más) de ffmpeg

ensucié alrededor con ffmpeg hace un tiempo y encontré la curva de aprendizaje que ser bastante empinada. Entonces, aunque OP hizo esta pregunta hace meses, publicaré un código en caso de que otros aquí en SO estén buscando hacer algo similar. La función Open() a continuación está completa, pero tiene muchas afirmaciones y carencias en el manejo correcto de errores.

A un lado, una diferencia inmediata lo que veo es que he usado av_open_input_file en lugar de avformat_open_input. Tampoco utilicé av_dump_format.

Calcular la duración puede ser complicado, especialmente con H.264 y MPEG-2; ver cómo durationSec se calcula a continuación.

Nota: Este ejemplo también usa JUCE C++ Utility Library.

Nota 2: Este código es una versión modificada del 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 propiedad duración es en time_base no unidades de milisegundos o segundos.La conversión de milisegundos es bastante fácil,

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 duración está ahora en milisegundos, sólo toma el suelo o ceil para obtener un número entero de milisegundos, lo que te gusta.

+1

Tenga en cuenta que incluso hay una función en línea en los encabezados de ffmpeg para convertir: 'av_q2d' en' rational.h'. – Sam

1

Diferencia entre av_open_input_file() y avformat_open_input() es probable que este último no lee información de la corriente - de ahí duration no se ha inicializado. Llamar al avformat_find_stream_info() me solucionó el problema.

Tomé el fragmento de código que calcula/muestra desde http://ffmpeg.org/doxygen/trunk/dump_8c_source.html#l00480 (tenga en cuenta que el número de línea puede y probablemente cambiará en las versiones más recientes). Y agregó un código de inicialización, 'funciona para mí'. Espero eso ayude.

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

Para compilar,

gcc -o duration -lavutil -lavformat duration.c 
Cuestiones relacionadas