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

news2024/11/15 21:38:16

系列文章目录

  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.coloradmin.cn/o/738583.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【unity小技巧】委托(Delegate)的基础使用和介绍

文章目录 一、前言1. 什么是委托&#xff1f;2. 使用委托的优点 二、举例说明1. 例12. 例2 三、案例四、泛型委托Action和Func1. Action委托2. Func委托 五、参考六、完结 一、前言 1. 什么是委托&#xff1f; 在Unity中&#xff0c;委托&#xff08;Delegate&#xff09;是一…

2FRE-10B-35-G24电比例调速阀控制器

2FRE-06B-20-G24、2FRE-10B-35-G24、2FRE-10B-50-G24、2FRE-16B-80-G24、2FRE-16B-115-G24、2FRE-16B-160-G24电比例调速阀&#xff0c;通过给定电信号&#xff0c;控制阀出口流量大小&#xff0c;并且采用压力补偿原理&#xff0c;保证输出流量大小不受负载压力影响维持恒定;该…

低代码平台实用吗?有哪些大型企业在用低代码?

一、目前正在使用低代码平台的企业有哪些&#xff1f; &#xff08;例举一些各行业规模较大的企业&#xff09; 制造行业&#xff1a;施耐德电气、吉利汽车、科大能通、伟星集团、远东控股、一汽大众、老板电器、双菱电梯、君乐宝。 建筑行业&#xff1a;中国交建、筑福集团…

【C++STL】“vector“用法 入门必备 超详细

vector用法 什么是vector&#xff1f;vector的使用vector的定义(构造函数)vector iterator 的使用迭代器演示范围for vector 空间增长扩容机制 vector 的增删查改assignvector 迭代器失效问题。 &#x1f340;小结&#x1f340; &#x1f389;博客主页&#xff1a;小智_x0___0x…

关于将Leetcode上代码直接复制到自己环境中的问题

实例代码&#xff08;不考虑代码的优劣性&#xff09;&#xff1a; 注&#xff1a;我用的是一个在线平台的编译器 Problem1 NameError&#xff1a;name ‘List’ is not defined 解决方法&#xff1a;添加下面代码&#xff1a; from typing import ListProblem2 TypeError…

Android Glide预加载preload ,kotlin

Android Glide预加载preload ,kotlin val imageView findViewById<ImageView>(R.id.image_view)Glide.with(this).asBitmap().load(image_file.path).signature(ObjectKey(image_file.path)).addListener(object : RequestListener<Bitmap> {override fun onLoadF…

抓取唯美图库(BeautifulSoup)

使用BeautifulSoup 1、拿到主页面的源代码&#xff0c;然后提取到子页面的简介地址&#xff0c;href 2、通过href拿到子页面的内容。从子页面中找到导图片的下载地址 img -> src 3、下载图片 import requests from bs4 import BeautifulSoupurlhttps://www.umei.cc/bizhi…

qt 闹钟实现

实现一个闹钟 自定义时间 按下开始后 开始计时&#xff0c;结束计时会播报语音 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimer> #include <QTimerEvent> #include <QDateTime> #include <QTime> #include …

【Leetcode】37.解数独(困难)

一、题目 1、题目描述 编写一个程序,通过填充空格来解决数独问题。 数独的解法需 遵循如下规则: 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图) 数独部分空格内已填入了数字…

UVM中测试用例的启动

一、命令行指定 ./simv -l simv.log UVM_TESTNAMEmy_case0 其中 UVM_TESTNAMEmy_case0 中的my_case0就是测试用例的名字 二、文件结构 在test文件夹中my_case0.sv对应case的名字 三、文件说明 3.1 my_case0.sv 其中其他都是写死的&#xff0c;只有红框的部分根据具体的代码…

温度反转效应Temperature Inversion(载流子迁移率与过阈值电压 谁占主导)

In general, as temperature increases, the delay of standard cells increases because of mobility degradation at higher temperatures.    But in lower technology nodes the impact of temperature on the delay of the cell is inverse. In lower nodes, the delay …

上海亚商投顾:沪指维持震荡 光伏等新能源赛道走强

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 大小指数今日集体反弹&#xff0c;沪指高开后维持震荡走势&#xff0c;创业板指盘中涨超2%&#xff0c;午后涨幅有…

ts开发npm依赖包(插件)demo

序&#xff1a;涉及如下几个点 1、用js开发依赖包&#xff08;换个说法&#xff1a;你在开发第三方的\node_modules的插件了&#xff09; 2、用ts开发依赖包 3、解决本地开发的npm包重命名后不生效的问题 一、js版&#xff08;简单的&#xff09; 你直接在你的项目的\node_mod…

ndp48-web.exe_ndp48-x86-x64-allos-enu.exe_ndp48-x86-x64-allos-chs.exe下载地址

ndp48-web.exe、ndp48-x86-x64-allos-enu.exe、ndp48-x86-x64-allos-chs.exe下载地址 我发现网上几乎找不到地方直接下载&#xff0c;费了我九牛二虎的搜商&#xff0c;才发现原来可以这么下载。 我们可以去微软官方地址下载&#xff1a;下载 .NET Framework 4.8

react 利用antd-mobile实现楼层效果

首先是js模块 import React, { useEffect, useRef, useState } from react import { SideBar } from antd-mobile import ./louceng.css import { useThrottleFn } from ahooksconst items [{ key: 1, title: 第一项, text: <div>12313212313第一项12313212313第一项1…

图床项目之公网发布和测试

项目发布和测试 一、http服务测试1.1、ab http压力测试1.2、post测试&#xff08;注册请求和登录请求&#xff09; 二、性能测试2.1、生成测试脚本2.2、上传测试2.2.1、单客户端测试本地上传到本机服务器2.2.2、如果使用集群的方式进行测试 2.3、下载测试2.4、删除测试2.5、测试…

Vivado2018.3安装教程

1 下载安装包 这个软件是免费的&#xff0c;去官网注册即可完成下载。 https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vivado-design-tools/archive.html 2 解压安装包 注意&#xff1a;安装包需要解压到一个全英文路径 3 安装 1.双…

linux与windows趣味谈

文章目录 前言linux&windows两者常见的系统版本系统安装版本选择linux和windows使用体验聊聊折腾收获Linux系统使用技巧 前言 windows和linux同为常见的操作系统&#xff0c;相信大部分人对widows比较熟悉一点&#xff0c;对linux比较陌生一点儿。但相信&#xff0c;作为程…

如何利用MES管理系统做到车间可视化管理

车间可视化管理是提高生产效率和质量的关键一环。而MES生产管理系统能够为企业提供车间实时数据监控、生产计划管理、异常处理等功能&#xff0c;帮助企业实现车间可视化管理。本文将介绍如何利用MES生产管理系统做到车间可视化管理&#xff0c;包括数据采集、数据分析、实时监…

Error:java: 不再支持源选项 5 请使用 6 或更高版本。

今天电脑重新安装系统&#xff0c;安装jdk环境选择了11版本&#xff0c;但是创建工程时突然报错 报错&#xff1a;Error:java: 不再支持源选项 5 请使用 6 或更高版本 解决方案&#xff1a; 1.查看project setting中的project 和Modules的版本号是否与本机jdk的版本号是否一…