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;
}
相关推荐
RTC实战笔记2 天前
Android 实时音视频接入教程:媒体补充增强信息(SEI)
音视频·媒体·rtc
潜创微科技2 天前
HDMI1.3 无线传输芯片方案 空旷 150 米量产级音视频方案
音视频
VidDown2 天前
VidDown 工具站:免费、本地优先的开发者工具箱
javascript·编辑器·音视频·视频编解码·视频
换个昵称都难2 天前
音频格式之WAV
音视频
AI创界者3 天前
PilotTTS 一键整合包(Win/Mac):8G 显存畅跑,实测解锁情绪与副语言的精准控制
人工智能·macos·aigc·音视频
u152109648493 天前
S.S.Audio PRO A2音频隔离器
嵌入式硬件·音视频·实时音视频·视频编解码·视频
闪闪发亮的小星星3 天前
高斯光以及高斯光公式解释
笔记
cqbzcsq3 天前
CellFlow虚拟细胞论文阅读
论文阅读·人工智能·笔记·学习·生物信息
VidDown3 天前
显卡处理视频技术详解:从硬解码到 NVENC,GPU 如何让视频处理起飞?
javascript·编辑器·音视频·视频编解码·视频
阿米亚波3 天前
【Windows】QEMU 启动 openEuler aarch64/arm64 架构系统 + 离线软件源
linux·windows·经验分享·笔记·架构·arm