news 2026/5/7 23:08:29

FFmpeg 音视频开发笔记(一):H.264 解码为 YUV

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FFmpeg 音视频开发笔记(一):H.264 解码为 YUV

1. 看了一下雷霄哥的代码,自己用ai学习一版h.264转换为.yuv文件的代码。

h.264代码生成可以使用ffmpeg的命令行比如

ffmpeg -i input.mp4 -c:v libx264 -s 1280x720 -an output.h264

-c:v libx264 使用 H.264 编码器
-an 不要音频
-s 1280x720 分辨率
-b:v 2M 视频码率
-r 25 帧率
-d 10 时长(秒)

2. 我使用了ffplay对.yuv文件进行播放,代码:

ffplay -video_size 1280x720 -pix_fmt yuv420p output.yuv

遇到了几个问题,第一个问题就是如果你h264的分辨率是1280x720 如果你设置ffplay为 640x480,但实际视频可能是别的尺寸。就会出现:闪烁因为播放分辨率不对

3. 实现代码:

#include <stdio.h> #define __STDC_CONSTANT_MACROS #ifdef __cplusplus extern "C" { #endif #include <libavcodec/avcodec.h> #include <libavutil/pixdesc.h> #ifdef __cplusplus } #endif // 解决 FF_INPUT_BUFFER_PADDING_SIZE 未定义的问题 #ifndef FF_INPUT_BUFFER_PADDING_SIZE #define FF_INPUT_BUFFER_PADDING_SIZE 64 #endif #define TEST_H264 1 #define TEST_HEVC 0 int main(int argc, char *argv[]){ #if TEST_HEVC enum AVCodecID codec_id = AV_CODEC_ID_HEVC; const char* input_file = "test.hevc"; #elif TEST_H264 enum AVCodecID codec_id = AV_CODEC_ID_H264; const char* input_file = "test.h264"; #endif const char* output_file = "output.yuv"; const AVCodec* codec = avcodec_find_decoder(codec_id); if(!codec){ fprintf(stderr, "Codec not found\n"); return -1; } AVCodecContext* codec_ctx = avcodec_alloc_context3(codec); if(!codec_ctx){ fprintf(stderr, "Codec context allocation failed\n"); return -1; } if(avcodec_open2(codec_ctx,codec,NULL) < 0){ fprintf(stderr, "Codec open failed\n"); return -1; } // ========== 5. 初始化解析器 ========== AVCodecParserContext* parser_ctx = av_parser_init(codec_id); if(!parser_ctx){ fprintf(stderr, "Parser context initialization failed\n"); return -1; } FILE* in_file = fopen(input_file,"rb"); if(!in_file){ fprintf(stderr, "Input file open failed\n"); av_parser_close(parser_ctx); return -1; } FILE* out_file = fopen(output_file,"wb"); if(!out_file){ fprintf(stderr, "Output file open failed\n"); fclose(in_file); av_parser_close(parser_ctx); avcodec_free_context(&codec_ctx); return -1; } AVFrame* frame = av_frame_alloc(); AVPacket* pkt = av_packet_alloc(); if(!frame || !pkt){ fprintf(stderr, "Frame or packet allocation failed\n"); if(frame) av_frame_free(&frame); if(pkt) av_packet_free(&pkt); fclose(in_file); fclose(out_file); av_parser_close(parser_ctx); avcodec_free_context(&codec_ctx); return -1; } const size_t buf_size = 4096; uint8_t* buffer = (uint8_t*)malloc(buf_size + AV_INPUT_BUFFER_PADDING_SIZE); if(!buffer){ fprintf(stderr, "Buffer allocation failed\n"); av_frame_free(&frame); av_packet_free(&pkt); fclose(in_file); fclose(out_file); av_parser_close(parser_ctx); avcodec_free_context(&codec_ctx); return -1; } memset(buffer,0,buf_size + AV_INPUT_BUFFER_PADDING_SIZE); int first_frame = 1; int frame_count = 0; int write_count = 0; int ret = 0; while(1){ size_t data_size = fread(buffer,1,buf_size,in_file); if(data_size == 0) { break; } uint8_t* cur_ptr = buffer; size_t cur_size = data_size; while(cur_size > 0){ int len = av_parser_parse2(parser_ctx,codec_ctx,&pkt->data,&pkt->size, cur_ptr,(int)cur_size,AV_NOPTS_VALUE,AV_NOPTS_VALUE,AV_NOPTS_VALUE); cur_ptr += len; cur_size -= len; if(pkt->size == 0) { continue; } printf("[Packet] size:%d\n",pkt->size); switch(parser_ctx->pict_type){ case AV_PICTURE_TYPE_I: printf("[Packet] I frame\n"); break; case AV_PICTURE_TYPE_P: printf("[Packet] P frame\n"); break; case AV_PICTURE_TYPE_B: printf("[Packet] B frame\n"); break; default: printf("[Packet] Unknown frame\n"); break; } printf("number: %d\n",parser_ctx->output_picture_number); int ret = avcodec_send_packet(codec_ctx,pkt); if(ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF){ fprintf(stderr, "Error sending packet to decoder: %d\n",ret); break; } while(1){ ret = avcodec_receive_frame(codec_ctx,frame); if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){ break; }else if(ret < 0){ // const char *err_str = av_err2str(ret); fprintf(stderr, "冲刷时出错: %d\n", ret); break; } if(first_frame){ printf("解码器: %s\n",codec_ctx->codec->long_name); printf("[Frame] width:%d, height:%d, format:%s\n", frame->width,frame->height,av_get_pix_fmt_name((AVPixelFormat)frame->format)); printf("[Frame] linesize: Y=%d, U=%d, V=%d\n", frame->linesize[0], frame->linesize[1], frame->linesize[2]); first_frame = 0; } if(frame->format == AV_PIX_FMT_YUV420P){ for(int i = 0;i < frame->height;i++){ fwrite(frame->data[0]+i*frame->linesize[0],1,frame->width,out_file); } for(int i = 0;i < frame->height/2;i++){ fwrite(frame->data[1]+i*frame->linesize[1],1,frame->width/2,out_file); } for(int i = 0;i < frame->height/2;i++){ fwrite(frame->data[2]+i*frame->linesize[2],1,frame->width/2,out_file); } write_count++; }else{ fprintf(stderr, "Unsupported pixel format: %s\n",av_get_pix_fmt_name((AVPixelFormat)frame->format)); } frame_count++; printf("[Frame] number: %d\n",frame_count); } } } avcodec_send_packet(codec_ctx,NULL); while(1){ ret = avcodec_receive_frame(codec_ctx,frame); if(ret == AVERROR_EOF) break; if(ret < 0){ fprintf(stderr, "冲刷时出错: %d\n", ret); break; } if(frame->format == AV_PIX_FMT_YUV420P){ for(int i = 0;i < frame->height;i++){ fwrite(frame->data[0]+i*frame->linesize[0],1,frame->width,out_file); } for(int i = 0;i < frame->height/2;i++){ fwrite(frame->data[1]+i*frame->linesize[1],1,frame->width/2,out_file); } for(int i = 0;i < frame->height/2;i++){ fwrite(frame->data[2]+i*frame->linesize[2],1,frame->width/2,out_file); } write_count++; } frame_count++; printf("[Frame] number: %d\n",frame_count); } printf("解码完成,共解码%d帧,写入%d帧\n",frame_count, write_count); // ========== 11. 释放资源 ========== free(buffer); av_packet_free(&pkt); av_frame_free(&frame); fclose(out_file); fclose(in_file); av_parser_close(parser_ctx); avcodec_free_context(&codec_ctx); return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/7 23:06:19

赵明能享受到千里科技的推背感吗?

三年增超17倍&#xff0c;千里科技的底气谁给的&#xff1f;作者|华应龙编辑|古廿“窗口在缩紧&#xff0c;千里科技现在想做第三方供应商有点晚了”一位汽车行业相关人士告诉「市象」。这句话&#xff0c;多少给赵明的新战场泼了一盆冷水。离开荣耀之后&#xff0c;赵明没有去…

作者头像 李华
网站建设 2026/5/7 23:03:45

FastAPI 中间件

FastAPI 中间件学习笔记 一、中间件概念 中间件是一个在请求到达路由处理函数之前和响应返回客户端之前执行的函数&#xff0c;类似于一个"拦截器"&#xff1a; 客户端请求 → 中间件1 → 中间件2 → ... → 路由处理函数↓ 客户端响应 ← 中间件1 ← 中间件2 ← ...…

作者头像 李华
网站建设 2026/5/7 23:03:43

FastAPI CORS 跨域

FastAPI CORS 跨域学习笔记 一、什么是跨域问题 1. 同源策略 浏览器遵循同源策略&#xff08;Same-Origin Policy&#xff09;&#xff0c;限制一个源的网页向另一个源发送请求。 同源 协议 域名 端口 三者一致&#xff1a;URL AURL B是否同源原因http://example.com/ahttp:…

作者头像 李华