使用 libavcodec、C 解码 H264 视频
Decode H264 video using libavcodec, C
我正在尝试使用 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,但其中 none 也能正常工作。
我希望有人可以帮助我并告诉我我在这里做错了什么。谢谢
avcodec_decode_video2 的每个输入数据包 (avpkt) 都应包含一帧的完整(且仅)数据,即它不应在帧 NAL 的中间被截断。因此,您以 4096 字节块读取和发送数据的代码将无法工作。您需要通过解析 Annex B 数据并查找起始代码和分析 NAL 类型(甚至在帧具有超过 1 个切片的情况下更多)或使用 H.264 的 libavformat 解析器来对其进行打包。作为 H.264 的解决方法,您可以尝试使用 CODEC_FLAG2_CHUNKS 标志,但我不确定它的可靠性如何,并且仍然认为 4096 字节的块太小了。
我正在尝试使用 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,但其中 none 也能正常工作。 我希望有人可以帮助我并告诉我我在这里做错了什么。谢谢
avcodec_decode_video2 的每个输入数据包 (avpkt) 都应包含一帧的完整(且仅)数据,即它不应在帧 NAL 的中间被截断。因此,您以 4096 字节块读取和发送数据的代码将无法工作。您需要通过解析 Annex B 数据并查找起始代码和分析 NAL 类型(甚至在帧具有超过 1 个切片的情况下更多)或使用 H.264 的 libavformat 解析器来对其进行打包。作为 H.264 的解决方法,您可以尝试使用 CODEC_FLAG2_CHUNKS 标志,但我不确定它的可靠性如何,并且仍然认为 4096 字节的块太小了。