我一直觉得从音频中解码出pcm数据是做音频的第一件事,要重要最简单的事情。所以第一个功能实验就是音频转码。
当然了,要我自己写肯定是写不出的,所以就求助于万能的google了。折腾了两天,现在看起来感觉很简单,但是最开始的时候什么都不知道,程序都看不懂,难受的就要不行了。
流程如下(偷来的,嘿嘿)
1,打开音乐文件,调用av_open_input_file()
2,查找audio stream,调用av_find_stream_info()
3,查找对应的decoder,调用avcodec_find_decoder()
4,打开decoder,调用avcodec_open2()
5,读取一桢数据包,调用av_read_frame()
6,解码数据包,调用avcodec_decode_audio4()
7,将解码后的数据返回
没啥说的,上代码。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/mathematics.h>
#include <libswresample/swresample.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavformat/avio.h>
#include <libavutil/file.h>
int main(int argc, char * argv[]) {
const char *input = "ping.mp3";
const char *outfilename = "test.wav";
/******************注册解码器************************/
av_register_all();
avcodec_register_all();
/**************初始化并打开AVFormatContext************/
AVFormatContext *iFormatCtx = NULL;
iFormatCtx = avformat_alloc_context();
if (avformat_open_input(&iFormatCtx, input, NULL, NULL) != 0) {
printf("could not open input_file\n");
return -1;
}
/**************获取文件内音视频流的信息************/
if (avformat_find_stream_info(iFormatCtx, NULL) < 0) {
printf("find stream info failed\n");
return -1;
}
/*****************打印format信息*****************/
av_dump_format(iFormatCtx, 0, input, 0);
/*****************查找媒体流信息*****************/
int AudioStreamIndex;
int i;
AudioStreamIndex = -1;
for (i = 0; i < iFormatCtx->nb_streams; i++) {
if (iFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO
&& AudioStreamIndex < 0) {
AudioStreamIndex = i;
break;
}
}
if (AudioStreamIndex == -1) {
printf("Cannot find VideoStream\n");
return -1;
}
/*****************初始化并打开解码器ls*****************/
AVCodecContext *c = NULL;
AVCodec *iaCodec = NULL;
c = iFormatCtx->streams[AudioStreamIndex]->codec;
iaCodec = avcodec_find_decoder(c->codec_id);
if (iaCodec == NULL) {
printf("unsupported codec!\n");
return -1;
}
if (iaCodec->capabilities & CODEC_CAP_TRUNCATED)
c->flags |= CODEC_CAP_TRUNCATED;
if (avcodec_open2(c, iaCodec, NULL) < 0) {
printf("could not open the codec\n");
return -1;
}
/*************************************************/
FILE *outfile = fopen(outfilename, "wb");
if (!outfile) {
printf("open outfile failed\n");
av_free(c);
return -1;
}
/*****************申请一个frame并初始化为默认值******************/
AVFrame *frame = NULL;
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate audio frame\n");
exit(1);
}
/****************初始化存储帧数据的容器AVPacket***************/
AVPacket ipacket;
av_init_packet(&ipacket);
/****************按帧读取数据,直到读完所有帧***************/
while (av_read_frame(iFormatCtx, &ipacket) >= 0) {
if (ipacket.stream_index == AudioStreamIndex) {
while (ipacket.size > 0) {
int out_size;
//解码一帧
int len = avcodec_decode_audio4(c, frame, &out_size, &ipacket);
if (len < 0) {
//pktsize = 0;
printf("Error while decoding\n");
continue;
}
if (out_size) {
//获取bit_rate
int data_size = av_get_bytes_per_sample(c->sample_fmt);
if (data_size < 0) {
/* This should not occur, checking just for paranoia */
fprintf(stderr, "Failed to calculate data size\n");
exit(1);
}
//多声道存储
int i = 0;
int ch = 0;
for (i = 0; i < frame->nb_samples; i++) {
for (ch = 0; ch < c->channels; ch++) {
fwrite(frame->data[ch] + data_size * i, 1,
data_size, outfile);
}
}
}
ipacket.size -= len;
ipacket.data += len;
}
}
}
return 0;
}
代码没优化,很多内存泄漏的地方。