ffmpeg[学习(四)](代码实现) 实现音频数据解码并且用SDL播放

news2025/1/17 21:44:39

0、作者杂谈

CSDN大多数都是落后的,要么是到处复制粘贴的,对于初学者我来说困惑了很久,大多数CSDN文章都是使用旧的API ,已经被否决了,于是我读一些官方文档,和一些开源项目音视频的输出过程,写出这篇文章希望能帮助到入门音视频的人。
感觉这个专栏没多少人看呃,哎~

一、流程导图

其实与视频解码播放流程差不了太多,前面部分和专栏(一)一样
ffmpeg学习(一)
后面的话是添加了回调函数用于声卡通过回调函数拉数据到声卡缓冲区
在这里插入图片描述

二、实现过程

在这里插入图片描述
这中间省略了很多步骤 其实和ffmpeg学习(三)类似

SDL参数

在这里插入图片描述

转码参数和一开始的参数

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/2d471a44ad4f45d99eac2af9ae05b400.pn
这里新API中将AVChannelLayout分离出来了,我们需要自己创建一个AVChannelLayout来获得声道布局为后面转码参数做铺垫

转码器

在这里插入图片描述

数据转换格式

在这里插入图片描述
这里SDL_Delay主要是防止声音播放过快。

回调函数

在这里插入图片描述

播放过程

在这里插入图片描述
😔 这里播放的是瓦罗兰特的die for you 可惜你们听不到 😄 希望这篇文章对读者有收获!

源代码

#include<iostream>
#include "vp_test.h"
 static uint8_t* audio_buf = new uint8_t[4096];
 static int audio_size;

void read_audio_data(void* userdata, Uint8* stream,int len)
{
	if (audio_size == 0)
		return;
	int audio_buf_index = 0;
	int len1 = 0; 
	while (len > 0){
	len1 = audio_size - audio_buf_index;
	if (len1 > len)
		len1 = len;
	memcpy(stream, audio_buf+audio_buf_index, len1);
	audio_buf_index += len1;
	stream += len1;
	len -= len1;
	}
	SDL_Delay(1);

}

int vp_audio(const char * filepath) {
	int ret = 0;

	AVFormatContext* is = NULL;
	AVCodecContext* ic = NULL;
	const AVCodec* codec = NULL;
	AVPacket* pkt = NULL;
	AVFrame* frame = NULL;
	int audio_index;

	//init ffmpeg
	is = avformat_alloc_context();
	pkt = av_packet_alloc();
    frame = av_frame_alloc();

	//初始化网络库
	avformat_network_init();
     
	if (avformat_open_input(&is, filepath, NULL, NULL) != 0) {
		return -1;
	}

	if (avformat_find_stream_info(is, NULL) < 0) {
		return -1;
	}
	//查找音频解码器
	for (int i = 0; i < is->nb_streams; i++) {
		AVStream *stream = NULL;
		stream = is->streams[i];
		if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
			codec = avcodec_find_decoder(stream->codecpar->codec_id);
			ic = avcodec_alloc_context3(codec);
			avcodec_parameters_to_context(ic,stream->codecpar);
			audio_index = i;
		}
	}
	//打开解码器
	if (avcodec_open2(ic, codec, NULL) != 0)
		return -1;


	//SDL 初始化音频模块
	SDL_Init(SDL_INIT_AUDIO | SDL_INIT_AUDIO);

	//初始化SDL中自己想设置的参数
	SDL_AudioSpec wanted_spec ;
	wanted_spec.freq = 44100;
	wanted_spec.format = AUDIO_S16SYS;
	wanted_spec.channels = 2;
	wanted_spec.samples = 1024;
	wanted_spec.callback = read_audio_data;
	wanted_spec.userdata = ic;
	
	//设置转码参数(转码成我们SDL播放的音频参数格式)
	AVChannelLayout out_ch;
	av_channel_layout_default(&out_ch, 2);
	int out_nb_samples = 1024;
	enum AVSampleFormat sample_fmt = AV_SAMPLE_FMT_S16;
	int out_sample_rate = 44100;

	// 解码前的格式参数
	AVChannelLayout in_ch ;
	av_channel_layout_default(&in_ch, 2);
	enum AVSampleFormat in_sample_fmt=ic->sample_fmt;
	int in_sample_rate=ic->sample_rate;

	//转码器
	SwrContext* swr_ctx = NULL;
	swr_alloc_set_opts2(
		&swr_ctx,
		&out_ch,
		sample_fmt,
		out_sample_rate,
		&in_ch,
		in_sample_fmt,
		in_sample_rate,
		0, NULL);

	swr_init(swr_ctx);
	//打开音频播放设备
	if (SDL_OpenAudio(&wanted_spec, NULL) < 0)
		return -1;
	//开始或暂停播放
	SDL_PauseAudio(0);//开始调用回调函数填充缓冲区
	while (true) {
		while (true) {
			if (av_read_frame(is, pkt))
				goto end;//读取完毕
		if (pkt->stream_index == audio_index)
			break;
		}
		//发送编码包
		avcodec_send_packet(ic, pkt);
		av_frame_unref(frame);
		if (avcodec_receive_frame(ic, frame) == 0) {
			//数据转换
			int upper_bound_samples = swr_get_out_samples(swr_ctx, frame->nb_samples);
			uint8_t* out[4] = { 0 };
			out[0] = (uint8_t*)av_malloc(upper_bound_samples * 2 * 2);
			int samples = swr_convert(
				swr_ctx,
				out,
				upper_bound_samples,
				(const uint8_t**)frame->data,
				frame->nb_samples);
			//将数据写入buffer区
			memcpy(audio_buf, out[0], samples * 4);
			audio_size = samples * 4;
			SDL_Delay(19);
		}
	}
end:
	if (is)
		avformat_free_context(is);
	if (ic)
		avcodec_free_context(&ic);
	if (pkt)
		av_packet_free(&pkt);
	if (frame)
		av_frame_free(&frame);
	if (swr_ctx)
		swr_free(&swr_ctx);
	SDL_CloseAudio();
	SDL_Quit();
	return 0;
}

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

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

相关文章

CCSC,一种CPU架构

core-circuit-separate-computer 核与执行电路的分离&#xff0c;最初是为了省电。 用寄存器实现这种分离。 V寄存器控制着执行电路的供电&#xff0c;V0则不供电&#xff0c;进入省电模式&#xff1b;V1则供电&#xff0c;进入工作模式。 P寄存器是parameter-register&#xf…

Qt应用-实现图像截取功能类似QQ上传头像截取功能

本文演示利用Qt实现图像截取功能类似QQ上传头像截取功能。 效果如下,通过移动中间的裁剪区域可以获得一张裁剪后的图片。 目录

Open3D 搜索某个点的圆柱形邻域点云(11)

Open3D 搜索某个点的圆柱形邻域点云(11) 一、算法介绍二、算法实现1、代码2、结果一、算法介绍 具体而言,search_hybrid_vector_3d方法会以指定的查询点为中心,在给定的半径范围内搜索邻域点。还可以指定近邻点的数量阈值,这对于需要特定数量邻域点的应用非常有用,比如提…

安全技能讲座 - 便携式灭火器 (Portable Fire Extinguishers )

【Transcript 】 火灾随时随地都可能发生&#xff0c;而且毫无征兆。如果您在家中或工作中遇到火灾&#xff0c;便携式灭火器可以帮助您保护自己&#xff0c;并有可能将火灾扼杀在摇篮中。本课程将向您介绍便携式灭火器、其工作原理和使用方法。成功完成本课程后&#xff0c;您…

kubectl常用命令(主题篇)

上一篇是按照操作把全局性的命令给整理出来&#xff0c;但是在实际的使用中&#xff0c;经常需要对某一个主题进行操作&#xff0c;因此这一篇按照对应的主题进行一系列操作。 集群 查看集群基本信息 kubectl cluster-info namespace 命名空间 查看 kubectl get namespace k…

查看Linux系统内存、CPU、磁盘使用率和详细信息

一、查看内存占用 1、free # free -m 以MB为单位显示内存使用情况 [rootlocalhost ~]# free -mtotal used free shared buff/cache available Mem: 11852 1250 8668 410 1934 9873 Swap: 601…

大模型学习之书生·浦语大模型3——基于InternLM和LangChain搭建知识库

基于InternLM和LangChain搭建知识库 1 大模型开发范式 LLM的局限性 知识受限&#xff1a;最新知识无法实时获取专业能力有限&#xff1a;有广度无深度定制化成本高&#xff1a;训练成本高 RAG VS Finetune RAG&#xff1a; 无需重新训练组织外挂加入知识容易受基座模型的影响…

Linux中快速搭建RocketMQ测试环境

必要的文件下载 为什么选择RocketMQ | RocketMQ x86_64位JDK下载0jdk/8u391-b13 rocketmq二进制包下载-rocketmq-all-5.1.4-bin-release.zip 编译好的直接可用的dashboard【rocketmq-dashboard-1.0.0.jar】请在文章顶部下载 dashboard配套的配置文件【application.propert…

基于宝塔搭建Discuz!论坛

一、安装宝塔 我是在我的虚拟机上安装图的宝塔 虚拟机版本&#xff1a;Ubuntu 18.04 wget -O install.sh https://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh 6dca892c安装完成之后在浏览器输入你的地址 https://你的域名&#xff08;或…

NX二次开发 Block UI 指定方位控件的应用

一、概述 NX二次开发中一般都是多个控件的组合&#xff0c;这里我首先对指定方位控件进行说明并结合选择对象控件&#xff0c;具体如下图所示。 二、实现功能获取方位其在选择面上原点的目标 2.1 在initialize_cb()函数中进行初始化&#xff0c;实现对象选择过滤面 //过滤平…

C++多态(超详解哦)

C多态 引言定义及实现多态的条件虚函数与虚函数的重写接口继承与实现继承函数重载&#xff0c;隐藏&#xff0c;重写的区别 抽象类多态的原理虚函数表&#xff08;虚表&#xff09;动态绑定与静态绑定 总结 引言 在生活中不乏这样的例子&#xff1a;成人与儿童在买票时会有不同…

FRPS配置服务端(腾讯云)、客户端(PC电脑Windows、树莓派Debian)并设置虚拟域名

1.服务端&#xff08;腾讯云&#xff09;&#xff1a;frps.ini [common] bind_port 7000 vhost_http_port8080 vhost_https_port44344 dashboard_port 7500 privilege_token your_password subdomain_host example.com use_encryption true encryption_method tls dashb…

oracle角色管理

常用角色 CONNECT,RESOURCE,DBA,EXP_FULL_DATABASE,IMP_FULL_DATABASE 1角色可以自定义&#xff0c;语法与创建用户一样 CREATE role role1 IDENTIFIED by 123; 2授权权限给角色 --自定义角色 CREATE role role1 IDENTIFIED by 123; --授权权限给角色 GRANT create view, …

Apache Doris (六十三): Spark Doris Connector - (3)-配置型及列映射关系

🏡 个人主页:IT贫道-CSDN博客 🚩 私聊博主:私聊博主加WX好友,获取更多资料哦~ 🔔 博主个人B栈地址:豹哥教你学编程的个人空间-豹哥教你学编程个人主页-哔哩哔哩视频 目录 1. Spark 操作Doris配置项

【软件测试】学习笔记-脚本与数据的解耦 + Page Object模型

本篇文章介绍GUI测试中两个非常重要的概念&#xff1a;测试脚本和数据的解耦&#xff0c;以及页面对象&#xff08;Page Object&#xff09;模型。 测试脚本和数据的解耦 GUI自动化测试适用的场景&#xff0c;尤其适用于需要回归测试页面功能的场景。如果在测试脚本中硬编码&a…

Fiddler工具 — 10.Statistics(统计)面板

1、Statistics介绍 Statistics 页签显示当前用户选择的 Sessions 的汇总信息&#xff0c;包括&#xff1a;选择的 Sessions 总数、发送字节数、接收字节数、响应类型的汇总表、世界各地通过不同请求方式所需的时间等。 Statistics 分页还会统计请求和响应的其他一些信息,如&a…

红帽宣布CentOS 7和RHEL 7将在2024年6月30日结束支持,企业面临紧迫的迁移压力!

2020 年红帽 (RedHat&#xff0c;已在 2019 年被 IBM 收购) 单方面宣布终止 CentOS Linux 的开发&#xff0c;此后 CentOS Linux 8 系列的更新已经在 2021 年 12 月结束&#xff0c;而 CentOS Linux 7 系列的更新将在 2024 年 6 月 30 日结束。 与 CentOS Linux 7 一起发布的 R…

网络安全B模块(笔记详解)- nmap扫描渗透测试

nmap扫描渗透测试 1.通过BT5对服务器场景Linux进行TCP同步扫描 (使用工具Nmap,使用参数n,使用必须要使用的参数),并将该操作使用命令中必须要使用的参数作为Flag提交; Flag:sS 2.通过BT5对服务器场景Linux进行TCP同步扫描 (使用工具Nmap,使用参数n,使用必须要使用的参数…

Adobe XD是什么?探索这款创新的用户体验设计工具

Adobexd是一种基于矢量的设计工具&#xff0c;主要用于设计移动和Web应用程序的用户界面(UI)。与Photoshop或ilustrator等其他Adobe产品相比&#xff0c;它相当轻。对于对快速设计和原型迭代感兴趣的界面设计师来说&#xff0c;轻量级并不是一件坏事。 在早期&#xff0c;Adob…

光缆通信有什么特点?

光缆由一个或多个光纤组成&#xff0c;每个光纤由一个非常纤细的玻璃或塑料纤维组成&#xff0c;可以传输光信号的高速数据。光缆通信具有以下特点&#xff1a; 1. 高带宽&#xff1a;光缆通信可以提供非常高的带宽&#xff0c;远远超过传统的铜缆通信。光纤的宽带特性使其能够…