导读

前面我们介绍了使用FFmpeg解码视频,今天我们使用FFmpeg解码音频。我们的目标将mp4中的音频文件解码成PCM数据,并输出到本地文件,然后使用ffplay播放验证。

音频的解码过程就是将经过压缩后的数据重新还原成原始的PCM声音信号的过程。对于音频解码所用到的API和视频解码是一样的。

PCM基础知识

PCM是指未经过压缩的原始声音脉冲信号数据,它主要通过采样率、采样格式(比如每个采样点是8位、16位、32位等)、声道数来描述。

在FFmpeg中有两种表示PCM数据包的模式,分别是planer和packed模式,那么它们有什么区别呢?
其中packed又叫做交错模式,而planer又叫平面模式,所谓交错或平面就是不同声道的声音信号排列储存的方式,例如对于一个双声道的PCM数据来说,
用packed模式表示是这样子的:

// 我们用L表示左声道数据,用R表示右声道数据
LRLRLRLRLRLRLRLR

而用laner模式表示的话,则是这样子的:

// 我们用L表示左声道数据,用R表示右声道数据
LLLLLLLL RRRRRRRR

在FFmpeg中,packed模式的格式有:

AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits
AV_SAMPLE_FMT_S16,         ///< signed 16 bits
AV_SAMPLE_FMT_S32,         ///< signed 32 bits
AV_SAMPLE_FMT_FLT,         ///< float
AV_SAMPLE_FMT_DBL,         ///< double

它的数据只存在于AVFrame的data[0]中。

而planer模式一般是FFmpeg内部储存音频所使用的模式,例如通过一般planar模式的后面都有字母P标识,planar模式的格式有:

AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
AV_SAMPLE_FMT_FLTP,        ///< float, planar
AV_SAMPLE_FMT_DBLP,        ///< double, planar
AV_SAMPLE_FMT_S64,         ///< signed 64 bits
AV_SAMPLE_FMT_S64P,        ///< signed 64 bits, planar

例如对于一帧planar格式的双声道的音频数据,AVFrame中的data[0]表示左声道的数据,data[1]表示的是右声道的数据。

在FFmpeg中我们可以使用函数av_sample_fmt_is_planar来判断采样格式是planar模式还是packed模式。

需要注意的一点是planar仅仅是FFmpeg内部使用的储存模式,我们实际中所使用的音频都是packed模式的,也就是说我们使用FFmpeg解码出音频PCM数据后,如果需要写入到输出文件,应该将其转为packed模式的输出。

我们可以使用ffplay播放PCM原始音频数据,命令是:

// -ar 表示采样率
// -ac 表示音频通道数
// -f 表示 pcm 格式,sample_fmts + le(小端)或者 be(大端)  f32le表示的是 AV_SAMPLE_FMT_FLTP 的小端模式
// sample_fmts可以通过ffplay -sample_fmts来查询
// -i 表示输入文件,这里就是 pcm 文件
ffplay -ar 44100 -ac 2 -f f32le -i pcm文件路径

音频解码

直接上代码吧,有注释:

class AudioDecoder {public:AudioDecoder();~AudioDecoder();void decode_audio(std::string media_path,std::string pcm_path);};

以下是实现文件:

#include "AudioDecoder.h"extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}AudioDecoder::AudioDecoder() {}AudioDecoder::~AudioDecoder() {}void AudioDecoder::decode_audio(std::string media_path, std::string pcm_path) {AVFormatContext *avFormatContext = avformat_alloc_context();avformat_open_input(&avFormatContext, media_path.c_str(), nullptr, nullptr);int audio_index = av_find_best_stream(avFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);if (audio_index < 0) {std::cout << "没有找到可用的音频流" << std::endl;// todo 如果找不到可以遍历 avFormatContext->streams的codec type是否是音频来再次寻找} else {// 打印媒体信息av_dump_format(avFormatContext, 0, media_path.c_str(), 0);// 初始化解码器相关const AVCodec *audio_codec = avcodec_find_decoder(avFormatContext->streams[audio_index]->codecpar->codec_id);if(nullptr == audio_codec){std::cout << "没找到对应的解码器:"  << std::endl;return;}AVCodecContext *codec_ctx = avcodec_alloc_context3(audio_codec);// 如果不加这个可能会 报错Invalid data found when processing inputavcodec_parameters_to_context(codec_ctx,avFormatContext->streams[audio_index]->codecpar);// 打开解码器int ret = avcodec_open2(codec_ctx, audio_codec, NULL);if (ret < 0) {std::cout << "解码器打开失败:"  << std::endl;return;}// 初始化包和帧数据结构AVPacket *avPacket = av_packet_alloc();av_init_packet(avPacket);AVFrame *frame = av_frame_alloc();std::cout << "sample_fmt:"  << codec_ctx->sample_fmt << std::endl;std::cout << "AV_SAMPLE_FMT_U8:"  << AV_SAMPLE_FMT_U8 << std::endl;std::cout << "采样率sample_fmt:"  << codec_ctx->sample_fmt << std::endl;FILE *audio_pcm = fopen(pcm_path.c_str(), "wb");while (true) {ret = av_read_frame(avFormatContext, avPacket);if (ret < 0) {std::cout << "音频读取完毕" << std::endl;break;} else if(audio_index == avPacket->stream_index){ // 过滤音频ret = avcodec_send_packet(codec_ctx, avPacket);if(ret == AVERROR(EAGAIN)) {std::cout << "发送解码EAGAIN:" << std::endl;} else if(ret < 0) {char error[1024];av_strerror(ret,error,1024);std::cout << "发送解码失败:"  << error << std::endl;return;}while (true) {ret = avcodec_receive_frame(codec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret < 0) {std::cout << "音频解码失败:" << std::endl;return;}// 每帧音频数据量的大小int data_size = av_get_bytes_per_sample(codec_ctx->sample_fmt);/*** P表示Planar(平面),其数据格式排列方式为 :LLLLLLRRRRRRLLLLLLRRRRRRLLLLLLRRRRRRL...(每个LLLLLLRRRRRR为一个音频帧)而不带P的数据格式(即交错排列)排列方式为:LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRL...(每个LR为一个音频样本)播放范例:   ffplay -ar 44100 -ac 2 -f f32le pcm文件路径并不是每一种都是这样的格式*//*** ffplay -ar 44100 -ac 2 -f f32le -i pcm文件路径-ar 表示采样率-ac 表示音频通道数-f 表示 pcm 格式,sample_fmts + le(小端)或者 be(大端)sample_fmts可以通过ffplay -sample_fmts来查询-i 表示输入文件,这里就是 pcm 文件**/const char *fmt_name = av_get_sample_fmt_name(codec_ctx->sample_fmt);AVSampleFormat pack_fmt = av_get_packed_sample_fmt(codec_ctx->sample_fmt);std::cout << "fmt_name:" << fmt_name << std::endl;std::cout << "pack_fmt:" << pack_fmt << std::endl;std::cout << "frame->format:" << frame->format << std::endl;if (av_sample_fmt_is_planar(codec_ctx->sample_fmt)) {std::cout << "pcm planar模式" << std::endl;for (int i = 0; i < frame->nb_samples; i++) {for (int ch = 0; ch < codec_ctx->channels; ch++) {// 需要储存为pack模式fwrite(frame->data[ch] + data_size * i, 1, data_size, audio_pcm);}}} else {std::cout << "pcm Pack模式" << std::endl;fwrite(frame->data[0], 1, frame->linesize[0], audio_pcm);}}} else{av_packet_unref(avPacket); // 减少引用计数}}}
}

todo

析构函数释放资源,时间篇幅问题,就不写了。。。

推荐阅读

FFmpeg连载1-开发环境搭建
FFmpeg连载2-分离视频和音频
FFmpeg连载3-视频解码

关注我,一起进步,人生不止coding!!!

FFmpeg连载4-音频解码相关推荐

  1. FFmpeg连载3-视频解码

    导读 在前面我们介绍了FFmpeg的解封装,并且实现了提取视频文件中的音频流和视频流单独输出,使用ffplay播放验证, 今天我们使用FFmpeg解码视频流,将视频解码为YUV并输出到文件,然后使用f ...

  2. 【FFmpeg杂记】音频解码输出PCM格式数据分析

      FFmpeg音频解码后输出的为PCM数据,PCM中的声音数据没有被压缩.   FFmpeg中音视频数据基本上都有Packed和Planar两种存储方式,对于双声道音频来说,Packed方式为两个声 ...

  3. audioread-支持多种解码 (GStreamer + Core Audio + MAD + FFmpeg) 的python音频解码库

    解码支持 使用任何可用的后端解码音频文件都是支持的.该库当前支持: 通过PyGObject的Gstreamer. 通过ctypes在Mac OS X上的核心音频.(不需要PyObjC) 通过pymad ...

  4. ffmpeg C代码音频解码

    生成原始PCM数据 #include <stdio.h> #include <stdlib.h> #include <libavcodec/avcodec.h> # ...

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

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

  6. FFmpeg连载7-mp3转码aac及AVAudioFifo的使用

    前言 如今以抖音.快手为代表的短视频秀无处不在,比如它们一个很普通的功能就是使用流行音乐替换作为视频的背景音乐.而在视频中音频一般都是以AAC的形成存在,但流行音乐大多以mp3的格式传播, 因此需要完 ...

  7. FFmpeg 开发(04):FFmpeg + OpenGLES 实现音频可视化播放

    本文基于上一篇文章 FFmpeg + OpenSLES 实现音频解码播放 ,利用 FFmpeg 对一个 Mp4 文件的音频流进行解码,然后将解码后的 PCM 音频数据进行重采样,最后利用 OpenSL ...

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

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

  9. 基于 FFMPEG 的音频编解码(二):音频解码

    音频解码 基于 FFMPEG 的音频编解码(一):Hello FFMPEG,安装与编译 基于 FFMPEG 的音频编解码(二):音频解码 基于 FFMPEG 的音频编解码(三):音频编码 在 Hell ...

最新文章

  1. sdn主要包含哪些接口_SDN 是什么?
  2. 【高清大图下载】机器学习从入门到精通思维导图
  3. 影响SEO网站文章收录的因素有哪些?
  4. 1135 Is It A Red-Black Tree (30 分)【难度: 难 / 知识点: 红黑树 未完成】
  5. 关于Apache虚拟主机的设置
  6. 【含义解析】%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %m%n
  7. markdown 流程图js_MarkDown 流程图示例
  8. python2.0_day19_后台数据库设计思路
  9. php单引号中变量,php中单引号双引号那点事---顺便说说把php变量的值传给js
  10. 计算机网络与综合布线系统设计,浅谈计算机网络综合布线系统设计
  11. python数字猜大小游戏
  12. Cocos Creator接入谷歌广告AdMob详细教程(原创)
  13. 编写函数求整形数组a中存储的m个不重复的整数的第k大的整数(其中m=1,1=k=m)很简单的一个思路是酱紫的:管他辣么多干啥,上来一把排序然后直接得答案...
  14. 微信小程序推广方式有哪些?
  15. kindle如何设置不闪屏_kindle闪屏怎么解决
  16. Unity 编辑器下运行没有声音
  17. JavaScript RegExp 正则对象
  18. Crd(自定义资源类型)2021.12.05
  19. CTime/COleDateTime::Format方法的使用
  20. HDU 1002 A+BII大数

热门文章

  1. 如何删除我的电脑里的优酷影视库
  2. struct model in Python
  3. 编译原理 - 词法分析(Lexical Analysis)
  4. 求生模型预览及模型地址(部分)[v2.1.2.1]
  5. spell 命令 查找英语语言拼写错误。
  6. phpcms模板页面命名规则
  7. 剧情介绍:“阿甘正传”
  8. 完全二叉树总结点叶子结点计算公式题型总结--技术岗笔试(持续更新)
  9. 2021年茶艺师(中级)考试题及茶艺师(中级)复审考试
  10. 网站维护协议书——我的模板