最简单的基于 FFmpeg 的音频解码器

最简单的基于 FFmpeg 的音频解码器

  • 最简单的基于 FFmpeg 的音频解码器
    • 正文
    • 参考

参考雷霄骅博士的文章,链接:最简单的基于FFMPEG+SDL的音频播放器:拆分-解码器和播放器

最简单的基于 FFmpeg 的音频解码器

正文

FFmpeg 音频解码器实现了音频数据到 PCM 采样数据的解码。

如果你不会 Vusual Studio 下 FFmpeg 的项目配置,可以看我写的教程:Visual Studio 2015 中 FFmpeg 开发环境的搭建。

源代码:

// Simplest FFmpeg Audio Decoder.cpp : 定义控制台应用程序的入口点。

/**
* 最简单的基于 FFmpeg 的音频解码器
* Simplest FFmpeg Audio Decoder
*
* 刘文晨 Liu Wenchen
* 812288728@qq.com
* 电子科技大学/电子信息
* University of Electronic Science and Technology of China / Electronic and Information Science
* https://blog.csdn.net/ProgramNovice
*
* 本程序可以将音频码流(MP3,AAC等)解码为 PCM 采样数据。
*
* This software decode audio streams (MP3, ACC...) to PCM data.
*
*/

#include "stdafx.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define __STDC_CONSTANT_MACROS
#ifdef _WIN32
// Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
};
#else
// Linux
#endif

// 1 second of 48kHz 32bit audio,单位是字节
#define MAX_AUDIO_FRAME_SIZE 192000 

int main(int argc, char* argv[])
{
	// 结构体及变量定义
	AVFormatContext* pFormatCtx;
	int i, audioStream;
	AVCodecContext* pCodecCtx;
	AVCodec* pCodec;
	AVPacket* packet;
	uint8_t* out_buffer;
	AVFrame* pFrame;
	int ret;
	uint32_t len = 0;
	int got_picture;
	int index = 0;
	int64_t in_channel_layout;
	struct SwrContext *au_convert_ctx;

	// 输出文件路径
	FILE *pFile = fopen("output.pcm", "wb");
	// 输入文件路径
	char url[] = "skycity.mp3";

	// 注册支持的所有的文件格式(容器)及其对应的 CODEC,只需要调用一次
	av_register_all();
	// 对网络库进行全局初始化(加载 socket 库以及网络加密协议相关的库,为后续使用网络相关提供支持)
	// 注意:此函数仅用于解决旧 GnuTLS 或 OpenSSL 库的线程安全问题。
	// 如果 libavformat 链接到这些库的较新版本,或者不使用它们,则无需调用此函数。
	// 否则,需要在使用它们的任何其他线程启动之前调用此函数。
	avformat_network_init();
	// 使用默认参数分配并初始化一个 AVFormatContext 对象
	pFormatCtx = avformat_alloc_context();
	// 打开输入媒体流,分配编解码器上下文、解复用上下文、I/O 上下文
	if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0)
	{
		printf("Can't open input stream.\n");
		return -1;
	}
	// 读取媒体文件的数据包以获取媒体流信息
	if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
	{
		printf("Can't find stream information.\n");
		return -1;
	}

	printf("---------------- File Information ---------------\n");
	// 将 AVFormatContext 结构体中媒体文件的信息进行格式化输出
	av_dump_format(pFormatCtx, 0, url, false);
	printf("-------------------------------------------------\n");

	audioStream = -1;
	for (i = 0; i < pFormatCtx->nb_streams; i++)
	{
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			audioStream = i;
			break;
		}
	}
	if (audioStream == -1)
	{
		printf("Can't find a audio stream.\n");
		return -1;
	}
	// pCodecCtx 是指向音频流的编解码器上下文的指针
	pCodecCtx = pFormatCtx->streams[audioStream]->codec;
	// 查找 ID 为 pCodecCtx->codec_id 的已注册的音频流解码器
	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	if (pCodec == NULL)
	{
		printf("Codec not found.\n");
		return -1;
	}
	// 初始化指定的编解码器
	if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
	{
		printf("Can't open codec.\n");
		return -1;
	}

	// 为 packet 分配空间
	packet = (AVPacket *)av_malloc(sizeof(AVPacket));
	// 将 packet 中的可选字段初始化为默认值
	av_init_packet(packet);

	// 输出音频参数
	// 通道类型:双声道
	uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
	int out_nb_samples = pCodecCtx->frame_size;
	// 采样格式:pcm_s16le(整型 16bit)
	AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
	// 采样率:44100
	int out_sample_rate = 44100;
	int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
	int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);

	out_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
	pFrame = av_frame_alloc();

	// 根据声道数目获取声道布局
	in_channel_layout = av_get_default_channel_layout(pCodecCtx->channels);
	// 创建重采样结构体 SwrContext 对象
	au_convert_ctx = swr_alloc();
	// 设置重采样的转换参数
	au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, out_channel_layout, out_sample_fmt, out_sample_rate,
		in_channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0, NULL);
	// 初始化 SwrContext 对象
	swr_init(au_convert_ctx);

	// 读取码流中的音频若干帧或者视频一帧
	while (av_read_frame(pFormatCtx, packet) >= 0)
	{
		if (packet->stream_index == audioStream)
		{
			// 解码 packet 中的音频数据,pFrame 存储解码数据
			ret = avcodec_decode_audio4(pCodecCtx, pFrame, &got_picture, packet);
			if (ret < 0)
			{
				printf("Error in decoding audio frame.\n");
				return -1;
			}
			if (got_picture > 0)
			{
				// 进行格式转换,返回值为实际转换的采样数
				swr_convert(au_convert_ctx, &out_buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)pFrame->data, pFrame->nb_samples);
				// 打印每一帧的信息
				printf("index: %5d\t pts: %lld\t packet size: %d\n", index, packet->pts, packet->size);
				// 向输出文件中写入 PCM 数据
				fwrite(out_buffer, 1, out_buffer_size, pFile);
				index++;
			}
		}
		// 清空 packet 里面的数据
		av_free_packet(packet);
	}

	// 释放 SwrContext 对象
	swr_free(&au_convert_ctx);
	// 关闭文件指针
	fclose(pFile);
	// 释放内存
	av_free(out_buffer);
	// 关闭解码器
	avcodec_close(pCodecCtx);
	// 关闭输入音频文件
	avformat_close_input(&pFormatCtx);

	return 0;
}

本程序可以直接在 Visual Studio 2015 上运行。

程序运行后,会解码下面的音频文件。

在这里插入图片描述

解码后的 PCM 采样数据被保存成了一个文件,名叫 output.pcm。使用 Adobe Audition 设置采样率等信息后可以查看 PCM 的内容。

在这里插入图片描述

在这里插入图片描述

参考

05 FFmpeg4.4源码分析–解码

error C4996: ‘fopen’: This function or variable may be unsafe 的解决方法

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IO流(Java)

IO流 在学习IO流之前&#xff0c;我们首先了解一下File File File即文件或文件夹路径对象&#xff0c;其示例类可以是存在路径也可以是未创造路径 File有什么用 用于创建或操作文件或文件夹 File常用API API部分看得懂会查会用即可 IO流 IO(Input 读数据 Output写数据…

scikit-learn实现线性回归

要学习scikit-learn,我们必须要到scikit-clearn的官网中去查看公式和原理 scikit-learn 官网 scikit-learn 中文社区 进入官网一以后我们找到回归&#xff0c;然后再有监督学习中找到线性模型 scikit-learn实现简单的线性回归 公式&#xff1a; L2范数是指向量中每个元素的平…

倒计时模块复习

经典回顾倒计时 倒计时的基本布局介绍。 一个内容区域和一个输入区域&#xff0c;内容区域进行划分 直接使用flex布局会更快一点。 js代码 我们利用一下模块化思想&#xff0c;直接把获得时间这个功能写成一个函数。方便后续的调用 function getTime() {const date new Date…

第一课【习题】三方库

三方组件是开发者在系统能力的基础上进行了一层具体功能的封装&#xff0c;对其能力进行拓展的工具 。 可以通过ohpm uninstall 指令下载指定的三方库 lottie使用loadAnimation方法加载动画。 通过ohpm安装lottie后&#xff0c;在哪个文件中会生成相关的配置信息&#xf…

【Java探索之旅】我与Java的初相识(一):Java的特性与优点及其发展史

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Java入门到精通 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 一. Java语言概述与优势1.1 Java的概述1.2 Java语言的优势 二. Java领域与发展史2.1 Java的使用领域2.…

智能优化算法应用:基于跳蛛算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于跳蛛算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于跳蛛算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.跳蛛算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

MDK官网如何下载stm32支持包

网站&#xff1a;https://www.keil.com/demo/eval/arm.htm 1 2 3点这个下载

数字图像处理(实践篇)十九 漫水填充

目录 一 漫水填充算法--FloodFill 二 涉及的函数 三 实践 一 漫水填充算法--FloodFill FloodFill漫水填充算法就是选中与种子点相连接的区域&#xff0c;利用指定颜色进行区域颜色填充。可以通过设置连通方式或像素的范围控制填充的效果。通常是用来标记或者分离图像的一部…

直流负载箱的技术发展趋势和创新有哪些?

直流负载箱广泛应用于电子、通信、航空航天等领域&#xff0c;随着科技的不断发展&#xff0c;直流负载箱也在不断创新和改进&#xff0c;直流负载箱在负载电流和电压的测量方面要求高精度和高稳定性。未来的发展趋势是提高负载箱的测量精度和稳定性&#xff0c;以满足更高要求…

你知道怎样在 Python 中管理内存吗

memray 是一个Python库&#xff0c;它提供了一种可视化内存管理工具&#xff0c;可以帮助Python开发人员更好地理解和优化他们的代码中的内存使用情况。 它是由彭博社开发的&#xff0c;可用于分析Python程序中的内存泄漏和其他内存问题。以下是memray库的使用场景和入门案例。…

ChatGPT可能即将发布新版本,带有debug功能:支持下载原始对话、可视化对话分支等

本文原文来自DataLearnerAI官方网站&#xff1a;ChatGPT内置隐藏debug功能&#xff1a;支持下载原始对话、可视化对话分支等 | 数据学习者官方网站(Datalearner) AIPRM的工作人员最近发现ChatGPT的客户端隐藏内置了一个新的debug特性&#xff0c;可以提高ChatGPT对话的问题调试…

【学习笔记】LLM for Education

ChatGPT has entered the classroom: how LLMs could transform education 前言IntroductionThe risks are realEmbracing LLMsIntroducing the AI tutorAugmenting retrievalWill it catch on?总结 前言 一篇来自Nature的文章&#xff0c;探讨了教育行业的不同参与者&#x…

harmony开发之Text组件的使用

TextInput、TextArea是输入框组件&#xff0c;通常用于响应用户的输入操作&#xff0c;比如评论区的输入、聊天框的输入、表格的输入等&#xff0c;也可以结合其它组件构建功能页面&#xff0c;例如登录注册页面。 图片来源黑马程序员 Text组件的使用&#xff1a; 文本显示组…

Elasticsearch:什么是检索增强生成 (RAG)?

检索增强生成 (RAG) 定义 检索增强生成 (RAG) 是一种利用来自私有或专有数据源的信息来补充文本生成的技术。 它将旨在搜索大型数据集或知识库的检索模型与大型语言模型 (LLM) 等生成模型相结合&#xff0c;后者获取该信息并生成可读的文本响应。 检索增强生成可以通过添加来…

STC15F100E单片机模拟串口

文章目录 一、芯片简介二、开发环境三、软件模拟串口参考 一、芯片简介 STC15F100系列单片机是宏晶科技生产的单时钟/机器周期(1T)的单片机&#xff0c;新一代8051单片机&#xff0c;指令代码完全兼容传统8051&#xff0c;但是速度快6-12倍。 内部集成R/C时钟&#xff0c;5MHz…

Vue3拖拽功能【VueDraggablePlus】

一、介绍 由于 Sortablejs 的 vue3 组件一直没有更新&#xff0c;已经跟 vue3 严重脱节&#xff0c;所以诞生了这个项目&#xff0c;这个组件是基于 Sortablejs 的&#xff0c;所以如果你想了解更多关于 Sortablejs 的信息&#xff0c;可以查看Sortablejs 官网。VueDraggableP…

Markdown从入门到精通

Markdown从入门到精通 文章目录 Markdown从入门到精通前言一、Markdown是什么二、Markdown优点三、Markdown的基本语法3.1 标题3.2 字体3.3 换行3.4 引用3.5 链接3.6 图片3.7 列表3.8 分割线3.9 删除线3.10 下划线3.11 代码块3.12 表格3.13 脚注3.14 特殊符号 四、Markdown的高…

拼多多商品详情数据接口在数据分析行业的作用性

在数据分析行业中&#xff0c;拼多多商品详情数据的作用性主要体现在以下几个方面&#xff1a; 了解市场和用户需求&#xff1a;通过拼多多商品详情数据&#xff0c;企业可以了解到市场上什么产品受欢迎&#xff0c;用户对产品的反馈和评价如何&#xff0c;从而调整自己的销售…

js获取快递单号小练习

目录 1、css代码 2、html代码 3、js代码 完整代码 效果图 1、css代码 .box{width: 400px;height: 300px;margin: 100px auto;position: relative;}input{width: 250px;height: 40px;outline: none;}span{display: block;position: absolute;min-width: 270px;max-width: 40…