FFmpeg 解码音频
·
使用FFmpeg库(版本号为:4.4.2-0ubuntu0.22.04.1)解码媒体文件中的音频流为pcm数据文件。可以使用我上传的MP4文件测试,PCM查看工具:Audacity
https://www.audacityteam.org/
或者使用ffplay播放,命令如下:
# `-f f32le`:指定输入格式为 32-bit 浮点小端序 PCM。
# `-ar 48000`:指定音频采样率为 48000 Hz。
# `-ac 2`:指定音频声道数为 2(立体声)。
# `input.pcm`:PCM 文件的名称。
ffplay -f f32le -ar 48000 -ac 2 input.pcm
代码如下:
#include <stdio.h>
#include "libavformat/avformat.h"
int main(int argc, char *argv[])
{
int ret = -1;
const char *input_filePath = argv[1];
const char *output_filePath = argv[2];
AVFormatContext *ifmt_ctx = NULL;
AVCodec *codec = NULL;
AVPacket *pkt = NULL;
AVFrame *frame = NULL;
AVCodecContext *codec_ctx = NULL;
if (argc < 3)
{
printf("Usage: %s <input file> <output file>\n", argv[0]);
return -1;
}
// 打印ffmpeg版本信息
printf("ffmpeg version: %s\n", av_version_info());
// 打开输入的流
if (avformat_open_input(&ifmt_ctx, input_filePath, NULL, NULL) != 0)
{
printf("avformat_open_input failed\n");
return -1;
}
// 查找流信息
if (avformat_find_stream_info(ifmt_ctx, NULL) < 0)
{
printf("avformat_find_stream_info failed\n");
goto end;
}
// 寻找音频流索引和解码器
int audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
if (audio_index < 0)
{
printf("av_find_best_stream failed\n");
goto end;
}
// 打印输入流信息
printf("输入流信息:\n");
av_dump_format(ifmt_ctx, audio_index, input_filePath, 0);
printf("\n");
// 初始化解码器上下文
codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx)
{
printf("avcodec_alloc_context3 failed\n");
goto end;
}
// 将流的解码器参数拷贝到解码器上下文
if (avcodec_parameters_to_context(codec_ctx, ifmt_ctx->streams[audio_index]->codecpar) < 0)
{
printf("avcodec_parameters_to_context failed\n");
goto end;
}
// 打开解码器
if (avcodec_open2(codec_ctx, codec, NULL) < 0)
{
printf("avcodec_open2 failed\n");
goto end;
}
pkt = av_packet_alloc();
if (!pkt)
{
printf("av_packet_alloc failed\n");
goto end;
}
frame = av_frame_alloc();
if (!frame)
{
printf("av_frame_alloc failed\n");
goto end;
}
// 打开输出文件
FILE *foutput = fopen(output_filePath, "wb");
if (!foutput)
{
printf("fopen failed\n");
goto end;
}
/* 从输入文件中读取数据包 */
// 调用 av_read_frame 成功后需要调用 av_packet_unref 释放数据包
while ((ret = av_read_frame(ifmt_ctx, pkt)) >= 0)
{
if (pkt->stream_index == audio_index)
{
// 发送数据包到解码器
int val = avcodec_send_packet(codec_ctx, pkt);
if (val >= 0)
{
// 从解码器中接收解码后的数据
val = avcodec_receive_frame(codec_ctx, frame);
if (val == AVERROR(EAGAIN) || val == AVERROR_EOF)
{
av_packet_unref(pkt);
continue;
}
else if (val < 0)
{
printf("avcodec_receive_frame error (errmsg '%s')\n", av_err2str(val));
av_packet_unref(pkt);
goto end;
}
// 计算每个样本的字节数
int data_size = av_get_bytes_per_sample(codec_ctx->sample_fmt);
if (data_size > 0)
{
// 将解码后的数据写入文件
// printf("每帧样本个数: %d, 通道数: %d\n", frame->nb_samples, codec_ctx->channels);
for (int i = 0; i < frame->nb_samples; i++)
{
for (int ch = 0; ch < codec_ctx->channels; ch++)
{
fwrite(frame->data[ch] + data_size * i, 1, data_size, foutput);
}
}
}
}
}
av_packet_unref(pkt);
}
// 判断是否读取到文件末尾,av_read_frame 返回 AVERROR_EOF 表示读取到文件末尾
if (ret < 0 && ret != AVERROR_EOF)
{
printf("av_read_frame error (errmsg: '%s')\n", av_err2str(ret));
goto end;
}
// 冲刷解码器
avcodec_send_packet(codec_ctx, NULL);
while (1)
{
int val = avcodec_receive_frame(codec_ctx, frame);
if (val == AVERROR_EOF)
{
break;
}
else if (val < 0)
{
printf("avcodec_receive_frame error (errmsg '%s')\n", av_err2str(val));
goto end;
}
// 计算每个样本的字节数
int data_size = av_get_bytes_per_sample(codec_ctx->sample_fmt);
if (data_size > 0)
{
// 将解码后的数据写入文件
for (int i = 0; i < frame->nb_samples; i++)
{
for (int ch = 0; ch < codec_ctx->channels; ch++)
{
fwrite(frame->data[ch] + data_size * i, 1, data_size, foutput);
}
}
}
}
end:
if (foutput)
fclose(foutput);
if (frame)
av_frame_free(&frame);
if (pkt)
av_packet_free(&pkt);
if (codec_ctx)
avcodec_free_context(&codec_ctx);
avformat_close_input(&ifmt_ctx);
return 0;
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)