FFmpeg学习之安卓音频文件解封装解码到PCM文件

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);
}

二、编译jni (借鉴雷霄骅大神的系列)

# 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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值