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 时长(秒)

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

ffplay -video_size 1280x720 -pix_fmt yuv420p output.yuv

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

  1. 实现代码:
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;
}
相关推荐
qq_369224337 分钟前
打开剪辑/直播/播放器提示ffmpeg.dll丢失?专属场景修复方法汇总
ffmpeg·dll·dll修复·dll错误
都市放羊9 分钟前
网络小白自学网工——因特网与网络互联技术
网络·笔记·自学
東隅已逝,桑榆非晚14 分钟前
新手入门指南:认识 C 语言文件操作(上)
c语言·开发语言·笔记
暴躁小师兄数据学院18 分钟前
【AI大数据工程师特训笔记】第08讲:集合运算与超级函数
大数据·笔记·sql·ai·postgresql
searchforAI36 分钟前
我的Obsidian知识库,现在可以自动剪藏笔记到本地了
人工智能·笔记·学习·音视频·ai工具·obsidian·视频总结
NiceCloud喜云41 分钟前
Claude Code 跑 HyperFrames 实测:本地生成 AI 视频素材全流程
java·运维·人工智能·自动化·json·音视频·飞书
lcj25111 小时前
vector的基本使用 + 手搓成员变量 size capacity begin end operator[] reserve扩容 拷贝构造 赋值析构
开发语言·c++·笔记·面试
眺望电子-ARM嵌入式1 小时前
RK3588+XS9922B:I2S-TDM多通道音频采集实例
音视频
DogDaoDao1 小时前
AV1 帧内预测核心文件 reconintra.c 源码深度解析
音视频·实时音视频·视频编解码·av1·libaom·帧内预测·reconintra.c
天行健王春城老师1 小时前
制造业生产管控实操指南 PMC体系落地与优化技巧解析
笔记·制造