FFMPEG4.1 - 音频解码
原文件为采样率44100,单声道,fltp音频格式的mp4文件
解码后,将fltp转变为s16标准的pcm格式
然后使用ffplay播放:ffplay -ar 44100 -ac 1 -f s16le -i 2.pcm
1、下面是使用swr_convert进行转换s16的代码
// 解码为PCM格式文件
// 检查解码内容,使用如下命令:
#include <memory>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
}
// 申请智能指针变量
#define NEW_PTR(T, P, V, Fn) T *P = V; std::shared_ptr<T> P##P(P, [&P](T *){if(P != nullptr){Fn;}})
// 打印异常信息,并退出main函数
#define FATAL(M, ...) printf(M, ##__VA_ARGS__); return -1
// 自定义变量
const char *src_media = "D:/1.mp4";
const char *dst_media = "D:/2.pcm";
// 定义要输出的pcm格式
#define PCM_FORMAT AV_SAMPLE_FMT_S16
#define PCM_CHANNELS 1
#define PCM_SAMPLE_RATE 44100
int main(int argc, char **argv) {
// 根据输入文件,打开媒体格式上下文
NEW_PTR(AVFormatContext, fmt_ctx, nullptr, avformat_close_input(&fmt_ctx));
if (avformat_open_input(&fmt_ctx, src_media, nullptr, nullptr) < 0) {
FATAL("open src media file failed.");
}
// 查询stream信息
if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
FATAL("find stream info failed.");
}
// 查询音频stream的索引
int audio_st_idx;
if ((audio_st_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0)) < 0) {
FATAL("find audio stream failed.");
}
// 通过索引找到音频stream
AVStream *audio_st;
audio_st = fmt_ctx->streams[audio_st_idx];
// 查询音频stream的解码器
AVCodec *audio_dec;
if ((audio_dec = avcodec_find_decoder(audio_st->codecpar->codec_id)) == nullptr) {
FATAL("find audio decoder failed.");
}
// 为解码器申请上下文
NEW_PTR(AVCodecContext, audio_dec_ctx, nullptr, avcodec_free_context(&audio_dec_ctx));
if ((audio_dec_ctx = avcodec_alloc_context3(audio_dec)) == nullptr) {
FATAL("allocate audio dec context failed.");
}
// 从音频stream中拷贝解码参数到解码器的上下文
if (avcodec_parameters_to_context(audio_dec_ctx, audio_st->codecpar) < 0) {
FATAL("copy codec params failed.");
}
// 打开解码器
if (avcodec_open2(audio_dec_ctx, audio_dec, nullptr) < 0) {
FATAL("codec open failed.");
}
// 申请一个packet,并初始化
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = nullptr;
pkt.size = 0;
// 申请一个frame
NEW_PTR(AVFrame, audio_frame, nullptr, av_frame_free(&audio_frame));
if ((audio_frame = av_frame_alloc()) == nullptr) {
FATAL("allocate audio frame failed.");
}
// 创建音频转换后的frame
NEW_PTR(AVFrame, convert_frame, nullptr, av_frame_free(&convert_frame));
if ((convert_frame = av_frame_alloc()) == nullptr) {
FATAL("allocate convert frame failed.");
}
convert_frame->format = PCM_FORMAT;
convert_frame->nb_samples = audio_dec_ctx->frame_size;
convert_frame->channel_layout = (uint64_t)av_get_default_channel_layout(PCM_CHANNELS);
convert_frame->sample_rate = PCM_SAMPLE_RATE;
if(av_frame_get_buffer(convert_frame, 0) < 0) {
FATAL("create convert frame buffer failed.");
}
// 创建swr的上下文
NEW_PTR(SwrContext, swr_ctx, swr_alloc(), swr_free(&swr_ctx));
swr_alloc_set_opts(swr_ctx,
convert_frame->channel_layout,
(AVSampleFormat)convert_frame->format,
convert_frame->sample_rate,
av_get_default_channel_layout(audio_dec_ctx->channels),
audio_dec_ctx->sample_fmt,
audio_dec_ctx->sample_rate,
0, nullptr);
swr_init(swr_ctx);
// 打开输出文件
NEW_PTR(FILE, output, nullptr, fclose(output));
if ((output = fopen(dst_media, "wb")) == nullptr) {
FATAL("no writable file.");
}
// 如果小于0,则表示读完了或者报错了,跳出循环
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
// 我们只关心音频stream
if (pkt.stream_index == audio_st_idx) {
// 发送一个音频包
if (avcodec_send_packet(audio_dec_ctx, &pkt) < 0) {
FATAL("send packet exception.");
}
// 接受解码完的内容
while (true) {
auto frame_ret = avcodec_receive_frame(audio_dec_ctx, audio_frame);
// 判断是否完全接受了frame
if (frame_ret == AVERROR(EAGAIN) || frame_ret == AVERROR_EOF) {
break;
}
// 检查是否接受异常
if (frame_ret < 0) {
FATAL("receive frame exception.");
}
// 转换音频frame
swr_convert(swr_ctx, convert_frame->data, convert_frame->nb_samples,
(const uint8_t **) audio_frame->data, audio_frame->nb_samples);
// 写入文件,如果是带P的音频格式,需要分通道写入文件
if (av_sample_fmt_is_planar((AVSampleFormat)convert_frame->format)) {
// 计算一个样本的大小
auto sampleBytes = av_get_bytes_per_sample((AVSampleFormat)convert_frame->format);
for (int i = 0; i < convert_frame->nb_samples; ++i) {
for (int ch = 0; ch < convert_frame->channels; ++ch) {
fwrite(convert_frame->data[ch] + i * sampleBytes, 1, (size_t)sampleBytes, output);
}
}
} else {
// 计算帧的大小
auto frameBytes = av_samples_get_buffer_size(nullptr, convert_frame->channels, convert_frame->nb_samples, (AVSampleFormat)convert_frame->format, 1);
fwrite(convert_frame->data[0], 1, (size_t)frameBytes, output);
}
av_frame_unref(audio_frame);
}
}
av_packet_unref(&pkt);
}
return 0;
}
2、下面是使用filter进行转换s16的代码
// 解码为PCM格式文件
// 检查解码内容,使用如下命令:
#include <memory>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/buffersink.h"
#include "libavutil/opt.h"
}
// 申请智能指针变量
#define NEW_PTR(T, P, V, Fn) T *P = V; std::shared_ptr<T> P##P(P, [&P](T *){if(P != nullptr){Fn;}})
// 打印异常信息,并退出main函数
#define FATAL(M, ...) printf(M, ##__VA_ARGS__); return -1
// 自定义变量
const char *src_media = "D:/1.mp4";
const char *dst_media = "D:/2.pcm";
// 定义要输出的pcm格式
#define PCM_FORMAT AV_SAMPLE_FMT_S16
#define PCM_CHANNELS 1
#define PCM_SAMPLE_RATE 44100
int main(int argc, char **argv) {
// 根据输入文件,打开媒体格式上下文
NEW_PTR(AVFormatContext, fmt_ctx, nullptr, avformat_close_input(&fmt_ctx));
if (avformat_open_input(&fmt_ctx, src_media, nullptr, nullptr) < 0) {
FATAL("open src media file failed.");
}
// 查询stream信息
if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
FATAL("find stream info failed.");
}
// 查询音频stream的索引
int audio_st_idx;
if ((audio_st_idx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0)) < 0) {
FATAL("find audio stream failed.");
}
// 通过索引找到音频stream
AVStream *audio_st;
audio_st = fmt_ctx->streams[audio_st_idx];
// 查询音频stream的解码器
AVCodec *audio_dec;
if ((audio_dec = avcodec_find_decoder(audio_st->codecpar->codec_id)) == nullptr) {
FATAL("find audio decoder failed.");
}
// 为解码器申请上下文
NEW_PTR(AVCodecContext, audio_dec_ctx, nullptr, avcodec_free_context(&audio_dec_ctx));
if ((audio_dec_ctx = avcodec_alloc_context3(audio_dec)) == nullptr) {
FATAL("allocate audio dec context failed.");
}
// 从音频stream中拷贝解码参数到解码器的上下文
if (avcodec_parameters_to_context(audio_dec_ctx, audio_st->codecpar) < 0) {
FATAL("copy codec params failed.");
}
// 打开解码器
if (avcodec_open2(audio_dec_ctx, audio_dec, nullptr) < 0) {
FATAL("codec open failed.");
}
// 申请一个packet,并初始化
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = nullptr;
pkt.size = 0;
// 申请一个frame
NEW_PTR(AVFrame, audio_frame, nullptr, av_frame_free(&audio_frame));
if ((audio_frame = av_frame_alloc()) == nullptr) {
FATAL("allocate audio frame failed.");
}
// 创建音频转换后的frame
NEW_PTR(AVFrame, filter_frame, nullptr, av_frame_free(&filter_frame));
if ((filter_frame = av_frame_alloc()) == nullptr) {
FATAL("allocate convert frame failed.");
}
// 添加音频转换过滤器
NEW_PTR(AVFilterGraph, filter_graph, nullptr, avfilter_graph_free(&filter_graph));
if ((filter_graph = avfilter_graph_alloc()) == nullptr) {
FATAL("alloc graph failed.");
}
char args[512] = {0};
snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",
audio_st->time_base.num, audio_st->time_base.den,
audio_dec_ctx->sample_rate, av_get_sample_fmt_name(audio_dec_ctx->sample_fmt),
audio_dec_ctx->channel_layout);
AVFilterContext *buffer_src_ctx = nullptr;
const AVFilter *in = avfilter_get_by_name("abuffer");
if (avfilter_graph_create_filter(&buffer_src_ctx, in, "in", args, nullptr, filter_graph)) {
FATAL("create src filter failed.");
}
AVFilterContext *buffer_sink_ctx = nullptr;
const AVFilter *out = avfilter_get_by_name("abuffersink");
if (avfilter_graph_create_filter(&buffer_sink_ctx, out, "out", nullptr, nullptr, filter_graph)) {
FATAL("create sink filter failed.");
}
enum AVSampleFormat sample_fmts[] = {PCM_FORMAT, AV_SAMPLE_FMT_NONE};
if (av_opt_set_int_list(buffer_sink_ctx, "sample_fmts", sample_fmts, -1, AV_OPT_SEARCH_CHILDREN)) {
FATAL("set sink filter options sample_fmts failed.");
}
int64_t channel_layouts[] = {av_get_default_channel_layout(PCM_CHANNELS), -1};
if (av_opt_set_int_list(buffer_sink_ctx, "channel_layouts", channel_layouts, -1, AV_OPT_SEARCH_CHILDREN)) {
FATAL("set sink filter options channel_layouts failed.");
}
int sample_rates[] = {PCM_SAMPLE_RATE, -1};
if (av_opt_set_int_list(buffer_sink_ctx, "sample_rates", sample_rates, -1, AV_OPT_SEARCH_CHILDREN)) {
FATAL("set sink filter options sample_rates failed.");
}
NEW_PTR(AVFilterInOut, inputs, nullptr, avfilter_inout_free(&inputs));
if ((inputs = avfilter_inout_alloc()) == nullptr) {
FATAL("alloc inputs failed.");
}
inputs->name = av_strdup("out");
inputs->filter_ctx = buffer_sink_ctx;
inputs->pad_idx = 0;
inputs->next = nullptr;
NEW_PTR(AVFilterInOut, outputs, nullptr, avfilter_inout_free(&outputs));
if ((outputs = avfilter_inout_alloc()) == nullptr) {
FATAL("alloc outputs failed.");
}
outputs->name = av_strdup("in");
outputs->filter_ctx = buffer_src_ctx;
outputs->pad_idx = 0;
outputs->next = nullptr;
char channel_layout_str[100] = {0};
av_get_channel_layout_string(channel_layout_str, sizeof(channel_layout_str), PCM_CHANNELS, (uint64_t)av_get_default_channel_layout(PCM_CHANNELS));
char filter_desc[512] = {0};
snprintf(filter_desc, sizeof(filter_desc), "aresample=%d,aformat=sample_fmts=%s:channel_layouts=%s",
PCM_SAMPLE_RATE, av_get_sample_fmt_name(PCM_FORMAT), channel_layout_str);
if (avfilter_graph_parse_ptr(filter_graph, filter_desc, &inputs, &outputs, nullptr) < 0) {
FATAL("parse graph failed.");
}
if (avfilter_graph_config(filter_graph, nullptr) < 0) {
FATAL("config graph failed.");
}
// 打开输出文件
NEW_PTR(FILE, output, nullptr, fclose(output));
if ((output = fopen(dst_media, "wb")) == nullptr) {
FATAL("no writable file.");
}
// 如果小于0,则表示读完了或者报错了,跳出循环
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
// 我们只关心音频stream
if (pkt.stream_index == audio_st_idx) {
// 发送一个音频包
if (avcodec_send_packet(audio_dec_ctx, &pkt) < 0) {
FATAL("send packet exception.");
}
// 接受解码完的内容
while (true) {
auto frame_ret = avcodec_receive_frame(audio_dec_ctx, audio_frame);
// 判断是否完全接受了frame
if (frame_ret == AVERROR(EAGAIN) || frame_ret == AVERROR_EOF) {
break;
}
// 检查是否接受异常
if (frame_ret < 0) {
FATAL("receive frame exception.");
}
if (av_buffersrc_add_frame_flags(buffer_src_ctx, audio_frame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) {
break;
}
while (true) {
auto got_ret = av_buffersink_get_frame(buffer_sink_ctx, filter_frame);
if (got_ret == AVERROR(EAGAIN) || got_ret == AVERROR_EOF) break;
if (got_ret < 0) {
FATAL("get filter frame failed.");
}
// 写入文件,如果是带P的音频格式,需要分通道写入文件
if (av_sample_fmt_is_planar((AVSampleFormat)filter_frame->format)) {
// 计算一个样本的大小
auto sampleBytes = av_get_bytes_per_sample((AVSampleFormat)filter_frame->format);
for (int i = 0; i < filter_frame->nb_samples; ++i) {
for (int ch = 0; ch < filter_frame->channels; ++ch) {
fwrite(filter_frame->data[ch] + i * sampleBytes, 1, (size_t)sampleBytes, output);
}
}
} else {
// 计算帧的大小
auto frameBytes = av_samples_get_buffer_size(nullptr, filter_frame->channels, filter_frame->nb_samples, (AVSampleFormat)filter_frame->format, 1);
fwrite(filter_frame->data[0], 1, (size_t)frameBytes, output);
}
av_frame_unref(filter_frame);
}
av_frame_unref(audio_frame);
}
}
av_packet_unref(&pkt);
}
return 0;
}