FFmpeg+QT输出音频

音频输出

使用 SDL作为音视频输出。

audio_decode_frame

cpp 复制代码
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

cpp 复制代码
    // 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_src
    if (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

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

cpp 复制代码
// 重采样输出参数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);
cpp 复制代码
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

相关推荐
「QT(C++)开发工程师」4 小时前
C++语言编程规范-风格
linux·开发语言·c++·qt
lihongli0005 小时前
ROS与Qt结合开发CAN控制界面(发布自定义的truck_send_can1消息)
开发语言·qt·ros
xiaopengbc5 小时前
免费的视频音频文档文件选装百种格式转换软件(附下载)
音视频
Shang180989357265 小时前
MS2107高性能USB 2.0视频信号和音频采集,支持NTSC/PAL制式,适用于低成本视频采集设备
嵌入式硬件·fpga开发·音视频·硬件工程·信息与通信·dsp开发
老贾专利烩6 小时前
音频共享耳机专利拆解:碰击惯性数据监测与阈值减速识别机制研究
音视频·信息与通信·科技前沿·创新专利
web前端进阶者7 小时前
音视频开发远端未发布视频占位图
音视频·webrtc
syso_稻草人7 小时前
基于 ComfyUI + Wan2.2 animate实现 AI 视频人物换衣:完整工作流解析与资源整合(附一键包)
人工智能·音视频
无敌最俊朗@7 小时前
解决 QML 中使用 Qt Charts 崩溃的三个关键步骤
开发语言·qt
有时间要学习8 小时前
Qt——控件
qt