2012-09-24 18 views
6

Próbuję użyć narzędzia ffmpeg do przechwytywania klatek z pliku wideo, ale nie mogę uzyskać nawet czasu trwania filmu wideo. za każdym razem, gdy próbuję uzyskać do niego dostęp za pomocą pFormatCtx->duration uzyskuję 0. Wiem, że wskaźnik został zainicjowany i zawiera poprawny czas trwania, ponieważ jeśli użyję av_dump_format(pFormatCtx, 0, videoName, 0);, otrzymam dane o czasie trwania wraz z innymi informacjami o filmie. To co mam, kiedy używać av_dump_format(pFormatCtx, 0, videoName, 0);:FFMPEG Nie można wyświetlić czasu trwania wideo

Wprowadź # 0, AVI, z 'futurama.avi':

Czas trwania: 00: 21: 36.28, start: 0.000000, bitrate: 1135 kb/s

strumień # 0.0: Film: MPEG4 (Advanced prosty profil) yuv420p, 512x384

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

Strumień # 0,1: Audio: ac3, 48000 Hz, stereo, s16, 192 kb/s

Nie rozumiem, dlaczego av_dum_format może wyświetlać czas trwania, a ja nie. Sprawdziłem definicję funkcji, aby wyświetlić czas trwania, funkcja również używa pFormatCtx-> duration. To nie tylko czas trwania inne zmienne składowe również nie wyświetla prawidłowe dane, kiedy nazywają ich w main.cpp

Oto mój 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; 
} 

Nie wiem, czy to pomaga, ale Używam QtCreatora na Ubuntu i statycznie łączę biblioteki.

Dziękuję za pomoc.

+1

Jest coś, co robi przed 'av_dump_format' odczytuje' pFormatCtv-> duration' sprawia, że ​​pole ważny. Innymi słowy, istnieje dodatkowy kod, który musi zostać wykonany przed upływem czasu trwania. Prześledź trochę kodu, który działa i powinieneś znaleźć brakujący element. BTW, czy nadal jesteś zainteresowany odpowiedzią na to pytanie? –

+0

Próbowałem użyć funkcji ffmpeg w moim osobistym projekcie, ale w końcu użyłem nowych procesów z ffmpeg.exe. Chciałbym znaleźć odpowiedź, ffmpeg to bardzo potężne narzędzie, jestem pewien, że będę go używał w przyszłości, i byłoby znacznie wydajniejsze, gdybym wiedział, jak korzystać z biblioteki, zamiast używać pliku wykonywalnego w nowych procesach. – Malkavian

+0

Prawdopodobnie nie będę mógł wypróbować twojej metody w najbliższym czasie, jestem teraz bardzo zajęty, oddaję ci głos, dam ci znać, jeśli to zadziała. Dzięki jeszcze raz! – Malkavian

Odpowiedz

1

Jak uzyskać informacje trwania (i więcej) z ffmpeg

zawiedli wokół z ffmpeg jakiś czas temu i okazało się, że krzywa uczenia się być dość strome. Tak więc, chociaż PO zadawał to pytanie kilka miesięcy temu, zamieściłem kod na wypadek, gdyby inni tutaj na SO starali się zrobić coś podobnego. Poniższa funkcja Open() jest kompletna, ale ma wiele gwarancji i braków w sposobie poprawnej obsługi błędów.

Tuż jeden natychmiastowy różnica widzę to, że użyłem av_open_input_file zamiast avformat_open_input. Nie użyłem również av_dump_format.

Obliczanie czasu trwania może być trudne, szczególnie w przypadku H.264 i MPEG-2; zobacz, jak oblicza się durationSec.

Uwaga: w tym przykładzie również użyto JUCE C++ Utility Library.

Uwaga 2: ten kod jest zmodyfikowaną wersją 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

Właściwość duration jest w jednostkach time_base, a nie milisekundach lub sekundach.Konwersja do milisekund jest dość łatwe,

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; 

Czas jest teraz w milisekundach, po prostu zabrać głos lub ceil aby uzyskać całkowitą liczbę milisekund, w zależności od tego, jak lubisz.

+1

Zauważ, że istnieje nawet funkcja inline w nagłówkach ffmpeg do konwersji: 'av_q2d' w' rational.h'. – Sam

1

Różnica między av_open_input_file() i avformat_open_input() jest prawdopodobnie taka, że ​​ta ostatnia nie odczytuje informacji o strumieniu - dlatego też nie zainicjowano duration. Dzwonienie pod numer avformat_find_stream_info() rozwiązało problem.

Wziąłem fragment kodu, który oblicza/wyświetla od http://ffmpeg.org/doxygen/trunk/dump_8c_source.html#l00480 (zauważ, że numer linii może i prawdopodobnie zmieni się w nowszych wersjach). Dodałem też kod inicjalizacyjny "działa dla mnie". Mam nadzieję, że to pomoże.

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

Aby skompilować,

gcc -o duration -lavutil -lavformat duration.c 
Powiązane problemy