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

FFmepg--20-合成H.264视频和AAC音频和时间基转化

文章目录

      • 程序概述
      • 核心功能模块
        • 流创建与编码器设置
        • 视频数据生成
        • 音频数据生成
      • 编码与混合流程
        • 时间戳与同步管理
      • 关键数据结构
      • 文件输出流程
      • 时间戳转换
        • 时间基(Time Base)
        • 完整的时间戳流转过程
        • 关键时间戳转换过程
        • 时间戳重缩放(Rescaling)
        • 流时间基计算
        • 容器格式与时间基对应表
      • 详细流程解析
        • 程序初始化与参数解析
        • 创建输出格式上下文
        • 添加音视频流
        • 打开编码器与资源分配
        • 文件写入准备
        • 主编码循环
        • 关键数据结构关系
      • 完整代码

程序概述

这是一个完整的音视频媒体文件生成程序,使用FFmpeg库合成H.264视频和AAC音频,并将它们混合封装到指定的容器格式中。

核心功能模块

流创建与编码器设置

// 视频流配置:

  • 编码器:H.264
  • 分辨率:352×288
  • 帧率:25fps
  • 像素格式:YUV420P
  • 码率:400kbps

// 音频流配置:

  • 编码器:AAC
  • 采样率:44100Hz
  • 声道:立体声
  • 采样格式:FLTP
  • 码率:64kbps
视频数据生成

// 合成YUV图像数据

  • 使用算法生成动态的YUV测试图像
  • Y分量:x + y + 帧序号×3 的渐变
  • U/V分量:基于位置和帧序号的渐变
  • 支持格式转换(如需要转换为非YUV420P格式)
音频数据生成

// 合成PCM音频数据

  • 生成110Hz的正弦波信号
  • 频率随时间线性增加(每秒增加110Hz)
  • 采样格式:16位有符号整数(S16)
  • 通过重采样转换为编码器所需的格式(FLTP)

编码与混合流程

// 主要处理循环:  
while (encode_video || encode_audio) {  if (应该编码视频帧) {  // 1. 生成YUV视频帧  // 2. 格式转换(如果需要)  // 3. H.264编码  // 4. 时间戳转换  // 5. 写入容器  } else {  // 1. 生成PCM音频帧    // 2. 重采样到目标格式  // 3. AAC编码  // 4. 时间戳转换  // 5. 写入容器  }  
}  
时间戳与同步管理

// 音视频同步策略:

  • 视频PTS:基于帧序号,每帧递增1
  • 音频PTS:基于采样点数,每帧递增nb_samples
  • 同步决策:使用av_compare_ts比较下一帧的显示时间
  • 确保时间戳较小的媒体先被处理写入

关键数据结构

OutputStream 结构体

typedef struct OutputStream {AVStream *st;           // 媒体流AVCodecContext *enc;    // 编码器上下文// 时间戳相关int64_t next_pts;       // 下一帧PTSint samples_count;      // 音频采样计数// 帧管理AVFrame *frame;         // 编码用帧(处理后)AVFrame *tmp_frame;     // 原始数据帧// 音频生成参数float t, tincr, tincr2; // 正弦波生成参数// 处理上下文struct SwsContext *sws_ctx; // 图像缩放/转换struct SwrContext *swr_ctx; // 音频重采样
} OutputStream;

文件输出流程

// 格式探测与上下文创建
avformat_alloc_output_context2(&oc, NULL, NULL, filename);
// 如果不能推测格式,默认使用FLV格式
// 流创建与参数设置
add_stream() → 创建流并初始化编码器参数
// 编码器打开与资源分配
open_video() / open_audio()- 打开编码器
- 分配帧缓冲区
- 初始化格式转换器
// 文件写入准备
avio_open()    // 打开输出文件
avformat_write_header()  // 写入文件头
// 数据生成与编码循环
// 持续生成并编码数据,直到达到5秒时长
// 资源清理
av_write_trailer()  // 写入文件尾
close_stream()      // 关闭各流
avio_closep()       // 关闭文件
avformat_free_context() // 释放上下文

时间戳转换

时间基(Time Base)

时间基是FFmpeg中表示时间的基本单位,定义为分数形式 {分子, 分母}:

音频时间基: {1, 采样率} (如 {1, 44100})
视频时间基: {1, 帧率} (如 {1, 25})

流时间基(Stream Time Base):
流时间基是容器格式(如MP4、FLV、AVI等)内部使用的时间基准单位,用于:统一管理不同媒体流(音频、视频)的时间戳;确保播放器能够正确解析和同步各媒体流

完整的时间戳流转过程

音频时间戳流转

信号生成 → PCM帧 → 重采样 → 编码 → 混合写入↓         ↓        ↓       ↓        ↓采样计数   采样PTS   转换PTS  包PTS   流PTS(0,1024..) (0,1024) (重计算) (保持)  (重缩放)

视频时间戳流转

图像生成 → YUV帧 → 格式转换 → 编码 → 混合写入↓        ↓         ↓       ↓        ↓帧计数    帧PTS     保持PTS  包PTS   流PTS(0,1,2..) (0,1,2)   (保持)  (保持)  (重缩放)
关键时间戳转换过程

音频时间戳处理

// 音频PTS计算
frame->pts = ost->next_pts;                    // 使用采样计数作为PTS
ost->next_pts += frame->nb_samples;           // 累计采样数// 重采样后时间戳转换
frame->pts = av_rescale_q(ost->samples_count, (AVRational){1, codec_ctx->sample_rate},codec_ctx->time_base);

音频PTS计算流程:

  • 初始PTS = 0
  • 每帧增加 nb_samples 个采样点
  • 实际时间 = PTS × 时间基 = PTS × (1/44100) 秒

视频时间戳处理

// 视频PTS计算
ost->frame->pts = ost->next_pts++;  // 每帧PTS递增1// 视频时间基设置
ost->st->time_base = (AVRational){1, STREAM_FRAME_RATE};  // {1, 25}
codec_ctx->time_base = ost->st->time_base;                // 编码器使用相同时间基

视频PTS计算流程:

  • 初始PTS = 0
  • 每帧PTS递增1
  • 实际时间 = PTS × 时间基 = PTS × (1/25) 秒
时间戳重缩放(Rescaling)
// 写入前的关键时间戳转换
av_packet_rescale_ts(pkt, *time_base, st->time_base);

转换公式:

新的时间戳 = 原时间戳 × (原时间基 / 新时间基)

具体示例

  • 音频转换:

    • 编码器时间基:{1, 44100}
    • 流时间基:{1, 1000} (常见值)
    • PTS转换:新PTS = 原PTS × 1000/44100
  • 视频转换:

    • 编码器时间基:{1, 25}
    • 流时间基:{1, 1000}
    • PTS转换:新PTS = 原PTS × 40
static int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base,AVStream *st, AVPacket *pkt)
{// 关键转换:从编码器时间基到流时间基av_packet_rescale_ts(pkt, *time_base, st->time_base);pkt->stream_index = st->index;return av_interleaved_write_frame(fmt_ctx, pkt);
}

转换时机:
1 编码完成后:获得编码器时间基的packet
2 写入容器前:转换为流时间基
3 写入文件:使用统一的流时间基存储

时间戳同步机制 :

// 音视频帧写入顺序决策
if (encode_video && (!encode_audio || av_compare_ts(video_st.next_pts, video_st.enc->time_base,audio_st.next_pts, audio_st.enc->time_base) <= 0)) {// 先写入视频帧
} else {// 先写入音频帧
}

av_compare_ts 比较两个时间戳,确保:

  • 时间戳较小的帧先写入
  • 维持音视频同步
流时间基计算

音频转换案例

// 编码器时间基: {1, 44100}
// 流时间基: {1, 1000} 
// 原始PTS: 1024 (表示第1024个采样点)新PTS = 1024 × (1/44100) ÷ (1/1000) = 1024 × (1000/44100) = 1024 × 0.022675723
意义:将"采样点计数"转换为"毫秒时间"

视频转换案例

// 编码器时间基: {1, 25}  
// 流时间基: {1, 1000}
// 原始PTS: 2 (表示第2帧)新PTS = 2 × (1/25) ÷ (1/1000)= 2 × 40 = 80
意义:将"帧序号"转换为"毫秒时间"
容器格式与时间基对应表
容器格式 常用时间基 原因
MP4 {1, 90000} MPEG标准,高精度
FLV {1, 1000} 基于毫秒,简单
AVI {1, 帧率} 保持与视频帧率一致
TS {1, 90000} MPEG传输流标准

详细流程解析

程序初始化与参数解析

代码段:

int main(int argc, char **argv)
{// 参数检查if (argc < 2) {printf("usage: %s output_file\n", argv[0]);return 1;}filename = argv[1];// 解析可选参数for (i = 2; i+1 < argc; i+=2) {if (!strcmp(argv[i], "-flags") || !strcmp(argv[i], "-fflags"))av_dict_set(&opt, argv[i]+1, argv[i+1], 0);}
}

功能说明:

检查命令行参数,确保指定了输出文件名
解析可选的FFmpeg标志参数
初始化全局变量和数据结构

创建输出格式上下文

代码段:

/* 分配AVFormatContext并根据filename绑定合适的AVOutputFormat */
avformat_alloc_output_context2(&oc, NULL, NULL, filename);
if (!oc) {// 如果不能根据文件后缀名找到合适的格式,缺省使用flv格式avformat_alloc_output_context2(&oc, NULL, "flv", filename);
}
fmt = oc->oformat;

功能说明:

根据文件扩展名自动推测容器格式(如.mp4、.avi等)
如果无法推测,默认使用FLV格式
获取输出格式对象用于后续配置

添加音视频流

代码段:

// 指定编码格式
fmt->video_codec = AV_CODEC_ID_H264;
fmt->audio_codec = AV_CODEC_ID_AAC;// 添加视频流
if (fmt->video_codec != AV_CODEC_ID_NONE) {add_stream(&video_st, oc, &video_codec, fmt->video_codec);have_video = 1;encode_video = 1;
}// 添加音频流
if (fmt->audio_codec != AV_CODEC_ID_NONE) {add_stream(&audio_st, oc, &audio_codec, fmt->audio_codec);have_audio = 1;encode_audio = 1;
}

add_stream函数详解:

static void add_stream(OutputStream *ost, AVFormatContext *oc,AVCodec **codec, enum AVCodecID codec_id)
{// 查找编码器*codec = avcodec_find_encoder(codec_id);// 创建新流ost->st = avformat_new_stream(oc, NULL);ost->st->id = oc->nb_streams - 1;// 分配编码器上下文codec_ctx = avcodec_alloc_context3(*codec);ost->enc = codec_ctx;// 配置编码器参数switch ((*codec)->type) {case AVMEDIA_TYPE_AUDIO:// 音频参数配置:采样率、声道、格式等codec_ctx->sample_fmt = (*codec)->sample_fmts[0];codec_ctx->bit_rate = 64000;codec_ctx->sample_rate = 44100;codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO;ost->st->time_base = (AVRational){ 1, codec_ctx->sample_rate };break;case AVMEDIA_TYPE_VIDEO:// 视频参数配置:分辨率、帧率、格式等codec_ctx->bit_rate = 400000;codec_ctx->width = 352;codec_ctx->height = 288;ost->st->time_base = (AVRational){ 1, STREAM_FRAME_RATE };codec_ctx->time_base = ost->st->time_base;codec_ctx->pix_fmt = STREAM_PIX_FMT;break;}
}
打开编码器与资源分配

视频编码器打开:

static void open_video(AVFormatContext *oc, AVCodec *codec, OutputS
http://www.dtcms.com/a/592955.html

相关文章:

  • 深入理解MQTT内核和实现实时通信实战:物联网消息推送的秘密武器
  • 乐云seo模板网站建设主流网站开发技术
  • 第二届数证杯物联网取证+网络流量取证
  • 宁夏住房和城乡建设厅网站首页公司主页和公司网站
  • 如何在 macOS 上安装和配置 Redis ?
  • 【Linux网络】Socket编程实战,基于UDP协议的Dict Server
  • web京东商城前端项目4页面
  • 15、Linux 打包压缩命令
  • 网站后台源代码更改营销广告策划方案
  • 数据库迁移革命:金仓KReplay如何用真实负载回放技术缩短3周测试周期
  • 网站开发搭建合同范本企业软件解决方案
  • Java 中 Arrays.sort() 的底层实现
  • MPAndroidChart 双柱分组图:解决 X 轴标签无法居中问题及 UI 宽度计算指南
  • 政务外网终端一机两用安全管控解决方案
  • 数字华容道游戏
  • M4-R1 开源鸿蒙(OpenHarmory)开发板丨串口调试助手实战案例
  • 建材做网站好吗破解插件有后门wordpress
  • 旅游网站建设流程步骤怎么自己做礼品网站
  • C语言--文件读写函数的使用,对文件读写知识有了更深的了解。C语言--文件读写函数的使用,对文件读写知识有了更深的了解。
  • 数据结构示例代码
  • 数字化工厂:基于层级模型的智能制造新范式
  • C语言--变量(全局变量、局部变量、初始化)
  • 羊驼送洗后因毛发未吹干致失温死亡,物联网技术助力防范宠物洗澡失温事故
  • Raylib 基本绘图操作
  • (Arxiv-2025)BINDWEAVE:通过跨模态整合实现主体一致性的视频生成
  • 怎么做会员积分网站建网站商城有哪些公司
  • 网站如何验证登陆状态广州专业做网页的公司
  • MySQL的增删改查功能合集
  • Oracle数据块编辑工具( Oracle Block Editor Tool)-obet
  • 什么是量子纠缠?大白话