2015-01-09 361 views
2

我試圖用ffmpeg/libavcodec解碼原始h264文件,但無法使其正常工作。現在輸出應該是一個原始的YUV文件。這可能與GCC使用libavcodec解碼H264視頻,C

gcc -o decoder decoder.c -L./lib/ -llibavcodec -llibavutil 

avcodec.dll,avutil.dll和swresample.dll必須放置在目錄中的.exe開始編譯代碼。在CMD輸出看起來像這樣(只是其中的一部分,但它總是這樣):

[h264 @ 00a80f20] reference picture missing during reorder 
[h264 @ 00a80f20] Missing reference picture, default is 65562 
[h264 @ 00a80f20] error while decoding MB 80 54, bytestream -10 
[h264 @ 00a80f20] concealing 1649 DC, 1649 AC, 1649 MV errors in B frame 
[h264 @ 00a80f20] reference picture missing during reorder 
[h264 @ 00a80f20] reference picture missing during reorder 
[h264 @ 00a80f20] reference picture missing during reorder 
[h264 @ 00a80f20] Missing reference picture, default is 65566 
[h264 @ 00a80f20] Missing reference picture, default is 65566 
[h264 @ 00a80f20] Missing reference picture, default is 65566 
[h264 @ 00a80f20] reference picture missing during reorder 
[h264 @ 00a80f20] Missing reference picture, default is 65568 
[h264 @ 00a80f20] reference picture missing during reorder 
[h264 @ 00a80f20] Missing reference picture, default is 65570 
[h264 @ 00a80f20] reference picture missing during reorder 

繼承人我的代碼

#include <stdlib.h> 
#include <stdio.h> 

#ifdef HAVE_AV_CONFIG_H 
#undef HAVE_AV_CONFIG_H 
#endif 

#include "libavcodec/avcodec.h" 
//#include "libavcodec/libavutil/mathematics.h" 

#define INBUF_SIZE 4096 

void video_decode(char *outfilename, char *filename) 
{ 
    AVCodec *codec; 
    AVCodecContext *c= NULL; 
    int frame, got_picture, len; 
    FILE *f, *outf; 
    AVFrame *picture; 
    uint8_t inbuf[INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE]; 
    AVPacket avpkt; 
    int i; 

    av_init_packet(&avpkt); 

    memset(inbuf + INBUF_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE); 

    codec = avcodec_find_decoder(AV_CODEC_ID_H264); 
    if (!codec) { 
     fprintf(stderr, "codec not found\n"); 
     exit(1); 
    } 

    c = avcodec_alloc_context3(codec); 
    picture = av_frame_alloc(); 

    if((codec->capabilities)&CODEC_CAP_TRUNCATED) 
     (c->flags) |= CODEC_FLAG_TRUNCATED; 

    c->height = 1080; 
    c->width = 1920; 

    if (avcodec_open2(c, codec, NULL) < 0) { 
     fprintf(stderr, "could not open codec\n"); 
     exit(1); 
    } 

    f = fopen(filename, "rb"); 
    if (!f) { 
     fprintf(stderr, "could not open %s\n", filename); 
     exit(1); 
    } 

    outf = fopen(outfilename,"w"); 
    if(!outf){ 
     fprintf(stderr, "could not open %s\n", filename); 
     exit(1); 
    } 
    frame = 0; 
    for(;;) { 
     avpkt.size = fread(inbuf, 1, INBUF_SIZE, f); 
     if (avpkt.size == 0) 
      break; 

     avpkt.data = inbuf; 
     while (avpkt.size > 0) { 

      len = avcodec_decode_video2(c, picture, &got_picture, &avpkt); 

      if (len < 0) { 
       fprintf(stderr, "Error while decoding frame %d\n", frame); 
       exit(1); 
      } 
      if (got_picture) { 
       printf("saving frame %3d\n", frame); 
       fflush(stdout); 
       for(i=0; i<c->height; i++) 
        fwrite(picture->data[0] + i * picture->linesize[0], 1, c->width, outf ); 
       for(i=0; i<c->height/2; i++) 
        fwrite(picture->data[1] + i * picture->linesize[1], 1, c->width/2, outf); 
       for(i=0; i<c->height/2; i++) 
        fwrite(picture->data[2] + i * picture->linesize[2], 1, c->width/2, outf); 
       frame++; 
      } 
      avpkt.size -= len; 
      avpkt.data += len; 
     } 
    } 

    avpkt.data = NULL; 
    avpkt.size = 0; 
    len = avcodec_decode_video2(c,picture, &got_picture, &avpkt); 
    if(got_picture) { 
     printf("saving last frame %d\n",frame); 
     fflush(stdout); 
     for(i=0; i<c->height; i++) 
      fwrite(picture->data[0] + i * picture->linesize[0], 1, c->width, outf); 
     for(i=0; i<c->height/2; i++) 
      fwrite(picture->data[1] + i * picture->linesize[1], 1, c->width/2, outf); 
     for(i=0; i<c->height/2; i++) 
      fwrite(picture->data[2] + i * picture->linesize[2], 1, c->width/2, outf); 
     frame++; 
    } 

    fclose(f); 
    fclose(outf); 

    avcodec_close(c); 
    av_free(c); 
    av_frame_free(&picture); 
    printf("\n"); 
} 

int main(int argc, char **argv){ 
    avcodec_register_all(); 
    video_decode("test", "trailer.264"); 

    return 0; 
} 

我也嘗試以不同的格式不同的視頻(當然,我改變在這種情況下代碼中的編解碼器)像MPEG1,H263,H265,但沒有一個工作正常。 我希望有人能幫助我,並告訴我我在這裏做錯了什麼。感謝

回答

3

爲avcodec_decode_video2每個輸入分組(avpkt)應包含一幀,即它不應在幀的NAL的中間被截斷滿(只)數據。因此,以4096字節塊讀取和發送數據的代碼將無法工作。您需要通過解析附錄B數據並查找開始代碼和分析NAL類型(甚至在幀數超過1片的情況下甚至更多)或使用用於H.264的libavformat解析器來分包。作爲H.264的解決方法,您可以嘗試使用CODEC_FLAG2_CHUNKS標誌,但我不確定它有多可靠,並且仍然認爲4096字節的塊太小。

+0

我嘗試使用CODEC_FLAG2_CHUNKS與128K塊,但它仍然無法正常工作。我將如何使用libavformat獲得解析爲單個幀的塊?或者我自己將數據分組化會更容易些嗎?謝謝。 – deadman 2015-01-10 08:46:21

+1

要使用libavformat,您需要使用avformat_open_input()來打開文件,該文件可以猜測格式,或者您可以指定它,而不是使用av_read_frame()讀取數據包,並將數據包從需要的數據流發送到解碼器,然後使用avformat_close_input()關閉文件。您可能還需要使用avformat_find_stream_info()來獲取解碼器的尺寸,並使用av_find_best_stream()來查找與視頻流相對應的所需AVPacket.stream_index(如果容器具有多於一個流)。有關更多信息,請在Demuxing組中閱讀avformat.h註釋。 – nobody555 2015-01-10 12:20:39

+0

我用我的h264文件(x264編碼)玩了一下,試着讀出NAL。我認爲它工作,我讀了第120個,我找到起始碼(00 00 00 01),第一個NAL後面是67,第二個是68,其他所有其他的01或41.01屬於b幀,41到p幀?我怎麼知道哪些NAL屬於一個框架?爲什麼沒有I幀?謝謝 – deadman 2015-01-10 15:15:41