基于 FFmpeg 的跨平台视频播放器简明教程(六):使用 SDL 播放音频和视频

系列文章目录

  1. 基于 FFmpeg 的跨平台视频播放器简明教程(一):FFMPEG + Conan 环境集成
  2. 基于 FFmpeg 的跨平台视频播放器简明教程(二):基础知识和解封装(demux)
  3. 基于 FFmpeg 的跨平台视频播放器简明教程(三):视频解码
  4. 基于 FFmpeg 的跨平台视频播放器简明教程(四):像素格式与格式转换
  5. 基于 FFmpeg 的跨平台视频播放器简明教程(五):使用 SDL 播放视频

文章目录

  • 系列文章目录
  • 前言
  • 音频基础
    • 计算机存放声音的方式:数字音频
    • FFmpeg 中的音频
  • 音频播放
  • SDL 中播放音频
  • FFmpeg 音频重采样
  • FFmpeg 解码,SDL 播放视频与音频
  • 总结
  • 参考


前言

在上篇文章 基于 FFmpeg 的跨平台视频播放器简明教程(五):使用 SDL 播放视频 中,我们使用 FFmpeg + SDL 来播放视频画面,但仅仅只是画面。今天,我们将讨论如何使用 FFmpeg + SDL 同时播放画面和声音。

本文参考文章来自 An ffmpeg and SDL Tutorial - Tutorial 03: Playing Sound 。这个系列对新手较为友好,但 2015 后就不再更新了,以至于文章中的 ffmpeg api 已经被弃用了。幸运的是,有人对该教程的代码进行重写,使用了较新的 api,你可以在 rambodrahmani/ffmpeg-video-player 找到这些代码。

本文的代码在 ffmpeg_video_player_tutorial-my_tutorial03。

音频基础

计算机存放声音的方式:数字音频

数字音频是一种将声音信号转换为可以被计算机存储和处理的格式的技术。顾名思义,数字音频指的是以数字形式表示的音频。在录制数字音频时,首先需要对原始的模拟声音信号进行采样,即在连续的声音波形上定期取样。然后,对每个采样点进行量化,即将其幅度映射到一个有限数量的离散数值集合上。这样,我们就得到了一系列数字值,它们可以在计算机系统中进行存储、编辑和播放。关于数字音频更多内容,请参考 数字音频基础­­­­­-从PCM说起。

FFmpeg 中的音频

在 基于 FFmpeg 的跨平台视频播放器简明教程(二):基础知识和解封装(demux)中,提到了一个容器通常同时包含视频流和音频流。当需要音频数据时,可以从音频流中读取AVPacket,然后进行音频解码,得到AVFrame。

FFmpeg的音频解码器支持包括MP3、AAC、FLAC、WAV等多种音频文件格式。解码音频后,数据将存储在AVFrame->data中。要正确使用这些解码后的数据,首先需要了解它们是以何种采样格式(Sample Format)存储的。

在 FFmpeg 中可以使用 AVSampleFormat 枚举类型来确定音频数据的采样格式。常见的采样格式有:S16、S32、FLT、DBL、U8等。当你了解了音频数据的采样格式,就可以对其进行消费,如将音频数据写入音频缓冲区,然后送到音频解码器或音频混合器中进行混音处理,最终实现音频的播放。在 基于 FFMPEG 的音频编解码(二):音频解码 中一文中,有更多关于 FFmpeg 采样格式的介绍,请阅读,此处不再赘述。

音频播放

计算机操作系统是如何播放音频的?计算机操作系统播放音频主要分为以下两部分:

  1. 音频线程:操作系统会启动一个专门负责处理音频数据的音频线程,这个线程的主要任务是将音频数据传递给音频硬件(例如声卡、扬声器)。音频线程在后台运行,它会定期将缓冲区中的音频数据发送给硬件,确保音频播放的连续性和稳定性。

  2. 回调函数:回调函数是音频线程与具体的音频播放应用之间的桥梁。音频线程通过调用回调函数从应用程序中获取待播放的音频数据。这些数据通常需要经过解码、重采样等处理,以适应音频硬件的播放要求。回调函数负责按照预设的格式提供音频数据,例如,对音量进行调整,将音频数据调整为硬件可识别的采样率、比特深度等。

在实际的音频播放过程中,音频线程周期性地调用回调函数,从应用程序中获取新的音频数据,然后发送给音频硬件进行播放。回调函数则需要在每次调用时读取解码后的音频数据并进行处理,以满足音频线程的需求。这样的设计模式使得音频播放能够与应用程序逻辑分离,方便开发者对音频播放过程进行控制,提高音频播放的响应速度与灵活性。同时,这种框架也有利于操作系统统一管理音频资源,保证多个音频应用之间的协同与兼容。

SDL 中播放音频

SDL 同样采用回调函数的形式来播放音频,具体步骤包括:

  1. 初始化SDL 首先需要对SDL进行初始化,以便能够使用它的音频功能。可以通过调用SDL_Init函数来实现,需要传入的参数为SDL_INIT_AUDIO。
if (SDL_Init(SDL_INIT_AUDIO) < 0) {// 错误处理逻辑
}
  1. 设置音频规格 接下来需要定义音频的规格,包括采样率、样本格式等。这可以通过填充SDL_AudioSpec结构来完成。
SDL_AudioSpec wanted_spec, obtained_spec;
memset(&wanted_spec, 0, sizeof(wanted_spec));
wanted_spec.freq = 44100;
wanted_spec.format = AUDIO_S16SYS;
wanted_spec.channels = 2;
wanted_spec.samples = 1024;
wanted_spec.callback = audio_callback;  // 回调函数
wanted_specs.userdata = user_data; // 设置回调函数中的 userdata
  1. 打开音频设备 使用SDL_OpenAudioDevice函数打开音频设备,并传入上一步定义的音频规格。这个函数将返回一个设备ID,以便于后续操作。
SDL_AudioDeviceID audio_dev;
audio_dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &obtained_spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
if (audio_dev == 0) {// 错误处理逻辑
}
  1. 实现回调函数 当音频设备需要更多的音频数据时,SDL会回调audio_callback()函数。需要为其提供一个播放位置、缓冲区等信息。
void audio_callback(void *userdata, Uint8 *stream, int len) {// 用户实现:将音频数据填充到stream中,长度为len// userdata 则是你自定义的数据,可以将其转换成对应的指针
}
  1. 开始播放 通过SDL_PauseAudioDevice函数启动音频设备,开始播放音频。
SDL_PauseAudioDevice(audio_dev, 0);
  1. 事件处理与播放控制 在实际应用中,需要处理不同的事件,如暂停、恢复等。具体处理方式可依据具体需求来实现。
  2. 关闭音频设备与释放资源 当音频播放结束或应用退出时,需要关闭音频设备并释放相关资源。
SDL_CloseAudioDevice(audio_dev);
SDL_Quit();

FFmpeg 音频重采样

在不同的视频文件中,音频流可能采用不同的采样格式,而系统支持的采样格式通常是固定的。例如,在 SDL 中,可以使用 SDL_OpenAudioDevice 函数来打开音频设备并设置所需的音频参数,其中 SDL_AudioSpec 结构体中的 format 字段表示所需的播放采样格式。

因此,要想实现通用的音频播放器,需要对不同的采样格式进行转换。这个过程被称为音频重采样,可以将音频数据从一个采样格式转换为另一个采样格式。在重采样过程中需要注意保持音频质量、减少失真和降噪等。

一般来说,重采样的核心思想是通过插值或者截断的方式改变采样率,达到从一种采样格式转换为另一种采样格式的目的。在实现过程中,可以使用各种算法,例如线性插值、卷积或者傅里叶变换等来实现重采样。

因此,在构建音频播放器时,需要在播放前对音频数据进行重采样,以保证其与系统所支持的播放采样格式相同,从而实现播放效果。

与图像转换类似,FFmpeg 中也提供了音频重采样的工具: libswresample。使用的步骤包括:

  1. 使用 swr_alloc 创建 SwrContext 重采样上下文
  2. 通过 av_opt_set_int 配置上下文变量,通常包括如下信息:
av_opt_set_int(swr, "in_channel_count", in_num_channels, 0);
av_opt_set_int(swr, "out_channel_count", out_num_channels, 0);
av_opt_set_int(swr, "in_channel_layout", in_channel_layout, 0);
av_opt_set_int(swr, "out_channel_layout", out_channel_layout, 0);
av_opt_set_int(swr, "in_sample_rate", in_sample_rate, 0);
av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", in_sample_format, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", out_sample_format, 0);
  1. 使用 swr_init(swr) 初始化上下午
  2. 初始化上下文成功后,即可调用 swr_convert 进行重采样

在我们的教程中封装了 FFmpegAudioResampler 进行音频重采样,具体实现请参看源码。

FFmpeg 解码,SDL 播放视频与音频

基于 FFmpeg 的跨平台视频播放器简明教程(五):使用 SDL 播放视频 中,我们已经知晓如何使用 FFmpeg + SDL 进行视频画面的播放。基于这份代码,我们添加上播放音频的逻辑。
在这里插入图片描述

整体框架如上图,其中:

  1. Demuxer 解封装,负责将音频流和视频流分开,将不同流的 Packet 送到不同的 Packet Queue 中。
  2. Codec 解码器,分为音频解码器和视频解码器,负责从 Packet Queue 读取 Packet,然后解码成 Frame,并将 Frame 送到不同的 Frame Queue 中。
  3. 主线程中,从 Video Frame Queue 中读取 Frame,经过 Image Convert 进行像素格式转换,通过 SDL Render 最终渲染至屏幕中。
  4. 音频线程中,通过 SDL 回调函数去 Audio Frame Queue 中读取 Frame,经过 Audio Resampler 进行重采样,最终通过喇叭播放出声音。

这个框架基本包含了播放器中所有基本要素,后续播放器音画同步、seek 等功能都基于此框架实现。

具体代码实现在 ffmpeg_video_player_tutorial-my_tutorial03 中,详细的代码说明就此略过,如有不清楚的地方可以直接评论。

总结

本文首先介绍了计算机音频的基本概念,并对 SDL 中播放音频的流程进行了说明;接着,介绍了 FFmpeg 中音频重采样的功能;最后,结合上述知识点和之前视频播放的功能,对视频播放器整体框架做了介绍,并给出了具体代码实现。

参考

  • An ffmpeg and SDL Tutorial - Tutorial 03: Playing Sound
  • rambodrahmani/ffmpeg-video-player
  • ffmpeg_video_player_tutorial-my_tutorial03
  • 数字音频基础­­­­­-从PCM说起
  • 基于 FFMPEG 的音频编解码(二):音频解码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/21678.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

re学习(15)BUUCTF 2019红帽杯easyRe

参考视频&#xff1a; 【BUUCTF】每天一个CTF11“2019红帽杯easyRe”_哔哩哔哩_bilibili &#xff08;本人觉得看视频比看博客效率能提高十倍&#xff0c;呜呜呜&#xff0c;还是视频香~~~与君共勉&#xff09; 下载地址&#xff1a; BUUCTF在线评测 前言&#xff1a;虽然…

opencv 图像基础处理_灰度图像

opencv 学习2_灰度图像 二值图像表示起来简单方便&#xff0c;但是因为其仅有黑白两种颜色&#xff0c;所表示的图像不够细腻。如果想要表现更多的细节&#xff0c;就需要使用更多的颜色。例如&#xff0c;图 2-3 中的 lena 图像是一幅灰度图像&#xff0c; 它采用了更多的数值…

如何用Three.js + Blender打造一个web 3D展览馆

作者&#xff1a;vivo 互联网前端团队- Wei Xing 运营活动新玩法层出不穷&#xff0c;web 3D炙手可热&#xff0c;本文将一步步带大家了解如何利用Three.js和Blender来打造一个沉浸式web 3D展览馆。 一、前言 3D展览馆是什么&#xff0c;先来预览下效果&#xff1a; 看起来像…

locust性能测试和分布式压测

一、工具介绍 Locust是一个开源的Python性能测试工具&#xff0c;用于模拟大量并发用户访问网站、API等&#xff0c;以测试系统的性能和稳定性。它的主要特点包括&#xff1a; 1.简单易用&#xff1a;Locust基于Python编写&#xff0c;使用方便&#xff0c;学习曲线较低。 2…

【高并发】高并发架构实战:从需求分析到系统设计

Yan-英杰的主页 悟已往之不谏 知来者之可追 C程序员&#xff0c;2024届电子信息研究生 很多软件工程师的职业规划是成为架构师&#xff0c;但是要成为架构师很多时候要求先有架构设计经验&#xff0c;而不做架构师又怎么会有架构设计经验呢&#xff1f;那么要如何获得架构设…

【Unity3D 问题总结】☀️ | 解决LayoutGroup配合Content Size Fitter使用时发生子成员位置错乱问题

&#x1f3ac; 博客主页&#xff1a;https://xiaoy.blog.csdn.net &#x1f3a5; 本文由 呆呆敲代码的小Y 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;Unity系统学习专栏 &#x1f332; 游戏制作专栏推荐&#xff1a;游戏制作 &…

【LLM】金融大模型场景和大模型Lora微调实战

文章目录 一、金融大模型背景二、大模型的研究问题三、大模型技术路线四、LLaMA家族模型五、Lora模型微调的原理六、大模型Lora微调实战Reference 一、金融大模型背景 金融行业需要垂直领域LLM&#xff0c;因为存在金融安全和数据大多数存储在本地&#xff0c;在风控、精度、实…

更开放、更高性能、更具规模,闪马智能布局AGI时代

7月6日&#xff0c;2023世界人工智能大会&#xff08;WAIC 2023&#xff09;在上海盛大开幕。本届大会以“智联世界 生成未来”为主题&#xff0c;聚焦通用人工智能发展&#xff0c;共话产业新未来。 8日上午&#xff0c;由上海闪马智能科技有限公司&#xff08;下称“闪马智能…

声网 Agora音视频uniapp插件跑通详解

一、前言 在使用声网SDK做音视频会议开发时, 通过声网官方论坛 了解到,声网是提供uniapp插件的,只是在官方文档中不是很容易找到。 插件地址如下: Agora音视频插件 Agora音视频插件(JS) 本文讲解如何跑通演示示例 二、跑通Demo 2.1 环境安装: 参考: 2. 通过vue-…

在Excel电子表格中用公式实现最最简易的标签套打

每月要为单位新入职员工打印标签贴纸&#xff0c;贴于档案之上&#xff0c;之前是用Excel建立一张表&#xff0c;通过拖动单元格大小&#xff0c;调整文本位置&#xff0c;实现标签贴纸的打印功能。 后来&#xff0c;公司每月都会新招入一批员工&#xff0c;每次打印贴纸时&…

漏洞深度分析 | CVE-2023-36053-Django 表达式拒绝服务

​ 项目介绍 Django 是一个高级 Python Web 框架&#xff0c;鼓励快速开发和简洁、务实的设计。它由经验丰富的开发人员构建&#xff0c;解决了 Web 开发的大部分麻烦&#xff0c;因此您可以专注于编写应用程序&#xff0c;而无需重新发明轮子。它是免费且开源的。 项目地址…

数字化时代,企业的数据指标体系

在社会节奏越来越快&#xff0c;处理的信息量越来越大的今天&#xff0c;传统的经营管理模式已经适应不了当下的环境。而由经验、情感组成的业务调整以及决策能力不再能正确指导企业走在正确的方向上&#xff0c;所以数据就成为了企业新的业务优化调整和支撑企业高层管理进行决…