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; }