ffmpeg 音频解码一

article/2025/10/22 20:37:05

1. ffmpeg 视频解码一
2. ffmpeg 视频解码二
3. ffmpeg 音频解码一
4. ffmpeg 音频解码二
5. ffmpeg 音视频解码
6. ffmpeg 视频编码一
7. ffmpeg 视频编码一(精简版)
8. ffmpeg 视频编码二(基于 libswscale 转换视频)
9. ffmpeg 过滤器libavfilter的使用
10. ffmpeg 视频编码三(基于 libavfilter 转换视频)

前言

前面已经介绍了视频的解码流程,这篇开始就开始音频解码了,同样是两篇,一篇使用parser解析器做解析,一篇按常规流程处理。

一些基础知识

  1. 采样率(sample_rate):

    即取样频率,定义了每秒从连续信号中提取并组成离散信号的采样个数,它用赫兹(Hz)来表示。采样频率的倒数是采样周期或者叫作采样时间,它是采样之间的时间间隔。通俗的讲采样频率是指计算机每秒钟采集多少个信号样本。

  2. 采样数(frame_size):

    一帧音频的大小。

  3. 采样格式(sample_fmt):

    音频sample的存储格式。

    可以使用8位无符号整数、16位有符号整数、32位有符号整数以及单精度浮点数,双精度浮点数表示一个采样。但是,没有使用24位的有符号整数,这是因为这些不同的格式使用的是原生的C类型,而C中是没有24位的长度的类型的。

    我们可以使用以下命令查看ffmpeg支持的格式:

    ffplay -sample_fmts
    

    当然也可查看源码,这里贴出 SampleFmtInfo(包含AVSampleFormat相关转化的信息)结构体的源代码:

     static const SampleFmtInfo sample_fmt_info[AV_SAMPLE_FMT_NB] = {[AV_SAMPLE_FMT_U8]   = { .name =   "u8", .bits =  8, .planar = 0, .altform = AV_SAMPLE_FMT_U8P  },[AV_SAMPLE_FMT_S16]  = { .name =  "s16", .bits = 16, .planar = 0, .altform = AV_SAMPLE_FMT_S16P },[AV_SAMPLE_FMT_S32]  = { .name =  "s32", .bits = 32, .planar = 0, .altform = AV_SAMPLE_FMT_S32P },[AV_SAMPLE_FMT_S64]  = { .name =  "s64", .bits = 64, .planar = 0, .altform = AV_SAMPLE_FMT_S64P },[AV_SAMPLE_FMT_FLT]  = { .name =  "flt", .bits = 32, .planar = 0, .altform = AV_SAMPLE_FMT_FLTP },[AV_SAMPLE_FMT_DBL]  = { .name =  "dbl", .bits = 64, .planar = 0, .altform = AV_SAMPLE_FMT_DBLP },[AV_SAMPLE_FMT_U8P]  = { .name =  "u8p", .bits =  8, .planar = 1, .altform = AV_SAMPLE_FMT_U8   },[AV_SAMPLE_FMT_S16P] = { .name = "s16p", .bits = 16, .planar = 1, .altform = AV_SAMPLE_FMT_S16  },[AV_SAMPLE_FMT_S32P] = { .name = "s32p", .bits = 32, .planar = 1, .altform = AV_SAMPLE_FMT_S32  },[AV_SAMPLE_FMT_S64P] = { .name = "s64p", .bits = 64, .planar = 1, .altform = AV_SAMPLE_FMT_S64  },[AV_SAMPLE_FMT_FLTP] = { .name = "fltp", .bits = 32, .planar = 1, .altform = AV_SAMPLE_FMT_FLT  },[AV_SAMPLE_FMT_DBLP] = { .name = "dblp", .bits = 64, .planar = 1, .altform = AV_SAMPLE_FMT_DBL  },
    };
    

    其中name为格式名称,bits是在计算机中所占的位数,plannar是文件存储方式,altform是获取文件根据存储方式不同时相应的名称(例:u8 是 plannar=0 的格式 ,转换为 plannar=1 时 即是 u8p)。

    sample有两种类型的存储方式:平面(planar)和打包(packed),在planar中每一个通道独自占用一个存储平面;在packed中,所有通道的sample交织存储在同一个平面。

  4. 声道信息:

    channels 为 音频的 通道数 1 2 3 4 5…
    channel_layout 为音频 通道格式类型 如 单通道 双通道 …

    对于单声道声音文件,采样数据为八位的短整数(short int 00H-FFH);

    而对于双声道立体声声音文件,每次采样数据为一个16位的整数(int),高八位(左声道)和低八位(右声道)分别代表两个声道。

    如果是双声道(stereo),采样就是双份的,文件也差不多要大一倍。

音频信息

如果音频,样本:fltp;采样率:44100;声道:2。
av_get_bytes_per_sample(fltp) == 4;

  1. AAC(nb_samples和frame_size = 1024)
    则可以得到一帧音频的大小为:
    4 * 2 * 1024 = 8192(字节)
    一帧的播放时间是
    1024*1000000/44100= 46.43ms

  2. MP3(nb_samples和frame_size = 1152)
    则可以得到一帧音频的大小为:
    4 * 2 * 1152= 9216(字节)
    一帧的播放时间是
    1152*1000000/44100= 52.24ms

流程图

在这里插入图片描述

代码流程即如流程图所示,下面讲解一下当中部分函数的作用。

  1. av_parser_init
    这是一个解析器,我们根据解码器,实例化这个解析器,后面解析数据时使用。
  2. av_parser_parse2
    我们从输入文件得到的原始数据(不适用ffmpeg自带的api的话),直接使用是不行的,此时我们就需要把这个原始数据使用上面实例化的解析器来解析,把数据分割成帧,为后面解码数据做准备。
  3. avcodec_send_packet
    发送我们刚刚得到的解析数据到解码器做解码。
  4. avcodec_receive_frame
    获取解码之后的数据。

源码


#pragma once
#define __STDC_CONSTANT_MACROS
#define _CRT_SECURE_NO_WARNINGSextern "C"
{
#include "libavcodec/avcodec.h"
}//缓冲区大小(缓存5帧数据)
#define AUDIO_INBUF_SIZE 40960  
/*name   depthu8        8s16      16s32      32flt      32dbl      64u8p       8s16p     16s32p     32fltp     32dblp     64s64      64s64p     64//此代码解码的音频文件格式如下://AAC文件(一帧1024字节),双声道(2),FLTP(32位,4字节)//AAC文件 frame_size 和 nb_samples 大小均为1024//一帧音频所占字节大小//1024*2*4=8192字节
*/
#define AUDIO_REFILL_THRESH 8192using namespace std;#define INPUT_FILE_NAME "lh_online.aac"
#define OUTPUT_FILE_NAME "lh_online.pcm"static int get_format_from_sample_fmt(const char** fmt,enum AVSampleFormat sample_fmt)
{int i;struct sample_fmt_entry {enum AVSampleFormat sample_fmt; const char* fmt_be, * fmt_le;} sample_fmt_entries[] = {{ AV_SAMPLE_FMT_U8,  "u8",    "u8"    },{ AV_SAMPLE_FMT_S16, "s16be", "s16le" },{ AV_SAMPLE_FMT_S32, "s32be", "s32le" },{ AV_SAMPLE_FMT_FLT, "f32be", "f32le" },{ AV_SAMPLE_FMT_DBL, "f64be", "f64le" },};*fmt = NULL;for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {struct sample_fmt_entry* entry = &sample_fmt_entries[i];if (sample_fmt == entry->sample_fmt) {*fmt = AV_NE(entry->fmt_be, entry->fmt_le);return 0;}}av_log(NULL, AV_LOG_ERROR, "sample format %s is not supported as output format\n", av_get_sample_fmt_name(sample_fmt));return -1;
}static void decode(AVCodecContext* dec_ctx, AVFrame* frame, AVPacket* pkt,FILE* ofile)
{int i, ch;int ret, data_size;ret = avcodec_send_packet(dec_ctx, pkt);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "发送数据包到解码器出错。\n");exit(1);}while (ret >= 0) {ret = avcodec_receive_frame(dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return;else if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Error sending a packet for decoding.\n");exit(1);}printf("frame_number: %d \n", dec_ctx->frame_number);//获取每个采样点当中每个声道的大小data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);if (data_size < 0) {av_log(NULL, AV_LOG_ERROR, "Failed to calculate data size.\n");exit(1);}//遍历采样点for (i = 0; i < frame->nb_samples; i++) {//遍历声道for (ch = 0; ch < dec_ctx->channels; ch++) {fwrite(frame->data[ch] + data_size * i, 1, data_size, ofile);}}}
}int main(int argc, char* argv[])
{const AVCodec* codec;AVCodecParserContext* parser;AVCodecContext* c = NULL;FILE* ifile, * ofile;AVFrame* frame;AVPacket* pkt;uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];uint8_t* data;size_t   data_size;int ret,len;enum AVSampleFormat sfmt;const char* fmt;//初始化inbuf数字默认值memset(inbuf + AUDIO_INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);//获取解码器(此处需要读取的文件是AAC,故)codec = avcodec_find_decoder(AV_CODEC_ID_AAC);if (!codec) {av_log(NULL, AV_LOG_ERROR, "Codec not found.\n");exit(1);}//注册解析器parser = av_parser_init(codec->id);if (!parser) {av_log(NULL, AV_LOG_ERROR, "parser not found.\n");exit(1);}//分配解析器上下文c = avcodec_alloc_context3(codec);if (!c) {av_log(NULL, AV_LOG_ERROR, "Could not allocate video codec context.\n");exit(1);}//打开解码器if (avcodec_open2(c, codec, NULL) < 0) {av_log(NULL, AV_LOG_ERROR, "Could not open codec.\n");exit(1);}//分配AVPacketpkt = av_packet_alloc();if (!pkt) {exit(1);}//分配AVFrameframe = av_frame_alloc();if (!frame) {exit(1);}//打开输入文件ifile = fopen(INPUT_FILE_NAME, "rb");if (!ifile) {av_log(NULL, AV_LOG_ERROR, "Could not open \s.\n", INPUT_FILE_NAME);exit(1);}//打开输入文件ofile = fopen(OUTPUT_FILE_NAME, "wb+");if (!ofile) {av_log(NULL, AV_LOG_ERROR, "Could not open \s.\n", OUTPUT_FILE_NAME);exit(1);}//从输入流 ifile 读取数据到 inbuf 所指向的数组中data = inbuf;data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, ifile);while (data_size > 0) {//使用注册的解析器 parser 把数据分割成帧ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,data, data_size,AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);if (ret < 0) {fprintf(stderr, "Error while parsing\n");exit(1);}//根据使用情况重置数据位置data += ret;data_size -= ret;//送往解码if (pkt->size)decode(c, frame, pkt, ofile);//判断缓存区剩余数据是否小于一帧音频大小//小于的话从文件继续读取,之后在送往解码if (data_size < AUDIO_REFILL_THRESH) {memmove(inbuf, data, data_size);data = inbuf;len = fread(data + data_size, 1,AUDIO_INBUF_SIZE - data_size, ifile);if (len > 0)data_size += len;}}//flush 解码器decode(c, frame, NULL, ofile);//此时就已经解码完了,我们稍后使用ffplay播放下音频//解码出来的pcm数据是没有这些基础数据的,我们需要从元数据获取//打印下基本信息//声道数printf("channels: %d \n", c->channels);  //采样率printf("sample_rate: %d  \n", c->sample_rate);  //一帧音频所占字节代销printf("buffer: %d  \n", av_samples_get_buffer_size(NULL, c->channels, c->frame_size, c->sample_fmt, 1));//采样格式sfmt = c->sample_fmt;printf("sample_fmt: %s  \n", av_get_sample_fmt_name(sfmt));//如果为planar,转换为packed格式if (av_sample_fmt_is_planar(sfmt)) {const char* packed = av_get_sample_fmt_name(sfmt);sfmt = av_get_packed_sample_fmt(sfmt);}if (get_format_from_sample_fmt(&fmt, sfmt) < 0) {av_log(NULL, AV_LOG_ERROR, "Could not get forma \s.\n", av_get_sample_fmt_name(sfmt));exit(1);}//打印播放命令printf("Play the output audio file with the command:\n""ffplay -f %s -ac %d -ar %d %s\n",fmt, c->channels, c->sample_rate,OUTPUT_FILE_NAME);//资源释放fclose(ifile);fclose(ofile);av_parser_close(parser);avcodec_free_context(&c);av_frame_free(&frame);av_packet_free(&pkt);return 0;
}

此实例演示了一个将aac文件解码成pcm文件的流程。
打印信息如下:

在这里插入图片描述
可见待解码文件是一个 有2个声道,采样率为44100HZ,采样格式为fltp的文件,共有1478帧。

接下来使用命令播放我们解码出来的音频试试:

ffplay -f f32le -ac 2 -ar 44100 lh_online.pcm

结果:
在这里插入图片描述
此时你应该能听到播放的音频声音,大功告成。

到此,基于parser解析器解码音频的方式就讲述完了。
下一篇和视频一样将讲述纯基于API的方式,应该是比这个方便很多。


http://chatgpt.dhexx.cn/article/ZAW76qJg.shtml

相关文章

MP3音频解码详细过程(二)

最近做了无人机的音频解码&#xff0c;二周内从无任何音频基础到输出PCM数据码流到无人机上可以实现播报功能&#xff0c;其中遇到了许多莫名的坑&#xff0c;谨以此篇记录心得。也算交个作业。 架构设计思路&#xff1a;由usart 实时传输mp3音频码流&#xff0c;STM32F446 将…

低延迟音频中的音频解码优化策略

文章目录 前言音频播放举个例子&#xff1a;PortAudio回调函数解码与播放 优化策略1. 一次性读取音频到内存中2. MMAP3. 音频转码&#xff0c;再接 MMAP4. 解码缓冲 总结参考资料 前言 延迟是指信号在系统中传输所需的时间。下面是常见类型的音频应用相关延迟时间&#xff1a;…

音频编解码基础

1. PCM PCM 脉冲编码调制是Pulse Code Modulation的缩写。脉冲编码调制是数字通信的编码方式之一。主要过程是将话音、图像等模拟信号每隔一定时间进行取样&#xff0c;使其离散化&#xff0c;同时将抽样值按分层单位四舍五入取整量化&#xff0c;同时将抽样值按一组二进制码来…

音视频解码流程详解

1、解码整体流程 &#xff08;1&#xff09; 音频解码整体流程 &#xff08;2&#xff09;视频解码整体流程 2、FFmpeg音视频解码详细流程 3、关键数据结构 AVCodecParser&#xff1a;⽤于解析输⼊的数据流并把它分成⼀帧⼀帧的压缩编码数据。⽐较形象 的说法就是把⻓⻓的⼀…

FFmpeg 音频解码(秒懂)

1.简介 解码音频数据&#xff0c;如下图所示&#xff0c;把MP3或者AAC数据解码成原始的数据pcm。 2.流程 2.1在使用FFmpeg API之前&#xff0c;需要先注册API&#xff0c;然后才能使用API。当然&#xff0c;新版本的库不需要再调用下面的方法。 av_register_all() 2.2 构建输…

语音编解码技术演进和应用选型

本文来自现网易云音乐音视频实验室负责人刘华平在LiveVideoStackCon 2017大会上的分享&#xff0c;并由LiveVideoStack整理而成。分享中刘华平以时间为主线&#xff0c;讲述了语音编解码技术的演进路线及实际应用中的技术选型。 文 / 刘华平 整理 / LiveVideoStack 大家好&…

回访。

wyx 过来&#xff0c;还 sxt &#xff0c;拿走了几张碟。为此&#xff0c;特意收拾了房间。还是被说没地方坐。 由于事先约了 zhmm 吃饭&#xff0c;没调开时间&#xff0c;所以&#xff0c;五点多&#xff0c;大家就一起吃了。大青花&#xff0c;东北风味儿。餐厅在二楼&…

客户信息管理软件系统

拟实现一个基于文本界面的《客户信息管理软件》 进一步掌握编程技巧和调试技巧&#xff0c;熟悉面向对象编程 主要涉及以下知识点&#xff1a; ▶类结构的使用&#xff1a; ▶对象的创建与使用 ▶类的封装性 ▶声明和使用数组 ▶数组的插入、删除和替换 ▶关键字的使用&#xf…

企业如何通过CRM系统有效触达客户,获取潜在商机

“守株待兔”式坐等客户上门的时代了已经过去了&#xff0c;尤其是在存量时代&#xff0c;企业想要提高销售&#xff0c;扩大客源&#xff0c;就要不断的通过各种渠道来去拓展自己的客户和销路&#xff0c;而互联网时代&#xff0c;获客的渠道也丰富多样&#xff0c;企业选择好…

呼叫中心系统接入CRM客户管理系统

呼叫中心是企业与客户建立联系的桥梁&#xff0c;企业想要发展必须要有统一的客户管理系统&#xff0c;呼叫中心与客户管理系统对接到一起能够更高效管理客户&#xff0c;档案数据更准确。 在以前企业都是通过纸质的客户档案管理客户的&#xff0c;寻找某个客户时特别不方便&am…

CRM管理系统、教育后台、赠品管理、优惠管理、预约管理、试听课、教师、学生、客户、学员、商品管理、科目、优惠券、完课回访、客户管理系统、收费、退费、回访、账号权限、订单流水、Axure原型、rp原型

CRM管理系统、教育后台、赠品管理、优惠管理、预约管理、试听课、教师、学生、客户、学员、商品管理、科目、优惠券、完课回访、客户管理系统、收费、退费、回访、账号权限、订单流水、Axure原型、rp原型 Axure原型演示及下载地址&#xff1a;https://www.pmdaniu.com/storage…

在线云客服管理系统、会话管理、访客管理、客户管理、工单管理、会话记录、考勤统计、数据报表、工单设置、全局设置、转人工服务、自动回复、客户标签、客服监控、客服系统、前端会话、客服管理、在线客服、人工客服

在线云客服管理系统、会话管理、访客管理、客户管理、工单管理、会话记录、考勤统计、数据报表、工单设置、全局设置、转人工服务、自动回复、客户标签、客服监控、客服系统、前端会话、客服管理、在线客服 、人工客服 Axure原型演示及下载地址&#xff1a;Untitled Documenth…

CRM管理系统、教育后台、赠品管理、优惠管理、预约管理、试听课、教师、学生、客户、学员、商品管理、科目、优惠券、完课回访、客户管理系统、收费、退费、回访、账号权限、订单流水、审批、转账、rp原型

CRM管理系统、教育后台、赠品管理、优惠管理、预约管理、试听课、教师、学生、客户、学员、商品管理、科目、优惠券、完课回访、客户管理系统、收费、退费、回访、账号权限、订单流水、Axure原型、rp原型 Axure原型演示及下载地址&#xff1a;Untitled Documenthttps://f2b1hj…

客户管理系统

项目github地址&#xff1a;https://github.com/gh995836/crm 项目技术&#xff1a;SpringMVCSpringMybatisAjaxBootstrap 项目描述&#xff1a;该客户管理系统&#xff0c;前端采用Bootstrap框架 Ajax发送请求&#xff0c;后台采用JavaWeb的SpringMVCSpringMybatis框架进行…

“顾客总是对的”,客户满意从在线客服系统开始

"顾客总是对的"——马歇尔菲尔德 哈里戈登赛尔费里奇 毋庸置疑&#xff0c;赢得客户的青睐是维系自身经济长青的基础。想要客户满意&#xff0c;得到最佳的客户评价&#xff0c;企业就需要为客户提供超出他们期望的服务。 有人将客户服务分为三重境界:第一重境界&am…

一文读懂:客户管理系统平台是什么?有什么作用?

“客户管理系统平台是什么&#xff1f;” “客户管理系统平台有什么作用&#xff1f;在哪里可以应用&#xff1f;怎么用&#xff1f;” 经常可以听到企业内部关于客户管理系统平台的这些问题&#xff0c;本文将会为您一一解答&#xff1a; 一、客户管理系统平台是什么 顾名…

什么是客户自助服务门户及其搭建方法

随着信息技术的快速发展&#xff0c;越来越多的企业开始转向以客户为中心的服务模式&#xff0c;而客户自助服务门户&#xff08;Customer Self-Service Portal&#xff09;则成为了重要的服务方式。它可以让客户在不需要人工干预的情况下&#xff0c;自行解决问题&#xff0c;…

客户管理系统如何提升体验

数字化时代&#xff0c;客户与企业交互的触点爆炸式增长&#xff0c;客户体验正从单一触点走向端到端旅程。众多的产品、海量的数据&#xff0c;导致客户对体验的要求越来越多......CRM客户管理系统是企业提升客户体验的有效工具&#xff0c;它不仅可以帮助您进一步了解客户&am…

在线云客服管理系统、会话管理、访客管理、客户管理、工单管理、会话记录、考勤统计、数据报表、工单设置、全局设置、人工服务、自动回复、客户标签、客服监控、客服系统、前端会话、客服管理、在线客服 、人工客服

在线云客服管理系统、会话管理、访客管理、客户管理、工单管理、会话记录、考勤统计、数据报表、工单设置、全局设置、转人工服务、自动回复、客户标签、客服监控、客服系统、前端会话、客服管理、在线客服 、人工客服 Axure原型演示及下载地址&#xff1a;https://www.pmdani…

CRM客户管理系统

Sunsiny资产CRM客户管理系统 目录 一、 CRM系统背景 2 二、 CRM系统分类 3 三、概述 3 四、系统的技术选型 3 五、 系统模块简介 4 六、 领导驾驶舱 6 七、 客户经理驾驶舱 6 八、 客户管理 7 九、 联系人管理 8 十、 服务记录 8 十一、 公出计划 8 十二、 资管产品 9 十三、 …