网上找了好多例子,因为是用了最新的3.2.2(2017年1月23日 04:22:33)版本的FFmpeg,所以找的东西基本上都不可能拿过来就能跑起来,研究了许久,参考了诸多大神的博客,然后一句句代码专研,终于改出了一个能跑起来的例子。其实并不是其他人的例子有问题,只是FFmpeg重构和废弃了一些接口。
#define TAG "Jni打印输出" // 这个是自定义的LOG的标识 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__) // 定义LOGD类型 #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 #ifdef __cplusplus extern "C" { //有些可能用不到。。。。。。懒得弄出来了 #include <libavcodec\avcodec.h> #include "libavutil/mathematics.h" #include <libavformat\avformat.h> #include <libswscale\swscale.h> #include "libavfilter/avfiltergraph.h" #include "libavfilter/buffersink.h" #include "libavfilter/buffersrc.h" #include "libavutil/avutil.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libswresample\swresample.h" #include "libavutil\fifo.h" #include "libavutil/samplefmt.h" } #endif
int audio_decoder(char *filename,char *cache_name) { AVFormatContext *pFormatCtx;//主要存储视音频封装格式中包含的信息 AVCodecContext *pCodecCtx;//存储该视频/音频流使用解码方式的相关数据 AVCodec *pCodec; int i, audioStream; av_register_all();//注册所有的编码器 pFormatCtx = avformat_alloc_context(); //打开输入文件 if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0) { LOGD("Couldn't open input stream.\n"); return -1; } // Retrieve stream information打开流 if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { LOGD("Couldn't find stream information.\n"); return -1; } // Dump valid information onto standard error av_dump_format(pFormatCtx, 0, filename, 0); // Find the first audio strea找到流 可以是视频流,也可以是音频流 audioStream = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { //判定是否为音频流 audioStream = i; break; } if (audioStream == -1) { LOGD("Didn't find a audio stream.\n");//找不到流就滚蛋了; return -1; } // Get a pointer to the codec context for the audio stream获取指向音频流的编解码器上下文的指针. pCodecCtx = pFormatCtx->streams[audioStream]->codec; // Find the decoder for the audio stream寻找解码器 pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == NULL) { LOGD("Codec not found.\n"); return -1; } // Open codec打开解码器 if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { return -1; } //设置解码参数 AVFrame *pFrame; pFrame = av_frame_alloc(); FILE *pFile = NULL; //文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL,并把错误代码存在errno 中。 pFile = fopen(cache_name, "wb"); //Out Audio Param音频参数 uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO; //AAC:1024 MP3:1152 int out_nb_samples = pCodecCtx->frame_size;//流的帧大小? enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16; int out_sample_rate = 44100; int out_channels = av_get_channel_layout_nb_channels(out_channel_layout); //输出音频数据大小,一定小于输出内存。 int out_linesize; //输出内存大小 int out_buffer_size=av_samples_get_buffer_size(&out_linesize, pCodecCtx->channels,pCodecCtx->frame_size,pCodecCtx->sample_fmt, 1); uint8_t *out_buffer = (uint8_t *) av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE * 2); uint32_t ret, len = 0; //FIX:Some Codec's Context Information is missing int64_t in_channel_layout = av_get_default_channel_layout(pCodecCtx->channels); //Swr(转码设置) struct SwrContext *au_convert_ctx; au_convert_ctx = swr_alloc(); au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, out_channel_layout, out_sample_fmt, out_sample_rate, in_channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0, NULL); swr_init(au_convert_ctx); int index=0; int output_index=0; uint8_t *pkt_data; int pkt_size; int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE * 100; AVPacket *packet=av_packet_alloc();; while (av_read_frame(pFormatCtx, packet)>=0) { if (packet->stream_index == audioStream) { int len=avcodec_decode_audio4(pCodecCtx, pFrame, &out_size, packet); if(len<0){ break; } if(out_size>0) { //先转码成pcm数据 swr_convert(au_convert_ctx, &out_buffer, out_linesize, (const uint8_t **) pFrame->data, pFrame->nb_samples); //然后写入到文件中 fwrite(out_buffer, 1, out_buffer_size, pFile); } }else{ } index++; av_packet_unref(packet); } av_packet_free(&packet); swr_free(&au_convert_ctx); fclose(pFile); av_free(out_buffer); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); LOGD("解码完成"); return 0; }
参考博客:
http://blog.csdn.net/leixiaohua1020/article/details/10528443
http://blog.csdn.net/yang_xian521/article/details/7699620
http://www.cnblogs.com/wangguchangqing/p/5790705.html
http://www.cnblogs.com/wangguchangqing/p/5851490.html
(最后这篇博客有说:音频解码API avcodec_decode_audio4
在新版中已废弃,替换为使用更为简单的avcodec_send_packet
和avcodec_receive_frame
。
后来我在FFmpeg的源码里面依旧看到avcodec_decode_audio4
这个函数还是存在的,并且简单的调试了一下替换的那两个函数,但是失败了,所以这里依旧是使用avcodec_decode_audio4
函数。)