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;
}
相关推荐
爱听歌的周童鞋1 小时前
Learn-Claude-Code | 笔记 | Collaboration | s11 Autonomous Agents
笔记·llm·agent·claude code·collaboration·autonomous
05候补工程师2 小时前
[线性代数] 判定线性相关性的“降维打击”:从基本定理到速通特殊法
经验分享·笔记·学习·线性代数·考研
ai产品老杨2 小时前
突破品牌壁垒:基于 GB28181 与 RTSP 的异构 AI 视频平台架构深度解析(支持 Docker 与源码交付)
人工智能·架构·音视频
AI服务老曹2 小时前
【架构深析】打破安防“黑盒”:GB28181/RTSP 视频管理平台如何通过源码交付与 API 驱动节省 95% 开发成本
架构·音视频
科研前沿2 小时前
多视角相机驱动的室内人员空间定位技术白皮书
大数据·人工智能·python·科技·数码相机·音视频
sweetheart7-73 小时前
go/golang 入门学习笔记(Java/Python/C++转Go快速上手)
笔记·学习·golang·go语言
Byron__3 小时前
Java JVM核心知识点复习笔记
java·jvm·笔记
ai产品老杨3 小时前
深度解析:异构算力下的 AI 视频管理平台架构实现 (GB28181 / Docker / 源码交付)
人工智能·架构·音视频
爱莉希雅&&&3 小时前
MySQL MGR + MySQL Router 高可用集群完整笔记(含手动配置 + Shell 接管双路线)
linux·数据库·笔记·mysql·mysqlrouter·mysqlshell