C语言使用ffmpeg和sdl实现多路音频播放

 更新时间:2023年06月07日 08:39:24   作者:CodeOfCC  
这篇文章主要为大家详细介绍了一种基于ffmpeg和sdl实现的音频多路混合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下

前言

音频的播放通常是单例的,在进程内通常一个播放设备只能打开一次,且只支持一个写入。写播放器的时候,会遇到需要实现多开同时播放多个视频,比如视频剪辑工具的多轨道播放。这个时候就需要采用一定的方法,比如音频混合。本文将提供一种基于ffmpeg和sdl实现的音频多路混合的方法。

一、 如何实现

1、回调式打开设备

为实现多路共用一个播放设备,只能采用SDL的回调式打开音频设备。

回调方法

static std::map<void*, std::function<void(void* userdata, Uint8* stream, int len)>> _audioCallbacks;//记录每一路音频的回调方法
//总的回调方法
static void _audio_callback_sum(void* userdata, uint8_t* stream, int len)
{
    memset(stream, 0, len);
    std::unique_lock< std::mutex >lck(_mtx);
    //调用每一路的回调方法
    for (auto i : _audioCallbacks)
    {
        i.second(userdata, stream, len);
    }
}

打开音频

static SDL_AudioSpec _wanted_spec, _spec;
_wanted_spec.channels = 2;
_wanted_spec.freq = 44100;
_wanted_spec.format = AUDIO_F32SYS;
_wanted_spec.silence = 0;
_wanted_spec.samples = 1024;
_wanted_spec.callback = _audio_callback_sum;
 SDL_OpenAudio(&_wanted_spec, &_spec) == 0;

2、注册多路回调

注册一路音频回调示例如下

_audioCallbacks[pFrame] = [&](void* userdata, Uint8* stream, int len) {
    //此处实现单独一路的播放逻辑
};

3、写入队列

每一路音频都创建一个音频队列,将解码的音频数据写入队列,下面是使用AVAudioFifo作为音频队列的例子。

if (av_audio_fifo_space(fifo) >= samples)
{
    av_audio_fifo_write(fifo, (void**)&data, (int)samples);
    break;
}

4、混合音频

在音频单独的回调中即上面_audioCallbacks注册的方法中,进行数据混合,示例如下。

//读取音频队列数据 
int read = av_audio_fifo_read(fifo, (void**)&data, _spec.samples);
//开始混合
SDL_MixAudioFormat(stream, data, _spec.format, newLen, SDL_MIX_MAXVOLUME);

二、完整代码

1、头文件

simple_audio_play.h

#pragma once
/// <summary>
/// 播放音频
/// 此方法同步阻塞,播放完成后返回
/// 支持多路播放,可放在线程中播放,线程安全。
/// </summary>
/// <param name="input">输入url</param>
/// <param name="exitFlag">中断标记</param>
/// <returns></returns>
int simple_audio_play(const char* input, int* exitFlag)

三、使用示例

1、播放音频

int exitflag= false;    
simple_audio_play("D:\\test_music.wav", &exitflag);

2、多路播放音频

#include"simple_audio_play.h"
void main() {
    int exitflag = false;
    auto t1 = new std::thread([&]() {
        while (!exitflag) {
            simple_audio_play("D:\\test_music.wav", &exitflag);
        }
        });
    auto t2 = new std::thread([&]() {
        while (!exitflag) {
            simple_audio_play("D:\\test.mp4", &exitflag);
        }
        });
    system("pause");
    exitflag = true;
    t1->join();
    t2->join();
}

总结

以上就是今天要的内容了,采用ffmpeg结合sdl实现音频多路播放还是比较容易的,都有相关的接口去调用,只要设计实现整体的上层逻辑即可。但是一开始基本没有头绪,通过一番尝试后找到关键方法SDL_MixAudioFormat后,实现起来还是很简单的,这个是实践才能得到的知识。

到此这篇关于C语言使用ffmpeg和sdl实现多路音频播放的文章就介绍到这了,更多相关C语言多路音频播放内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++实现LeetCode(58.求末尾单词的长度)

    C++实现LeetCode(58.求末尾单词的长度)

    这篇文章主要介绍了C++实现LeetCode(58.求末尾单词的长度),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • 详解C++中赋值和输入输出语句的用法

    详解C++中赋值和输入输出语句的用法

    这篇文章主要介绍了详解C++中赋值和输入输出语句的用法,是C++入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • c语言全局变量和局部变量问题及解决汇总

    c语言全局变量和局部变量问题及解决汇总

    局部变量能否和全局变量重名,如何引用一个已经定义过的全局变量,全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?,接下来为您一一介绍
    2013-01-01
  • C语言学习之条件和 if...else语句详解

    C语言学习之条件和 if...else语句详解

    这篇文章主要给大家介绍了C语言中的条件和 if...else语句,文中通过代码示例给大家介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-12-12
  • 手把手教你如何一眼分辨是C还是C++

    手把手教你如何一眼分辨是C还是C++

    在很大程度上,C++是C的超集,这意味着一个有效的C程序也是一个有效的C++程序,下面这篇文章主要给大家介绍了关于如何一眼分辨是C还是C++的相关资料,需要的朋友可以参考下
    2023-02-02
  • Prim(普里姆)算法求最小生成树的思想及C语言实例讲解

    Prim(普里姆)算法求最小生成树的思想及C语言实例讲解

    Prim算法能够在带权的图中搜索出最小生成树,这也是各大ACM和面试及考研题目中的热点,下面我们就来详细看一下Prim(普里姆)算法求最小生成树的思想及C语言实例讲解
    2016-06-06
  • QT UDP网络编程实现简单消息传输

    QT UDP网络编程实现简单消息传输

    这篇文章主要为大家详细介绍了QT UDP网络编程实现简单消息传输,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • C++ 转换函数用法案例详解

    C++ 转换函数用法案例详解

    这篇文章主要介绍了C++ 转换函数用法案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • 基于VC编写COM连接点事件的分析介绍

    基于VC编写COM连接点事件的分析介绍

    本篇文章是对VC编写COM连接点事件进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C语言如何实现可变参数详解

    C语言如何实现可变参数详解

    这种可变参数可以说是C语言一个比较难理解的部分,下面这篇文章主要给大家介绍了关于C语言如何实现可变参数的相关资料,需要的朋友可以参考下
    2021-07-07

最新评论