SDL2 播放音频(MP4)
1.简介
这里引入FFmpeg库,获取音频流数据,然后通过FFmpeg将视频流解码成pcm原始数据,再将pcm数据送入到SDL库中实现音频播放。
2.FFmpeg的操作流程
- 注册API:av_register_all()
- 构建输入AVFormatContext上下文:avformat_open_input()
- 查找音视频流信息:avformat_find_stream_info()
- 查找解码器:avcodec_find_decoder()
- 打开解码器:avcodec_open2()
- 然后通过while循环,不停的读取数据:av_read_frame()
- 帧解码:avcodec_send_packet()和avcodec_receive_frame()
- 重采样:swr_convert()
3.SDL音频播放流程
SDL播放音频的流程如下:
- 初始化音频子系统:SDL_Init()。
- 设置音频参数:SDL_AudioSpec。
- 设置回调函数:SDL_AudioCallback。
- 打开音频设备:SDL_OpenAudio()。
- 打开pcm文件,读取数据。
- 开始播放:SDL_PauseAudio()。
4.示例
#include <stdio.h>
#include <SDL.h>
#include <memory>extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libswresample/swresample.h"
};static Uint8 *audio_chunk;
static Uint32 audio_len;
static Uint8 *audio_pos;void fill_audio(void *udata, Uint8 *stream, int len)
{//SDL 2.0SDL_memset(stream, 0, len);if (audio_len == 0) /* Only play if we have data left */return;len = (len > audio_len ? audio_len : len); /* Mix as much data as possible */SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME/2);audio_pos += len;audio_len -= len;
}AVFrame *recv(AVCodecContext *codecCtx)
{if (!codecCtx){return NULL;}AVFrame *frame = av_frame_alloc();int ret = avcodec_receive_frame(codecCtx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){av_frame_free(&frame);return NULL;}else if (ret < 0){av_frame_free(&frame);return NULL;}return frame;
}#undef main
int main(int argc, char* argv[])
{av_register_all();///ffmpegavformat_network_init();AVFormatContext* pFormatCtx = NULL;const char* inputUrl = "./2.mp4";///打开输入的流int ret = avformat_open_input(&pFormatCtx, inputUrl, NULL, NULL);if (ret != 0){printf("Couldn't open input stream.\n");return -1;}//查找流信息if (avformat_find_stream_info(pFormatCtx, NULL) < 0){printf("Couldn't find stream information.\n");return -1;}//找到音频流索引int audio_index = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);AVStream* st = pFormatCtx->streams[audio_index];AVCodec* codec = nullptr;//找到解码器codec = avcodec_find_decoder(st->codecpar->codec_id);if (!codec){fprintf(stderr, "Codec not found\n");return -1;}//申请AVCodecContextAVCodecContext* pCodecCtx = avcodec_alloc_context3(codec);if (!pCodecCtx){return -1;}avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[audio_index]->codecpar);//打开解码器if ((ret = avcodec_open2(pCodecCtx, codec, NULL) < 0)){return -1;}AVPacket* pkt = av_packet_alloc();//------------SDL----------------//Output Info-----------------------------printf("---------------- File Information ---------------\n");av_dump_format(pFormatCtx, 0, inputUrl, 0);printf("-------------------------------------------------\n");SwrContext *swrContext = swr_alloc();if (!swrContext){return -1;}swrContext = swr_alloc_set_opts(NULL, //ctxAV_CH_LAYOUT_STEREO, //输出channel布局AV_SAMPLE_FMT_S16, //输出的采样格式44100, //采样率av_get_default_channel_layout(pCodecCtx->channels), //输入channel布局pCodecCtx->sample_fmt, //输入的采样格式pCodecCtx->sample_rate, //输入的采样率0, NULL);// 初始化重采样上下文if (swr_init(swrContext) < 0){swr_free(&swrContext);return -1;}if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){printf("Could not initialize SDL - %s\n", SDL_GetError());return -1;}//SDL_AudioSpecSDL_AudioSpec wanted_spec;wanted_spec.freq = 44100;wanted_spec.format = AUDIO_S16SYS;wanted_spec.channels = 2;wanted_spec.silence = 0;wanted_spec.samples = 1024;wanted_spec.callback = fill_audio;wanted_spec.userdata = pCodecCtx;if (SDL_OpenAudio(&wanted_spec, NULL) < 0){printf("can't open audio.\n");return -1;}//PlaySDL_PauseAudio(0);// 分配输出音频数据uint8_t *out_buffer = nullptr;while (av_read_frame(pFormatCtx, pkt) >= 0){if (pkt->stream_index == audio_index){//一次send 多次recvint ret = avcodec_send_packet(pCodecCtx, pkt);if (ret < 0)continue;//释放资源av_packet_unref(pkt);while (1){AVFrame *frame = recv(pCodecCtx);if (!frame)break;//输入的样本数int in_nb_samples = frame->nb_samples;//1024int out_linesize;int dst_nb_samples = av_rescale_rnd(in_nb_samples, 44100, frame->sample_rate, AV_ROUND_UP);//输出的样本数int out_buffer_size = av_samples_get_buffer_size(NULL, 2, dst_nb_samples, AV_SAMPLE_FMT_S16, 0);if(!out_buffer)out_buffer = (uint8_t *)av_malloc(out_buffer_size);//返回每个通道输出的样本数,错误时为负值int sampleCount = swr_convert(swrContext, &out_buffer, dst_nb_samples,(const uint8_t**)frame->data, in_nb_samples);if (sampleCount <= 0){av_frame_free(&frame);break;}int outSize = sampleCount * 2 * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);while (audio_len > 0)//Wait until finishSDL_Delay(1);//Set audio buffer (PCM data)audio_chunk = (Uint8 *)out_buffer;//Audio buffer lengthaudio_len = outSize;audio_pos = audio_chunk;av_frame_free(&frame);}}else{//释放资源av_packet_unref(pkt);}}//--------------av_free(out_buffer);av_packet_free(&pkt);swr_close(swrContext);swr_free(&swrContext);avcodec_close(pCodecCtx);avcodec_free_context(&pCodecCtx);avformat_close_input(&pFormatCtx);SDL_CloseAudio();SDL_Quit();
}
5.相关推荐
[总结]FFMPEG视音频编解码零基础学习方法_零基础ffmpeg 雷霄骅-CSDN博客
FFmpeg 音频解码(秒懂)-CSDN博客
SDL2 播放音频数据(PCM)-CSDN博客
SDL2 消息循环和事件响应-CSDN博客
SDL2 播放视频文件(MP4)-CSDN博客
相关文章:

SDL2 播放音频(MP4)
1.简介 这里引入FFmpeg库,获取音频流数据,然后通过FFmpeg将视频流解码成pcm原始数据,再将pcm数据送入到SDL库中实现音频播放。 2.FFmpeg的操作流程 注册API:av_register_all()构建输入AVFormatContext上下文:avform…...
WMS仓库管理系统库位功能
后端 using Infrastructure.Attribute; using Model.Dto.WarehouseManagement; using Model.Page; using Model.WarehouseManagement; using Repository; using Service.Interface.WarehouseManagement; using SqlSugar;namespace Service.WarehouseManagement {[…...
vue2组件通信中的一些拓展(props,emit,ref父子双向传参)
说明 我上一篇文章中基本对vue所有的数据通信方法进行了一个整理归纳。 其实我并没有像传统的那样去罗列,比如父传子有props,ref,子传父为emit,兄弟用$bus等等。 因为在我的实际练习和业务开发中,props,emit,ref等可以实现父子数据互传,这里就涉及一个比较重要的编程思维,函…...

Flink1.17 DataStream API
目录 一.执行环境(Execution Environment) 1.1 创建执行环境 1.2 执行模式 1.3 触发程序执行 二.源算子(Source) 2.1 从集合中读取数据 2.2 从文件读取数据 2.3 从 RabbitMQ 中读取数据 2.4 从数据生成器读取数据 2.5 …...

数据结构中树、森林 与 二叉树的转换
1 树转换为 二叉树 将树转换成二叉树的步骤是: 加线。在所有的兄弟结点之间加一条线。去线。对于树中的每个结点,只保留它与第一个孩子结点的连线,删除该结点其他孩子结点之间的连线。调整。以树的根结点为轴心,将整个树顺时针旋…...
力扣labuladong——一刷day43
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、力扣257. 二叉树的所有路径二、力扣129. 求根节点到叶节点数字之和三、力扣199. 二叉树的右视图四、力扣662. 二叉树最大宽度 前言 一般来说,如…...

MapApp 地图应用
1. 简述 1.1 重点 1)更好地理解 MVVM 架构 2)更轻松地使用 SwiftUI 框架、对齐、动画和转换 1.2 资源下载地址: Swiftful-Thinking:https://www.swiftful-thinking.com/downloads 1.3 项目结构图: 1.4 图片、颜色资源文件图: 1.5 启动图片配置图: 2. Mo…...
Java之反射获取和赋值字段
在Java中,反射能够使得代码更加通用,往往用于工具类中。 但常常我们在获取或者赋值反射的属性时,会出现没有赋值成功的结果,往往是由于这个属性在父级中而导致的,这个时候我们就不能用getDeclaredField方法单独获取字段…...

ckplayer自己定义风格播放器的开发记录
CKplayer是一款基于Flash和HTML5技术的开源视频播放器,支持多种格式的音视频播放,并且具有优秀的兼容性和扩展性。 它不仅可以在网页上播放本地或者网络上的视频,还可以通过代码嵌入到网页中,实现更加个性化的播放效果。CKplayer…...
全网最全Django面试题整理(一)
Django 中的 MTV 是什么意思? 在Django中,MTV指的是“Model-Template-View”,而不是常见的MVC(Model-View-Controller)架构。Django的设计理念是基于MTV的 Model(模型) 模型代表数据存取层&am…...
vue统一登录
说明: 统一登录其实就是前端去判断Url地址的token 之后如果有token未过期就直接跳转到首页。 说到浏览器输入url地址,那从浏览器输入地址一共发生了几件事大致如下: DNS解析域名,获取IP地址 --》 建立TCP连接(三次握…...

MVSNet论文笔记
MVSNet论文笔记 摘要1 引言2 相关基础2.1 多视图立体视觉重建(MVS Reconstruction)2.2 基于学习的立体视觉(Learned Stereo)2.3 基于学习的多视图的立体视觉(Learned MVS) 3 MVSNet3.1 网络架构3.2 提取图片…...

大型 APP 的性能优化思路
做客户端开发都基本都做过性能优化,比如提升自己所负责的业务的速度或流畅性,优化内存占用等等。但是大部分开发者所做的性能优化可能都是针对中小型 APP 的,大型 APP 的性能优化经验并不会太多,毕竟大型 APP 就只有那么几个&…...
K8S配置资源管理
这里写目录标题 K8S配置资源管理一.Secret1.介绍2.Secret 有四种类型3.创建 Secret4.使用方式 二.ConfigMap1.介绍2.创建 ConfigMap3.Pod 中使用 ConfigMap4.用 ConfigMap 设置命令行参数5.通过数据卷插件使用ConfigMap6.ConfigMap 的热更新7.ConfigMap 更新后滚动更新 Pod K8S…...

Redis 的集群模式实现高可用
来源:Redis高可用:武林秘籍存在集群里,那稳了~ (qq.com) 1. 引言 前面我们已经聊过 Redis 的主从同步(复制)和哨兵机制,这期我们来聊 Redis 的集群模式。 但是在超大规模的互联网应用中,业务规…...
21、嵌套路由实战操作
1、创建内嵌子路由,你需要添加一个vue文件,同时添加一个与该文件同名的目录用来存放子视图组件。 2、在父组件(.vue)内增加用于显示子视图内容 新建文件 pages\index_id.vue 生成的对应路由 {path: "/",component: _…...
WPF 控件的缩放和移动
WPF 控件的缩放和移动 1.页面代码 <ContentControl ClipToBounds"True" Cursor"SizeAll"><Viewboxx:Name"viewbox"MouseDown"viewbox_MouseDown"MouseMove"viewbox_MouseMove"MouseWheel"Viewbox_MouseWhee…...
Python and和or的优先级实例比较
Python and和or的优先级 and和or都是Python的逻辑运算符,都为保留字。通常情况下,在没有括号影响,and和or的优先级中and在代码的逻辑运算过程中会相对优先一些,及在同一行的Python代码中,and会优先与or执行。下面将通…...

数据结构与算法编程题2
逆置线性表,使空间复杂度为 O(1) #include <iostream> using namespace std;typedef int ElemType; #define Maxsize 100 #define OK 1 #define ERROR 0 typedef struct SqList {ElemType data[Maxsize];int length; }SqList;void Init_SqList(SqList& …...
Java开发者的Python快速进修指南:控制之if-else和循环技巧
简单介绍 在我们今天的学习中,让我们简要了解一下Python的控制流程。考虑到我们作为有着丰富Java开发经验的程序员,我们将跳过一些基础概念,如变量和数据类型。如果遇到不熟悉的内容,可以随时查阅文档。但在编写程序或逻辑时&…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...

LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...