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的方式,应该是比这个方便很多。

ffmpeg 音频解码一相关推荐

  1. ffmpeg中音频解码方法(附代码)+ffmpeg音频解码播放速度快的问题(随手笔记,以供查阅)

    最近在做一款取名为变速不变调播放器的时候,解码音频遇到了些问题(ffmpeg音频解码播放速度快的问题),网络上的方法对绝大多数的音视频文件有效,但是对于某些音频会有问题,比如某些ADPCM编码的WAV ...

  2. ffmpeg 音频解码二

    1. ffmpeg 视频解码一 2. ffmpeg 视频解码二 3. ffmpeg 音频解码一 4. ffmpeg 音频解码二 5. ffmpeg 音视频解码 6. ffmpeg 视频编码一 7. f ...

  3. FFMPEG音频解码浅析

    结合各种资料和自己的理解,估计有些浅显. FFMPEG解码流程: 1. 注册所有容器格式和CODEC: av_register_all() 2. 打开文件: av_open_input_file() ...

  4. FFmpeg音频解码流程详解及简单demo参考

    本文主要讲解FFmpeg的音频解码具体流程,API使用.最后再以一个非常简单的demo演示将一个mp3格式的音频文件解码为原始数据pcm文件. 本文主要基于FFmpeg音频解码新接口. 一.FFmpe ...

  5. 深入浅出:FFmpeg 音频解码与处理AVFrame全解析

    深入浅出:FFmpeg 音频解码与处理全解析 一.FFmpeg 简介 1.1 FFmpeg 的历史与发展 1.2 FFmpeg 的主要组成部分 二.音频编解码基础 (Basics of Audio E ...

  6. Android NDK开发之旅31 FFmpeg音频解码

    ###前言 #####基于Android NDK开发之旅30--FFmpeg视频播放这篇文章,我们已经学会视频解码基本过程.这篇文章就对音频解码进行分析. #####音频解码和视频解码的套路基本是一样 ...

  7. FFmpeg音频解码-音频可视化

    最近在做一个音频可视化的业务,网上有Java层的实现方法,但是业务需要用C实现,从原理出发其实很简单,先对音频进行解码,再计算分贝.这比把大象放进冰箱还简单.本文从音频可视化的业务为依托,以FFmpe ...

  8. FFmpeg - 音频解码过程

    1. 注册所有解码器  av_register_all(); 2. Codec & CodecContext AVCodec* codec = avcodec_find_decoder(COD ...

  9. ffmpeg音频解码重采样(立体声转成单声道)

    https://blog.csdn.net/qq_37003193/article/details/93870141 PCM双声道分离为单声道 https://blog.csdn.net/chinab ...

最新文章

  1. 关键词排名下降怎么办-优八学院给你支招
  2. Kali Linux下运行Sniffjoke出错的解决
  3. IIS5.1错误,启动时WEB服务提示:服务器没有及时响应启动或控制请求 之终极解决方案。...
  4. 原神:创新和成功有目共睹,游戏可玩性历久弥新,戒骄戒躁保初心
  5. 1.void main
  6. 应用程序利用ADO对象访问数据库
  7. python入门练习题3(函数)
  8. 查期刊是否开源_新期刊HardwareX促进科学的开源硬件
  9. EditText常用属性总结
  10. c语言 void fun(float *p1),C语言程序设计试题1
  11. 洛谷P1273 有线电视网
  12. 野火stm32资源下载(视频,手册等)
  13. 成功在虚拟机里面安装MAC苹果系统
  14. 企业为什么要选择人力外包?好处与坏处
  15. php不是当前时间,php取得时间与当前时间不一样
  16. 如何在官网下载tomcat
  17. JAVA--线程同步的三种方法
  18. html提取excel指定单元格数据,如何利用Excel函数公式快速提取出单元格中指定的数据...
  19. stm32中用到的实时系统_基于STM32平台的实时操作系统
  20. 免费jpg转换成pdf格式的软件

热门文章

  1. 安卓文件传输工具 Android File Transfer mac
  2. 统计学中基础概念说明
  3. 数据挖掘导论Pangaea-Ning Tan 读书笔记——(第一,二,三章)
  4. 睿智的目标检测——YoloV7-Tiny-OBB旋转目标检测算法部署
  5. cmd命令创建文件文件夹
  6. 零窗口探测怎么抓包_易语言防止助手探测窗口的代码
  7. CPU 型号后所代表的字母的意思
  8. bat执行cmd命令
  9. 如何在sqlserver中写存储过程
  10. 学习通信原理之——从实验中理解频谱/功率谱/功率谱密度(MATLAB演示)