FFmpeg学习之安卓音频文件解封装解码到PCM文件
一、c代码实现将音频文件解码转化成s16le的PCM(music_ffmpeg.c)
//--------------------------安卓的log
#include <jni.h>
#include <android/log.h>
#define LOG_TAG "zbv"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
//--------------------------安卓的log
//--------------------------ffmpeg
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
//--------------------------ffmpeg
static const char* sourch_path;
static const char* destination_path;
static AVFormatContext* fmt_ctx;
static AVCodecContext* audio_codec_ctx;
static enum AVSampleFormat dst_sample_fmt=AV_SAMPLE_FMT_S16;
void Java_com_example_simpleTestFFmpeg_MusicDisplayActivity_nativeDecodeMusic(JNIEnv* env,jclass clazz,jstring sourcePath,jstring destPath){
int ret,stream_index,got_frame;
AVStream *stream;
AVCodec *codec;
FILE* desFile=NULL;
struct SwrContext* swr_ctx;
int dst_nb_sample,max_dst_nb_sample;
uint8_t **dst_data=NULL;
AVFrame* frame;
AVPacket* packet;
sourch_path=(*env)->GetStringUTFChars(env,sourcePath,NULL);
destination_path=(*env)->GetStringUTFChars(env,destPath,NULL);
LOGE("原始文件path=%s,目标文件path=%s",sourch_path,destination_path);
av_register_all();
//需要联网调用
avformat_network_init();
//第三个参数是AVInputFormat* 如果是NULL则会自动被检测---该函数表示:打开一个输入流读取header
if(avformat_open_input(&fmt_ctx,sourch_path,NULL,NULL)<0){
LOGD("无法打开源文件");
return;
}
//该函数表示:读取媒体文件的packets去获取流信息
if(avformat_find_stream_info(fmt_ctx,NULL)<0){
LOGD("无法读取源文件流信息");
return;
}
//因为是音频文件所以就针对音频来
//第三个参数wanted_stream_nb如果为-1则自动选择,该函数表示:找到最适合的流
ret=av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
if(ret<0){
//av_get_media_type_string函数就是switch-case简单函数返回字符串char*
LOGD("没有找到%s类型的输入流",av_get_media_type_string(AVMEDIA_TYPE_AUDIO));
goto end;
}else{
//获取到流下标
stream_index=ret;
//注意streams是数组AVStream **streams
stream=fmt_ctx->streams[stream_index];
//该函数表示:用匹配的codecId找到注册过的解码器
codec=avcodec_find_decoder(stream->codecpar->codec_id);
if(!codec){
LOGD("失败去找到%s类型的解码器",av_get_media_type_string(AVMEDIA_TYPE_AUDIO));
return;
}
//该函数表示给AVCodecContext分配内存设置默认值,最后记得释放
audio_codec_ctx=avcodec_alloc_context3(codec);
if(!audio_codec_ctx){
LOGD("给AVCodecContext内存分配失败");
return;
}
//该函数表示将输入流的参数信息拷贝给AVCodecContext
ret=avcodec_parameters_to_context(audio_codec_ctx,stream->codecpar);
if(ret<0){
LOGD("给AVCodecContext拷贝参数失败");
return;
}
//该函数表示:用给定的AVCodec初始化AVCodecContext
ret=avcodec_open2(audio_codec_ctx,codec,NULL);
if(ret<0){
LOGD("打开%s类型的解码器失败",av_get_media_type_string(AVMEDIA_TYPE_AUDIO));
return;
}
}
//打开destination文件
desFile=fopen(destination_path,"wb");
if(!desFile){
LOGD("打开destination文件失败");
goto end;
}
//swsample
swr_ctx=swr_alloc();
if(!swr_ctx){
LOGD("分配resample context失败");
goto end;
}
av_opt_set_int(swr_ctx,"in_channel_layout",audio_codec_ctx->channel_layout,0);
av_opt_set_int(swr_ctx,"in_sample_rate",audio_codec_ctx->sample_rate,0);
av_opt_set_sample_fmt(swr_ctx,"in_sample_fmt",audio_codec_ctx->sample_fmt,0);
int channels=av_get_channel_layout_nb_channels(audio_codec_ctx->channel_layout);
char* fmt_name=av_get_sample_fmt_name(audio_codec_ctx->sample_fmt);
LOGD("channels=%d,sampleRate=%d,sampleFmt=%s",channels,audio_codec_ctx->sample_rate,fmt_name);
av_opt_set_int(swr_ctx,"out_channel_layout",audio_codec_ctx->channel_layout,0);
av_opt_set_int(swr_ctx,"out_sample_rate",audio_codec_ctx->sample_rate,0);
av_opt_set_sample_fmt(swr_ctx,"out_sample_fmt",dst_sample_fmt,0);
//该函数表示:设置好参数配置后初始化resample context
ret=swr_init(swr_ctx);
if(ret<0){
LOGD("初始化resample context失败");
goto end;
}
LOGD("swr init finished");
//audio_codec_ctx->frame_size---每个音频通道的样本数===>frame->nb_samples
max_dst_nb_sample=dst_nb_sample=av_rescale_rnd(audio_codec_ctx->frame_size,audio_codec_ctx->sample_rate,audio_codec_ctx->sample_rate,AV_ROUND_UP);
//类似于下面的av_samples_alloc--->从第三个参数开始:通道数、单通道的样本数、样本格式、对齐
ret=av_samples_alloc_array_and_samples(&dst_data,NULL,channels,dst_nb_sample,dst_sample_fmt,0);
if(ret<0){
LOGD("分配dst_data失败");
goto end;
}
LOGD("to do frame");
frame=av_frame_alloc();
if(!frame){
LOGD("无法分配frame");
goto end;
}
packet=(AVPacket *)malloc(sizeof(AVPacket));
av_init_packet(packet);
packet->data=NULL;
packet->size=0;
LOGD("start av_read_frame");
//该函数表示:返回流中的一帧
while((ret=av_read_frame(fmt_ctx,packet))>=0){
if(packet->stream_index==stream_index){
do{
int decoded = packet->size;
got_frame=0;
ret=0;
//该函数表示:解码音频,第三个参数为零表示no frame
ret=avcodec_decode_audio4(audio_codec_ctx,frame,&got_frame,packet);
if(ret<0){
LOGD("音频解码出错了");
goto end;
}
decoded = FFMIN(ret, packet->size);
if(got_frame){
/*如果是平面的话需要转换
if(av_sample_fmt_is_planar(audio_codec_ctx->sample_fmt)){
}*/
dst_nb_sample=av_rescale_rnd(swr_get_delay(swr_ctx,frame->sample_rate)+frame->nb_samples,frame->sample_rate,frame->sample_rate,AV_ROUND_UP);
if(dst_nb_sample>max_dst_nb_sample){
av_freep(&dst_data[0]);
ret=av_samples_alloc(dst_data,NULL,channels,dst_nb_sample,dst_sample_fmt,1);
if(ret<0){
LOGD("重新分配dst_data失败");
break;
}
max_dst_nb_sample=dst_nb_sample;
}
//该函数表示:开启格式转换
ret=swr_convert(swr_ctx,dst_data,dst_nb_sample,(uint8_t**)frame->data,frame->nb_samples);
if(ret<0){
LOGD("swr_convert转换错误");
goto end;
}
//该函数表示:通过给定的参数得到需要的buffer size
int dst_buffer_size=av_samples_get_buffer_size(NULL,channels,ret,dst_sample_fmt,1);
if(dst_buffer_size<0){
LOGD("获取样本buffer大小失败");
goto end;
}
LOGD("WRITE TO FILE %d",dst_buffer_size);
//write to destination file
fwrite(dst_data[0],1,dst_buffer_size,desFile);
}
packet->data+=decoded;
packet->size-=decoded;
}while(packet->size>0);
}
//过去使用av_free_packet()
av_free_packet(packet);
}
LOGD("flush cached frames");
/*flush cached frame*/
packet->data=NULL;
packet->size=0;
do{
ret=avcodec_decode_audio4(audio_codec_ctx,frame,&got_frame,packet);
if(ret<0){
LOGD("音频解码出错了");
goto end;
}
if(got_frame)
{
/*如果是平面的话需要转换
if(av_sample_fmt_is_planar(audio_codec_ctx->sample_fmt)){
}*/
dst_nb_sample=av_rescale_rnd(swr_get_delay(swr_ctx,frame->sample_rate)+frame->nb_samples,frame->sample_rate,frame->sample_rate,AV_ROUND_UP);
if(dst_nb_sample>max_dst_nb_sample){
av_freep(&dst_data[0]);
ret=av_samples_alloc(dst_data,NULL,channels,dst_nb_sample,dst_sample_fmt,1);
if(ret<0){
LOGD("重新分配dst_data失败");
break;
}
max_dst_nb_sample=dst_nb_sample;
}
//该函数表示:开启格式转换
ret=swr_convert(swr_ctx,dst_data,dst_nb_sample,(uint8_t**)frame->data,frame->nb_samples);
if(ret<0){
LOGD("swr_convert转换错误");
goto end;
}
//该函数表示:通过给定的参数得到需要的buffer size
int dst_buffer_size=av_samples_get_buffer_size(NULL,channels,ret,dst_sample_fmt,1);
if(dst_buffer_size<0){
LOGD("获取样本buffer大小失败");
goto end;
}
//ffplay -f s16le -ac 2 -ar 44100 -i lky_bhs.pcm 其中le表示小端尾
//write to destination file
fwrite(dst_data[0],1,dst_buffer_size,desFile);
}
}while(got_frame);
LOGE("解封装解码全部完成!!!");
end:
avcodec_free_context(&audio_codec_ctx);
avformat_close_input(&fmt_ctx);
if(desFile){
fclose(desFile);
}
if(dst_data){
av_freep(&dst_data[0]);
}
av_freep(&dst_data);
av_frame_free(&frame);
swr_free(&swr_ctx);
}
# Android.mk for FFmpeg
#
# Lei Xiaohua À×Ïöæè
# leixiaohua1020@126.com
# http://blog.csdn.net/leixiaohua1020
#
LOCAL_PATH := $(call my-dir)
# FFmpeg library
include $(CLEAR_VARS)
LOCAL_MODULE := avcodec
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavcodec-58.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avdevice
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavdevice-58.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avfilter
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavfilter-7.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avformat
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavformat-58.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avutil
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavutil-56.so
include $(PREBUIL