Windows下FFMPEG调用测试(解码264)
本篇主要测试FFmpeg解码H264
代码逻辑:
-
1 寻找解码器 avcodec_find_decoder(AV_CODEC_ID_H264);
- 2 创建解码上下文 avcodec_alloc_context3(codec);
- 3 创建断帧上下文(对应编码的时候知道YUV一帧的数据量,解码的时候由于H264一帧多长并不知道,且没有规律,所以要先对读取的数据断帧)
av_parser_init(codec->id);
- 4 初始化解码上下文,打开解码器
由于解码的时候自然就得到视频参数,所以这里就不用额外设置参数了;编码的时候是需要设置GOP,FPS,W,H,level之类的
avcodec_open2(c, codec, NULL)
- 5 创建package装H264,创建frame装解码后的YUV
frame = av_frame_alloc();
pkt = av_packet_alloc();
- 6 开始断帧,送解码库解码(注意解码到最后一帧,要送一个空package,把解码库内部的缓存刷出来)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | do { /* read raw data from the input file */ data_size = fread(inbuf, 1, INBUF_SIZE, f); if (ferror(f)) break ; eof = !data_size; /* use the parser to split the data into frames */ data = inbuf; while (data_size > 0 || eof) { ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); if (ret < 0) { fprintf(stderr, "Error while parsing\n" ); exit(1); } data += ret; data_size -= ret; if (pkt->size) decode(c, frame, pkt, outfilename); else if (eof) break ; } } while (!eof); |
- 7 解码函数
avcodec_send_packet(dec_ctx, pkt);
解码输出:avcodec_receive_frame(dec_ctx, frame);
- 8 关闭上下文和文件
av_parser_close(parser);
avcodec_free_context(&c);
av_frame_free(&frame);
av_packet_free(&pkt);
-
可运行代码:(注意YV12中YUV的存储顺序,和frame中数据所表达的意义)
这里用的H264你可以从FFmpeg中用命令行提取一个,然后用下边的代码解码;我的这个264是处理问题时候其他组件保存的一个文件
#include <stdio.h> #include <stdlib.h> #include <string.h> extern "C" { #include <libavcodec/avcodec.h> char av_error[AV_ERROR_MAX_STRING_SIZE] = { 0 }; #define av_err2str(errnum) av_make_error_string(av_error, AV_ERROR_MAX_STRING_SIZE, errnum) #define INBUF_SIZE 4096 static FILE* fout; static void pgm_save(AVFrame*frame, int xsize, int ysize, const char* filename) { int i; if (!fout) { fout = fopen(filename, "wb"); } //linesize中存储的是宽度,不是总长度 //写入顺序,Y V U =YV12 ;解码后的数据是YUV顺序存储的 for (i = 0; i < ysize; i++) fwrite(frame->data[0] + i * frame->linesize[0], 1, xsize, fout); for (i = 0; i < ysize/2; i++) fwrite(frame->data[2] + i * frame->linesize[2], 1, xsize/2, fout); for (i = 0; i < ysize / 2; i++) fwrite(frame->data[1] + i * frame->linesize[1], 1, xsize/2, fout); } static void decode(AVCodecContext* dec_ctx, AVFrame* frame, AVPacket* pkt, const char* filename) { char buf[1024]; int ret; ret = avcodec_send_packet(dec_ctx, pkt); if (ret < 0) { fprintf(stderr, "Error sending a packet for decoding\n"); exit(1); } while (ret >= 0) { ret = avcodec_receive_frame(dec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return; else if (ret < 0) { fprintf(stderr, "Error during decoding\n"); exit(1); } printf("saving frame %3ld\n", dec_ctx->frame_num); printf("YUV pts:%ld \n", frame->pts); fflush(stdout); /* the picture is allocated by the decoder. no need to free it */ pgm_save(frame,frame->width, frame->height, filename); } } int main(int argc, char** argv) { const char* filename, * outfilename; const AVCodec* codec; AVCodecParserContext* parser; AVCodecContext* c = NULL; FILE* f; AVFrame* frame; uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; uint8_t* data; size_t data_size; int ret; int eof; AVPacket* pkt; //注意直接解码要从I帧开始,所以如果H264开头不是关键帧是要报错的 filename = "D:/Emscripten/cmaketest/catch.264"; outfilename = "D:/Emscripten/cmaketest/catch_out.yuv"; pkt = av_packet_alloc(); if (!pkt) exit(1); /* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */ memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE); /* find the MPEG-1 video decoder */ codec = avcodec_find_decoder(AV_CODEC_ID_H264); if (!codec) { fprintf(stderr, "Codec not found\n"); exit(1); } //用来给H264断帧 parser = av_parser_init(codec->id); if (!parser) { fprintf(stderr, "parser not found\n"); exit(1); } c = avcodec_alloc_context3(codec); if (!c) { fprintf(stderr, "Could not allocate video codec context\n"); exit(1); } /* For some codecs, such as msmpeg4 and mpeg4, width and height MUST be initialized there because this information is not available in the bitstream. */ /* open it */ 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); } frame = av_frame_alloc(); if (!frame) { fprintf(stderr, "Could not allocate video frame\n"); exit(1); } do { /* read raw data from the input file */ data_size = fread(inbuf, 1, INBUF_SIZE, f); if (ferror(f)) break; eof = !data_size; /* use the parser to split the data into frames */ data = inbuf; while (data_size > 0 || eof) { ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); if (ret < 0) { fprintf(stderr, "Error while parsing\n"); exit(1); } data += ret; data_size -= ret; if (pkt->size) decode(c, frame, pkt, outfilename); else if (eof) break; } } while (!eof); /* flush the decoder */ decode(c, frame, NULL, outfilename); fclose(f); av_parser_close(parser); avcodec_free_context(&c); av_frame_free(&frame); av_packet_free(&pkt); if (fout) { fclose(fout); fout = nullptr; } return 0; } }
解码后的数据用elecard软件看,YUV正常,且颜色也是正常的:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
2022-09-21 string转wstring