Decode H264 video using libavcodec, C

2020-07-21 04:58发布

I'm trying to decode a raw h264 file with ffmpeg/libavcodec, but can't get it to work properly. Output should be a raw YUV-File for now. It's possible to compile the code with GCC

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

avcodec.dll, avutil.dll and swresample.dll must be placed in the directory for the .exe to start. Output in the CMD looks like this (only part of it, but its always like this):

[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

Heres my code

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

I also tried different videos in different formats (of course i changed the codec in the code in this case) like MPEG1, H263, H265, but none of those was working properly either. I hope someone can help me with this and tell me what I'm doing wrong here. Thanks

1条回答
Juvenile、少年°
2楼-- · 2020-07-21 05:49

Every input packet (avpkt) for avcodec_decode_video2 should contain full (and only) data for one frame i.e. it shouldn't be truncated in the middle of the frame NALs. So your code that reads and sends data in 4096 byte chunks wouldn't work. You need to packetize it yourself by parsing Annex B data and finding start codes and analyzing NAL types (even more in case of frame having more than 1 slice) or use libavformat parser for H.264. As workaround for H.264 you can try to use CODEC_FLAG2_CHUNKS flag but I am not sure how reliable it is and still think 4096-byte chunks are too small.

查看更多
登录 后发表回答