FFMpeg视频编码实战和音频编码实战

视频编码流程

  1. avcodec_find_encoder:首先,通过指定的编码器名称(如H.264、MPEG-4等)找到对应的编码器。
  2. avcodec_alloc_context3:为找到的编码器分配一个上下文结构,这个结构包含了编码器所需的各种参数和状态信息。
  3. 设置编码上下文参数:配置编码器上下文的参数,包括分辨率、帧率、比特率等。
  4. avcodec_open2:打开编码器,准备开始编码工作。
  5. av_packet_alloc:分配一个AVPacket结构,用于存储编码后的数据包。
  6. av_frame_alloc:分配一个AVFrame结构,用于存储原始视频帧的数据。
  7. 设置Frame参数:配置AVFrame结构,包括设置像素格式、宽度、高度等参数。
  8. av_frame_get_buffer:为AVFrame分配缓冲区,用于存储视频帧的数据。
  9. 读文件:从输入源读取视频帧数据。如果读取成功,则继续处理;如果读取失败,则跳转到flush_encode步骤。
  10. avcodec_send_frame:将AVFrame发送给编码器进行编码。
  11. avcodec_receive_packet:从编码器接收编码后的数据包。
  12. 写文件:将编码后的数据包写入输出文件。
  13. flush_encode:当读取文件失败时,调用flush_encode函数清理编码器中的剩余数据。
  14. 释放资源:释放之前分配的所有资源,包括AVFrame、AVPacket和编码器上下文。
cpp 复制代码
//准备yuv文件

//引入FFMPEG库

#include<QDebug>
extern "C"{
#include<libavcodec/avcodec.h>
}

const char* inFileName = "D:\\output.yuv";
const char* outFileName = "output.h264";

/**
 * @brief 编码一帧数据并写入输出文件
 *
 * @param codecContext 编码器上下文,包含编码器配置信息
 * @param packet 存储编码后的数据包
 * @param frame 待编码的原始帧数据
 * @param outFile 输出文件指针
 * @return int 成功返回0,失败返回错误码(负数)
 */
int encode(AVCodecContext* codecContext, AVPacket* packet, AVFrame* frame, FILE* outFile) {
    // 1. 发送待编码的帧到编码器
    int ret = avcodec_send_frame(codecContext, frame);
    if (ret < 0) {
        qDebug() << "avcodec_send_frame failed";
        return -8; // 错误码-8:发送帧失败
    }

    // 2. 循环接收编码后的数据包
    while (ret == 0) {
        ret = avcodec_receive_packet(codecContext, packet);

        // 2.1 检查是否需要更多输入或已结束
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            return 0; // 正常情况:需要更多数据或编码完成
        }
        // 2.2 检查其他错误
        else if (ret < 0) {
            qDebug() << "avcodec_receive_packet failed";
            return -9; // 错误码-9:接收数据包失败
        }

        // 3. 成功接收到数据包,写入文件
        if (ret == 0) {
            fwrite(packet->data, 1, packet->size, outFile);
        }
    }

    return 0; // 正常结束
}

int main(){
    int ret = 0;
    //准备编码器
    const AVCodec* codec = nullptr;
    AVCodecContext* codecContext = nullptr;
    AVPacket* packet = nullptr;
    AVFrame* frame = nullptr;
    FILE* inFile = nullptr;
    FILE* outFile = nullptr;

    //查找264编码器
    codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if(!codec){
        qDebug() << "avcodec_find_encoder failed";
        return -1;
    }

    //分配编码器上下文
    codecContext = avcodec_alloc_context3(codec);

    if(!codecContext){
        qDebug() << "avcodec_alloc_context3 failed";
        return -2;
    }


    //设置上下文参数
    codecContext->width = 1280; //设置视频的宽度为 1280 像素
    codecContext->height = 720; //设置视频的高度为 720 像素
    codecContext->time_base = AVRational{1, 25}; //设置时间基(time base)为 1/25 秒,这是时间戳的基本单位,表示每个时间单位代表 1/25 秒
    codecContext->pix_fmt = AV_PIX_FMT_YUV420P; //设置像素格式为 YUV420P
    codecContext->framerate = AVRational{25, 1}; //设置帧率为 25 帧/秒

    //打开编码器
    ret = avcodec_open2(codecContext, codec, NULL);
    if(ret != 0){
        qDebug() << "avcodec_open2 failed";
        return -3;
    }

    packet = av_packet_alloc();
    if(!packet){
        qDebug() << "avcodec_open2 failed";
        return -4;
    }

    frame = av_frame_alloc();
    if(!frame){
        qDebug() << "av_frame_alloc failed";
        return -5;
    }

    //设置帧参数
    frame->width = 1280;
    frame->height = 720;
    frame->format = AV_PIX_FMT_YUV420P;

    // 申请frame的内存来存放帧数据
    ret = av_frame_get_buffer(frame, 0);
    if (ret < 0) {  // 修正:检查返回值是否为负数(失败)
        qDebug() << "av_frame_get_buffer failed, error:" << ret;
        return -6;
    }

    //我们开始打开输入文件读数据
    inFile = fopen(inFileName, "rb");
    if(inFile == nullptr){
        qDebug() << "open infile failed";
        return -7;
    }

    //打开输出文件写数据
    outFile = fopen(outFileName, "wb");
    if(outFile == nullptr){
        qDebug() << "open outFileName failed";
        return -7;
    }

    //循环读数据编码
    while(!feof(inFile)){
        //frame能不能写,我们需要将yuv数据填充到frame,检查是否可写
        ret = av_frame_is_writable(frame);
        if(ret < 0){
            //如果不可写,我们设置可写
            ret = av_frame_make_writable(frame);
        }

        //从yuv文件读取
        //y分量
        fread(frame->data[0], 1, frame->width * frame->height, inFile);
        //u分量
        fread(frame->data[1], 1, frame->width * frame->height / 4, inFile);
        //v分量
        fread(frame->data[2], 1, frame->width * frame->height / 4, inFile);

        //编码
        encode(codecContext, packet, frame, outFile);
    }

    //如果没有数据了,我们退出
    //flush_encode
    encode(codecContext, packet, nullptr, outFile);  //刷新编码器
    qDebug()<<"编码完成";
    //释放资源
    av_packet_free(&packet);
    av_frame_free(&frame);
    avcodec_free_context(&codecContext);
    fclose(inFile);
    fclose(outFile);
    return 0;
}

音频编码实战

一、初始化阶段
  1. avcodec_find_encoder
    查找合适的音频编码器(如 AAC、MP3)。
  2. avcodec_alloc_context3
    分配编码器上下文。
  3. 设置编码器参数
    如采样率、通道数、比特率等。
  4. avcodec_open2
    打开编码器。
  5. avformat_alloc_output_context2
    创建输出格式上下文。
  6. avformat_new_stream
    新建输出音频流。
  7. avio_open
    打开输出文件。
  8. avformat_write_header
    写入文件头信息。
  9. swr_init
    初始化音频重采样(如果需要转换采样格式或通道布局)。
二、编码循环阶段
  1. av_frame_get_buffer
    获取一个音频帧缓冲区用于填充数据。
  2. 读取文件
    判断是否还有音频数据需要编码。
    • 如果有(true):
      • avcodec_send_frame:送入音频帧。
      • avcodec_receive_packet:从编码器接收压缩数据。
      • av_interleaved_write_frame:将编码后数据写入输出文件。
    • 如果没有(false):
      • flush_encode:刷新编码器内部缓存(送入空帧)。
      • av_write_trailer:写入文件尾。
      • 释放资源:关闭文件、释放上下文、帧、包等资源。
cpp 复制代码
#include <QDebug>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
}

int main(){
    char inputfile[] = "D:\\output.pcm";
    char outputfile[] = "audio.aac";

    //准备编码器
    const AVCodec* avCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    if(!avCodec){
        qDebug() << "avcodec_find_encoder failed";
        return -1;
    }

    //准备编码上下文
    AVCodecContext* avCodecContext = avcodec_alloc_context3(avCodec);
    if(!avCodecContext){
        qDebug() << "avcodec_alloc_context3 failed";
        return -1;
    }

    //设置上下文参数
    int ret=0;
    avCodecContext->sample_rate = 44100;  //表示音频的采样率为 44.1kHz(CD 音质标准)。
    avCodecContext->channels = 2; //双声道
    avCodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP; //表示浮点型平面(Planar)采样格式。
    avCodecContext->bit_rate = 64000;  //表示编码后的音频比特率为 64kbps。
    avCodecContext->channel_layout = AV_CH_LAYOUT_STEREO;  //明确指定声道布局为立体声(左右声道)。

    //打开解码器
    ret = avcodec_open2(avCodecContext, avCodec,NULL);
    if(ret<0){
        qDebug() << "avcodec_open2 failed";
        return -1;
    }



    //创建输出上下文
    AVFormatContext* avFormatContext = NULL;
    //初始化输出上下文
    avformat_alloc_output_context2(&avFormatContext, NULL, NULL, outputfile);
    if(!avFormatContext){
        qDebug() << "avformat_alloc_output_context2 failed";
        return -1;
    }


    // 新建输出音频流
    AVStream* st = avformat_new_stream(avFormatContext, NULL);
    st->codecpar->codec_tag = 0;

    //这个流需要获取编码器参数
    avcodec_parameters_from_context(st->codecpar, avCodecContext);

    //打开输出文件
    ret = avio_open(&avFormatContext->pb, outputfile, AVIO_FLAG_WRITE);
    if(ret<0){
        qDebug() << "avio_open failed";
        return -1;
    }

    //写入文件头信息
    avformat_write_header(avFormatContext, NULL);

    SwrContext* swrContext = NULL;
    // 设置参数, 1. 上下文 2.输出声道布局 4.输出采样率, 5.输入声道布局 6.输入样本格式 7.输入采样率 8.配音 9.日志
    swrContext = swr_alloc_set_opts(swrContext, avCodecContext->channel_layout, avCodecContext->sample_fmt, avCodecContext->sample_rate,
                                    AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 44100,
                                    0, 0);
    if(!swrContext){
        qDebug() << "swr_alloc_set_opts failed";
        return -1;
    }

    // 初始化音频重采样
    ret = swr_init(swrContext);
    if(ret<0){
        qDebug() << "swr_init failed";
        return -1;
    }

    //准备frame
    AVFrame* avFrame = av_frame_alloc();
    avFrame->format = AV_SAMPLE_FMT_FLTP;
    avFrame->channels=2;
    avFrame->channel_layout = AV_CH_LAYOUT_STEREO;
    avFrame->nb_samples=1024;

    // 获取一个音频帧缓冲区用于填充数据
    ret = av_frame_get_buffer(avFrame, 0);
    if(ret<0){
        qDebug() << "av_frame_get_buffer failed";
        return -1;
    }


    //开始读pcm->frame
    int readSize = avFrame->nb_samples*2*2; //双声道,float 16位,每次读取的大小
    char* pcms = new char[readSize]; //申请内存
    FILE* fp = fopen(inputfile, "rb");

    //开始编码
    for(;;){
        AVPacket pkt;
        av_init_packet(&pkt);
        int len = fread(pcms,1,readSize, fp);
        if(len <=0){
            break;
        }else{
            //开始重采样
            const uint8_t* data[1];
            data[0] = (uint8_t*)pcms;
            len = swr_convert(swrContext, avFrame->data, avFrame->nb_samples, data, avFrame->nb_samples);
            if(len <=0){
                qDebug() << "swr_convert failed";
                break;
            }

            //重采样成功,我们开始编码数据
            ret = avcodec_send_frame(avCodecContext, avFrame);
            if(ret < 0){
                qDebug() << "avcodec_send_frame failed";
                continue;
            }

            //
            ret = avcodec_receive_packet(avCodecContext, &pkt);
            if(ret == 0){
                av_interleaved_write_frame(avFormatContext, &pkt);
            }
        }
    }
    //写入结尾
    av_write_trailer(avFormatContext);

    //释放资源
    fclose(fp);
    avio_close(avFormatContext->pb);
    avcodec_close(avCodecContext);
    avcodec_free_context(&avCodecContext);
    avformat_free_context(avFormatContext);
    return 0;
}
相关推荐
SEONIB_Explorer2 小时前
跨境电商新手零门槛教程:AI 一键生成 TikTok/Ins 投放短视频,5 步 60 秒出片
人工智能·音视频·跨境电商·视频制作·veonib
AI创界者9 小时前
【AI视频生成】ComfyUI + LTX-Video 2.3 整合包:超低显存实现高帧率AI视频渲染与工作流详解
人工智能·aigc·音视频
开开心心就好11 小时前
Word双击预览图片插件弥补Word功能缺失
人工智能·python·智能手机·ocr·电脑·word·音视频
Yeauty14 小时前
你那条 ffmpeg 命令,一键翻成 Rust builder 代码
开发语言·rust·ffmpeg
tedcloud1231 天前
OmniRoute怎么部署?开源AI模型路由平台Linux部署教程
linux·服务器·人工智能·开源·音视频
潜创微科技1 天前
8K HDMI 2.1 多源切换芯片方案,一颗芯片搞定长线不黑屏
音视频
音视频牛哥1 天前
一款优秀的视频直播软件应具备哪些核心功能?结合 SmartMediaKit 谈实时音视频系统的能力边界
音视频·视频直播·低延迟rtsp播放器·gb28181接入·rtsp服务器·低延迟rtmp播放器·鸿蒙next rtsp
AI小码1 天前
把动作「画」给视频世界模型,跨本体双向推演,李飞飞参与
大数据·人工智能·算法·ai·大模型·音视频·编程
嵌入式-老费1 天前
esp32开发与应用(边采样边播放音频)
音视频
中微极客1 天前
Veo视频生成与Gemini Agent平台集成实践
数据库·人工智能·oracle·音视频