【问题标题】:Decode H264 video using libavcodec, C使用 libavcodec、C 解码 H264 视频
【发布时间】:2015-03-08 08:43:07
【问题描述】:

我正在尝试使用 ffmpeg/libavcodec 解码原始 h264 文件,但无法使其正常工作。输出现在应该是一个原始的 YUV 文件。可以用 GCC 编译代码

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,但这些视频也都不能正常工作。 我希望有人可以帮助我并告诉我我在这里做错了什么。谢谢

【问题讨论】:

  • 仅供参考:至少从 2017 年开始,在问题中提供的示例代码中,FF_INPUT_BUFFER_PADDING_SIZE 必须替换为 AV_INPUT_BUFFER_PADDING_SIZE(否则它甚至不会编译)

标签: c gcc ffmpeg h.264 libavcodec


【解决方案1】:

avcodec_decode_video2 的每个输入数据包 (avpkt) 都应包含一帧的完整(且仅)数据,即不应在帧 NAL 中间截断。因此,以 4096 字节块读取和发送数据的代码将无法正常工作。您需要通过解析附件 B 数据并查找起始代码和分析 NAL 类型(在帧具有超过 1 个切片的情况下更是如此)或使用 H.264 的 libavformat 解析器来自行打包。作为 H.264 的解决方法,您可以尝试使用 CODEC_FLAG2_CHUNKS 标志,但我不确定它有多可靠,仍然认为 4096 字节的块太小。

【讨论】:

  • 我尝试将 CODEC_FLAG2_CHUNKS 与 128k 块一起使用,但仍然无法正常工作。我将如何使用 libavformat 将块解析为单帧?或者自己打包数据会更容易吗?谢谢。
  • 要使用 libavformat,您需要使用可以猜测格式的 avformat_open_input() 打开文件,或者您可以指定它而不是使用 av_read_frame() 读取数据包并将数据包从所需的流发送到解码器,然后关闭带有 avformat_close_input() 的文件。您可能还需要 avformat_find_stream_info() 来获取解码器的尺寸和 av_find_best_stream() 来找到与视频流相对应的所需 AVPacket.stream_index (如果容器有超过 1 个流)。更多信息请阅读 Demuxing 组中的 avformat.h cmets。
  • 我玩了一下我的 h264 文件(x264 编码)并尝试读出 NAL。我认为它有效,我读了第一个 120,我找到了起始码(00 00 00 01),第一个 NAL 后面是 67,第二个是 68,所有其他的 01 或 41.01 属于 b 帧,41 到p帧?我如何知道哪些 NAL 属于一个框架?为什么没有 I 帧?谢谢
  • NAL 起始码后的第一个字节代表字段:forbidden_​​zero_bit f(1)、nal_ref_idc u(2)、nal_unit_type u(5)。所以 0x67 意味着 nal_ref_idc=3(最高优先级)和 nal_unit_type=7(SPS)。 0x67 表示 nal_ref_idc=3 和 nal_unit_type=8 (PPS)。 0x01 表示 nal_ref_idc=0(一次性/非参考优先级)和 nal_unit_type=1(非 IDR 切片)。 0x41 表示 nal_ref_idc=2(高/参考优先级)和 nal_unit_type=1(非 IDR 切片)。我建议您使用 libavformat,因为您已经使用 libavcodec 而不是手动解析,因为找到帧边界而不是简单的 NAL 边界并不容易。
猜你喜欢
  • 2011-03-30
  • 2019-01-21
  • 2013-01-16
  • 2020-04-27
  • 2012-11-12
  • 1970-01-01
  • 2013-12-30
  • 2012-08-03
  • 1970-01-01
相关资源
最近更新 更多