- 看了一下雷霄哥的代码,自己用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 时长(秒)
- 我使用了ffplay对.yuv文件进行播放,代码:
ffplay -video_size 1280x720 -pix_fmt yuv420p output.yuv
遇到了几个问题,第一个问题就是如果你h264的分辨率是1280x720 如果你设置ffplay为 640x480,但实际视频可能是别的尺寸。就会出现:闪烁因为播放分辨率不对
- 实现代码:
cpp
#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;
}