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

news/2025/1/12 17:37:11/

最简单的基于 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;// 采样率:44100int 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 的解决方法

工程文件下载

GitHub:UestcXiye / Simplest-FFmpeg-Audio-Decoder

CSDN:Simplest FFmpeg Audio Decoder.zip


http://www.ppmy.cn/news/1270800.html

相关文章

第16节:Vue3 响应式对象reactive()

在UniApp中使用Vue3框架时&#xff0c;你可以使用reactive()函数来创建一个响应式对象。reactive()函数返回一个响应式引用对象&#xff0c;它包装了一个普通的对象&#xff0c;使得该对象能够成为响应式数据源。 下面是一个示例&#xff0c;演示了如何在UniApp中使用Vue3框架…

什么是数据可视化?数据可视化的优势、方法及示例

前言 在当今的数字时代&#xff0c;数据是企业和组织的命脉&#xff0c;生成的数据量呈指数级增长。这种被称为大数据的海量数据在洞察力和决策方面具有巨大的潜力。然而&#xff0c;如果没有一种有效的方法来分析和理解这些数据&#xff0c;它就会变得毫无意义和难以管理。这就…

如何在Kali Linux安装Xrdp+cpolar内网穿透实现远程访问Kali系统

文章目录 前言1. Kali 安装Xrdp2. 本地远程Kali桌面3. Kali 安装Cpolar 内网穿透4. 配置公网远程地址5. 公网远程Kali桌面连接6. 固定连接公网地址7. 固定地址连接测试 前言 Kali远程桌面的好处在于&#xff0c;它允许用户从远程位置访问Kali系统&#xff0c;而无需直接物理访…

FPGA巩固基础:秒表的设计

设计要求&#xff1a; 6位8段数码管&#xff0c;低三位显示毫秒计数&#xff0c;最高位显示分钟&#xff0c;其余两位显示秒计数。 开始案件与暂停按键&#xff0c;复位按键直接全部归零。 扩展部分&#xff1a;每计满一次&#xff0c;led移位一次。 框图设计&#xff1a; …

【反射】Java小白也能手写的简易版框架

文章目录 1. 概要2. 反射的基本使用2.1 获取类的字节码2.2 反射获取构造器2.3 反射获取构造器的作用2.4 反射获取成员变量及其使用2.5 反射获取成员方法 3. 基于反射手写简易版框架 1. 概要 在日常实习开发中&#xff0c;反射基本其实是用不太到的。但是如果你想学习有些框架的…

第P7周:咖啡豆识别(VGG-16复现)

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营](https://mp.weixin.qq.com/s/rbOOmire8OocQ90QM78DRA) 中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊 | 接辅导、项目定制](https://mtyjkh.blog.csdn.net/)** 一、前期工作 import …

明明随机数

明明想在学校中请一些同学一起做一项问卷调查&#xff0c;为了实验的客观性&#xff0c;他先用计算机生成了N个1到1000之间的随机整数(N<100)&#xff0c;对于其中重复的数字&#xff0c;只保留一个&#xff0c;把其余相同的数去掉&#xff0c;不同的数对应着不同的学生的学…

一个简单的cmake模板(C++)

链接&#xff1a;小黑屋1024 / Python GitCode #对cmake版本的要求&#xff0c;此处不低于3.16 cmake_minimum_required(VERSION 3.16)#项目名称&#xff1a;此处为test project(test)#设置编译生成产物输出路径 ##可执行文件exe SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURC…