实现本文所述程序的开发环境搭建,请参考上一篇:《Ffmpeg视频开发教程(一)——实现视频格式转换功能超详细版》
原创文章,谢绝转载。
将mp3数据解码为pcm音频裸数据的场合并不多见。但是知道如何实现这一功能有时是有用的。首先参考教程(一)搭好环境,然后把我们控制台程序的主程序的cpp文件内容换成下面的内容:
/**
实现FFMPEG将mp3文件数据转换为pcm音频数据的完整测试代码,作者自己测试正确可用
作者:明天继续
使用的ffmpeg版本:ffmpeg-20180508-293a6e8-win32
开发工具:vs2012
**/
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern "C" {
#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
#pragma comment(lib,"../../ffmpeg-20180508-293a6e8-win32-dev/lib/avcodec.lib")
#pragma comment(lib,"../../ffmpeg-20180508-293a6e8-win32-dev/lib/avformat.lib")
#pragma comment(lib,"../../ffmpeg-20180508-293a6e8-win32-dev/lib/avfilter.lib")
#pragma comment(lib,"../../ffmpeg-20180508-293a6e8-win32-dev/lib/avutil.lib")
#if _MSC_VER
#define snprintf _snprintf_s
#define PRIx64 "I64x"
#define PRIX64 "I64X"
#endif
#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096
static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame,
FILE *outfile)
{
int i, ch;
int ret, data_size;
/* 发送编码数据包给解码器 */
ret = avcodec_send_packet(dec_ctx, pkt);
if (ret < 0) {
fprintf(stderr, "Error submitting the packet to the decoder\n");
return;
}
/* 读取所有的输出帧*/
while (ret >= 0) {
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during decoding\n");
exit(1);
}
data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
if (data_size < 0) {
/* 不应该出现的情况 */
fprintf(stderr, "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, outfile);
}
}
int _tmain(int argc, _TCHAR* argvec[])
{
//输入进行解码的mp3文件
char *intput_file = "D:\\Project\\ffmpeg\\video\\test.mp3";
//输出解码后获取到的pcm音频数据文件
char *output_file = "D:\\Project\\ffmpeg\\video\\out.pcm";
const char *outfilename, *filename;
const AVCodec *codec;
AVCodecContext *c= NULL;
AVCodecParserContext *parser = NULL;
int len, ret;
FILE *f, *outfile;
uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
uint8_t *data;
size_t data_size;
AVPacket *pkt;
AVFrame *decoded_frame = NULL;
av_register_all();
filename = intput_file;
outfilename = output_file;
pkt = av_packet_alloc();
/* 查找mp3解码器 */
codec = avcodec_find_decoder(AV_CODEC_ID_MP3);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
parser = av_parser_init(codec->id);
if (!parser) {
fprintf(stderr, "Parser not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate audio codec context\n");
exit(1);
}
/* 打开解码器*/
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
outfile = fopen(outfilename, "wb");
if (!outfile) {
av_free(c);
exit(1);
}
/* 解码数据 */
data = inbuf;
data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, f);
while (data_size > 0) {
if (!decoded_frame) {
if (!(decoded_frame = av_frame_alloc())) {
fprintf(stderr, "Could not allocate audio frame\n");
exit(1);
}
}
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, pkt, decoded_frame, outfile);
if (data_size < AUDIO_REFILL_THRESH) {
memmove(inbuf, data, data_size);
data = inbuf;
len = fread(data + data_size, 1,
AUDIO_INBUF_SIZE - data_size, f);
if (len > 0)
data_size += len;
}
}
/* 处理最后的数据 */
pkt->data = NULL;
pkt->size = 0;
decode(c, pkt, decoded_frame, outfile);
fclose(outfile);
fclose(f);
avcodec_free_context(&c);
av_parser_close(parser);
av_frame_free(&decoded_frame);
av_packet_free(&pkt);
return 0;
}
然后重新编译我们的程序,在D:\\Project\\ffmpeg\\video\\test.mp3位置放入我们测试用的test.mp3文件。运行程序就可以了。
我的编译出来是mediaPro.exe, 运行完毕就可以看到out.pcm文件已经生成了。
对于pcm数据,我们可以写个小工具直接进行播放。验证效果。或者安装一个免费软件Audacity导入裸数据,也可以方便的播放。