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

FFmpeg+QT输出音频

音频输出

使用 SDL作为音视频输出。

画板

audio_decode_frame

struct AudioParams audio_src; // 保存最新解码的音频参数
struct AudioParams audio_tgt; // 保存SDL音频输出需要的参数

重置音频重采样器的条件:

  • 采用格式不一致
  • 采样率不一致
  • 通道布局不一致

画板

因为使用 SDL 做输出最有可能的是采样格式区别,我们需要把 float planner重采样成 s16 packed格式。

buffer 管理

  • audio_buf1:实际 处理分配的 buffer
  • audio_buf1_size:buffer 的最大长度
  • audio_buf_size:有效的 PCM buffer
  • audio_buf:执行要拷贝的 buffer

画板

具体的 buffer 使用流程举例如下:

  1. 第一次触发回调的时候 audio_buf_index = 0,本次需要拷贝 4096 个字节,然后拷贝结束后 audio_buf_index = 4096
  2. 第二次触发回调的时候 audio_buf_index = 4096

audio_decode_frame

    // is->audio_tgt是SDL可接受的音频帧数,是audio_open()中取得的参数// 在audio_open()函数中又有"is->audio_src = is->audio_tgt""// 此处表示:如果frame中的音频参数 == is->audio_src == is->audio_tgt,// 那音频重采样的过程就免了(因此时is->swr_ctr是NULL)// 否则使用frame(源)和is->audio_tgt(目标)中的音频参数来设置is->swr_ctx,// 并使用frame中的音频参数来赋值is->audio_srcif (af->frame->format           != is->audio_src.fmt            || // 采样格式dec_channel_layout      != is->audio_src.channel_layout || // 通道布局af->frame->sample_rate  != is->audio_src.freq          // 采样率) {swr_free(&is->swr_ctx);is->swr_ctx = swr_alloc_set_opts(NULL,is->audio_tgt.channel_layout,  // 目标输出is->audio_tgt.fmt,is->audio_tgt.freq,dec_channel_layout,            // 数据源(enum AVSampleFormat)af->frame->format,af->frame->sample_rate,0, NULL);if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) {av_log(NULL, AV_LOG_ERROR,"Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n",af->frame->sample_rate, av_get_sample_fmt_name((enum AVSampleFormat)af->frame->format), af->frame->channels,is->audio_tgt.freq, av_get_sample_fmt_name(is->audio_tgt.fmt), is->audio_tgt.channels);swr_free(&is->swr_ctx);ret = -1;goto fail;}// 保存最新的audio解码后的参数is->audio_src.channel_layout = dec_channel_layout;is->audio_src.channels       = af->frame->channels;is->audio_src.freq = af->frame->sample_rate;is->audio_src.fmt = (enum AVSampleFormat)af->frame->format;}

第一次判断 is->audio_src = is->audio_tgt

第二次判断 解码后的参数==is->audio_src==is->audio_tgt

因为解码前后的参数可能会改变

// 重采样输出参数1:输出音频缓冲区
uint8_t **out = &is->audio_buf1; //真正分配缓存audio_buf1,指向是用audio_buf// 重采样输出参数2:输出音频缓冲区尺寸, 高采样率往低采样率转换时得到更少的样本数量,比如 96k->48k, wanted_nb_samples=1024
// 则wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate 为1024*48000/96000 = 512
// +256 的目的是重采样内部是有一定的缓存,就存在上一次的重采样还缓存数据和这一次重采样一起输出的情况,所以目的是多分配输出buffer
int out_count = (int64_t)wanted_nb_samples * is->audio_tgt.freq / af->frame->sample_rate+ 256;
// 计算对应的样本数 对应的采样格式 以及通道数,需要多少buffer空间
// 计算输出的buffer大小
int out_size  = av_samples_get_buffer_size(NULL, is->audio_tgt.channels,out_count, is->audio_tgt.fmt, 0);
int len2;
if (out_size < 0) {av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed\n");ret = -1;goto fail;
}
// if(audio_buf1_size < out_size) {重新分配out_size大小的缓存给audio_buf1, 并将audio_buf1_size设置为out_size }
av_fast_malloc(&is->audio_buf1, &is->audio_buf1_size, out_size);
if (!is->audio_buf1) {ret = AVERROR(ENOMEM);goto fail;
}
// 音频重采样:len2返回值是重采样后得到的音频数据中单个声道的样本数
// len2就是实际输出的样本数
len2 = swr_convert(is->swr_ctx, out, out_count, in, af->frame->nb_samples);
if (len2 < 0) {av_log(NULL, AV_LOG_ERROR, "swr_convert() failed\n");ret = -1;goto fail;
}if (len2 == out_count) { // 这里的意思是我已经多分配了buffer,实际输出的样本数不应该超过我多分配的数量av_log(NULL, AV_LOG_WARNING, "audio buffer is probably too small\n");if (swr_init(is->swr_ctx) < 0)swr_free(&is->swr_ctx);
}
// 重采样返回的一帧音频数据大小(以字节为单位)
is->audio_buf = is->audio_buf1;
resampled_data_size = len2 * is->audio_tgt.channels * av_get_bytes_per_sample(is->audio_tgt.fmt);
if (is->audio_buf_index >= is->audio_buf_size) {audio_size = audio_decode_frame(is); // 返回有效的PCM数据长度if (audio_size < 0) {// 静音的逻辑/* if error, just output silence */is->audio_buf = NULL;is->audio_buf_size = SDL_AUDIO_MIN_BUFFER_SIZE / is->audio_tgt.frame_size* is->audio_tgt.frame_size;} else {is->audio_buf_size = audio_size; // 讲字节 多少字节}is->audio_buf_index = 0;    // 重置为0

参考资料:https://github.com/0voice

http://www.dtcms.com/a/506641.html

相关文章:

  • 友点企业网站管理系统微信商城在哪里找
  • 深度学习(5)-PyTorch 张量详细介绍
  • 西安市建设厅网站软文营销的经典案例
  • Agent 开发设计模式(Agentic Design Patterns )第8章: 智能体记忆管理(Memory Management)
  • Linux 下使用 Docker-Compose 安装 Kafka 和 Kafka-UI(KRaft 模式)
  • 【C++入门篇 - 10】:模板
  • [Linux]学习笔记系列 -- [kernel][lock]mutex
  • 开源 Linux 服务器与中间件(七)数据库--MySQL
  • 在 JavaScript 中处理 `0.1 + 0.2` 这类精度问题
  • 今天我们学习python编程常用模块与面向对象
  • 网站的三大标签宁波专业seo服务
  • Day6C语言前期阶段练习之汉诺塔问题
  • Apache Spark 集群部署与使用指南
  • 基于3D LiDAR的作物排检,用于在不同种植密度下成熟时的大豆
  • Python使用Medical Information Dataset实战2025.07版(上)
  • 基因组学中的深度学习!
  • 基于容器适配器模式的 Stack 与 Queue 实现:复用底层容器的优雅设计
  • Kafka面试精讲 Day 26:Kafka集群部署与配置
  • 73_基于深度学习的水面漂浮垃圾检测系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
  • 在JavaScript中,清除 Canvas 画布上的内容
  • 方便做简笔画的网站或软件公司人员管理系统
  • SQL之参数类型讲解
  • CTFSHOW—WEB5
  • UU远程深度测评:重构远程控制体验的“无套路”标杆
  • 提升 iOS 26 系统流畅度的实战指南,多工具组合监控
  • vue3:vue3 + elementplus + pinia实现js的XMLHttpRequest 下载功能。
  • 如何在macOS上免密登录阿里云ECS服务器
  • 把“天猫”装进 JVM:Java 关键词商品爬虫从 0 到 1(含完整可运行代码)
  • tar打包过滤指定目录指南
  • 塘厦镇住房规划建设局网站wordpress主题生成