当前位置: 首页 > article >正文

FFmpeg开发学习:音视频封装

1.基本流程

注意:在新版本的ffmpeg中,已经不需要使用av_register_all注册api

1.输入参数

输出文件路径 char *output
视频编码参数 AVCodecParameters *video_par
音频编码参数 AVCodecParameters *audio_par
数据包 AVPacket *packets[]

2.封装流程

(1)创建输出的上下文AVFormatContext指针

AVFormatContext *out_fmt_ctx = nullptr;
    AVStream *video_stream = nullptr;
    AVStream *audio_stream = nullptr;

    //创建输出的上下文以及添加视频流和音频流
    if(avformat_alloc_output_context2(&out_fmt_ctx, nullptr, nullptr, output_filename) < 0) {
        fprintf(stderr, "falied to create output context\n");
        return;
    }

(2)添加AVStream的视频流和音频流

if(video_par) {
        video_stream = avformat_new_stream(out_fmt_ctx, nullptr);
        avcodec_parameters_copy(video_stream->codecpar, video_par);
        video_stream->time_base = (AVRational){1, 1000};
    }

    if(audio_par) {
        audio_stream = avformat_new_stream(out_fmt_ctx, nullptr);
        avcodec_parameters_copy(audio_stream->codecpar, audio_par);
    }

(3)打开输出的目标文件

//打开输出的文件
    if(!(out_fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
        if(avio_open(&out_fmt_ctx->pb, output_filename, AVIO_FLAG_WRITE) < 0) {
            fprintf(stderr, "can not open output file.\n" );
            return;
        }
    }

(4)写入文件头

//写入头文件
    if(avformat_write_header(out_fmt_ctx, nullptr) < 0) {
        fprintf(stderr, "error occurred when writing header.\n");
    }

(5)将所有的AVPacket写入AVStream

//写入所有packet
    for(int i=0; i<packet_count; i++) {
        AVPacket *pkt = packets[i];

        //根据类型设置正确的time_base和stream_index
        AVStream *out_stream = (pkt->stream_index==0) ? video_stream : audio_stream;
        AVRational in_tb = (pkt->stream_index==0) ? (AVRational){1, 1000} : (AVRational){1, 48000};

        //转换时间戳
        av_packet_rescale_ts(pkt, in_tb, out_stream->time_base);
        pkt->stream_index = out_stream->index;

        if(av_interleaved_write_frame(out_fmt_ctx, pkt) < 0) {
            fprintf(stderr, "failed to write packet.\n");
        }

        av_packet_unref(pkt);
    }

(5.1)设置正确的time_base和stream_index

//根据类型设置正确的time_base和stream_index
        AVStream *out_stream = (pkt->stream_index==0) ? video_stream : audio_stream;
        AVRational in_tb = (pkt->stream_index==0) ? (AVRational){1, 1000} : (AVRational){1, 48000};

(6)转换时间戳

//转换时间戳
        av_packet_rescale_ts(pkt, in_tb, out_stream->time_base);
        pkt->stream_index = out_stream->index;

(7)写入文件尾

    av_write_trailer(out_fmt_ctx);

(8)清理指针

if(!(out_fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
        avio_closep(&out_fmt_ctx->pb);
    }
    avformat_free_context(out_fmt_ctx);

基本上可以理解为,对于音视频封装的输入数据,需要视频编码参数以及音频编码参数两个参数,以及相关的数据帧
使用ffmpeg进行封装时,需要利用api写入相关格式的文件头以及文件尾,然后按顺序处理数据帧。

2.程序示例

模拟256个数据帧的音视频数据,并封装为mp4文件

extern "C"{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libavutil/avutil.h>
#include <libavutil/mem.h>
}

#include <cstdio>
#include <cstdlib>
#include <cstring>


inline void mux_audio_video(const char *output_filename,
    AVCodecParameters *video_par,
    AVCodecParameters *audio_par,
    AVPacket *packets[], int packet_count) {

    AVFormatContext *out_fmt_ctx = nullptr;
    AVStream *video_stream = nullptr;
    AVStream *audio_stream = nullptr;

    //创建输出的上下文以及添加视频流和音频流
    if(avformat_alloc_output_context2(&out_fmt_ctx, nullptr, nullptr, output_filename) < 0) {
        fprintf(stderr, "falied to create output context\n");
        return;
    }

    if(video_par) {
        video_stream = avformat_new_stream(out_fmt_ctx, nullptr);
        avcodec_parameters_copy(video_stream->codecpar, video_par);
        video_stream->time_base = (AVRational){1, 1000};
    }

    if(audio_par) {
        audio_stream = avformat_new_stream(out_fmt_ctx, nullptr);
        avcodec_parameters_copy(audio_stream->codecpar, audio_par);
    }

    //打开输出的文件
    if(!(out_fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
        if(avio_open(&out_fmt_ctx->pb, output_filename, AVIO_FLAG_WRITE) < 0) {
            fprintf(stderr, "can not open output file.\n" );
            return;
        }
    }

    //写入头文件
    if(avformat_write_header(out_fmt_ctx, nullptr) < 0) {
        fprintf(stderr, "error occurred when writing header.\n");
    }

    //写入所有packet
    for(int i=0; i<packet_count; i++) {
        AVPacket *pkt = packets[i];

        //根据类型设置正确的time_base和stream_index
        AVStream *out_stream = (pkt->stream_index==0) ? video_stream : audio_stream;
        AVRational in_tb = (pkt->stream_index==0) ? (AVRational){1, 1000} : (AVRational){1, 48000};

        //转换时间戳
        av_packet_rescale_ts(pkt, in_tb, out_stream->time_base);
        pkt->stream_index = out_stream->index;

        if(av_interleaved_write_frame(out_fmt_ctx, pkt) < 0) {
            fprintf(stderr, "failed to write packet.\n");
        }

        av_packet_unref(pkt);
    }

    //写入尾部信息
    av_write_trailer(out_fmt_ctx);

    //清理
    if(!(out_fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
        avio_closep(&out_fmt_ctx->pb);
    }
    avformat_free_context(out_fmt_ctx);
    return;


}
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libavutil/avutil.h>
#include <libavutil/mem.h>
}

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include "function/mux_demux_case.h"
#define FAKE_PACKET_COUNT 10

#include "function/libavformat_case.h"


int main() {
    av_log_set_level(AV_LOG_INFO);

    const char *filename = "D:/Cpp/FFmpegUsingCase/test_output.mp4";

    // 初始化视频参数
    AVCodecParameters *vpar = avcodec_parameters_alloc();
    vpar->codec_type = AVMEDIA_TYPE_VIDEO;
    vpar->codec_id = AV_CODEC_ID_H264;
    vpar->width = 1280;
    vpar->height = 720;
    vpar->format = AV_PIX_FMT_YUV420P;
    vpar->bit_rate = 400000;

    // 初始化音频参数
    AVCodecParameters *apar = avcodec_parameters_alloc();
    apar->codec_type = AVMEDIA_TYPE_AUDIO;
    apar->codec_id = AV_CODEC_ID_AAC;
    apar->sample_rate = 48000;
    apar->ch_layout.nb_channels = 2;
    apar->frame_size = 1024;
    // apar->channel_layout = AV_CH_LAYOUT_STEREO;
    apar->ch_layout.order = AV_CHANNEL_ORDER_NATIVE;
    apar->ch_layout.u.mask = AV_CH_LAYOUT_STEREO;
    apar->format = AV_SAMPLE_FMT_FLTP;
    apar->bit_rate = 128000;

    // 模拟一些 AVPacket
    AVPacket *packet_array[FAKE_PACKET_COUNT];
    for (int i = 0; i < FAKE_PACKET_COUNT; i++) {
        AVPacket *pkt = av_packet_alloc();
        pkt->data = (uint8_t *)av_malloc(100); // 模拟数据内容
        pkt->size = 100;
        pkt->pts = i * 40;
        pkt->dts = i * 40;
        pkt->duration = 40;
        pkt->stream_index = i % 2; // 偶数为视频帧,奇数为音频帧
        if (i % 5 == 0)
            pkt->flags |= AV_PKT_FLAG_KEY;
        packet_array[i] = pkt;
    }

    // 封装并输出到文件
    mux_audio_video(filename, vpar, apar, packet_array, FAKE_PACKET_COUNT);

    printf("finished: %s\n", filename);

    // 清理参数结构体
    avcodec_parameters_free(&vpar);
    avcodec_parameters_free(&apar);

    printAVFormatContextCase(filename);

    return 0;
}

http://www.kler.cn/a/611845.html

相关文章:

  • 分割 / 合并大文件的简单 python 代码
  • 【Mysql】SQL 优化全解析
  • 第十二篇《火攻篇》:一把火背后的战争哲学与生存智慧
  • 前端工程化开篇
  • 后端学习day1-Spring(八股)--还剩9个没看
  • C语言文件操作简介:从文件打开到文件读写
  • Ae 效果详解:3D 点控制
  • AWS CloudWatch 实战:构建智能监控与自动化运维体系
  • 华为OD机试2025A卷 - 流浪地球(Java Python JS C++ C )
  • MOSN(Modular Open Smart Network)是一款主要使用 Go 语言开发的云原生网络代理平台
  • Appium中元素定位之一组元素定位API
  • 蓝桥杯备考:真题之飞机降落(暴搜+小贪心)
  • Flutter 完整开发指南
  • 系统调用 与 中断
  • Transformer | 一文了解:缩放、批量、多头、掩码、交叉注意力机制(Attention)
  • DMA 之FIFO的作用
  • .NET开源的智能体相关项目推荐
  • c#的反射和特性
  • Docker实现MySQL主从复制配置【简易版】
  • 旅游纵览杂志旅游纵览杂志社旅游纵览编辑部2025年第2期目录