本文主要是写了一个demo,基于ffmpeg实现android音乐文件的解码,播放功能,主要是用来练习,大家也可以参考下,用来学习
基础知识
名称 | 作用 |
AVFormatContext | AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数 |
AVCodecContext | 编码器和解码器进行赋值,需要用具体的编码器实现来给他赋值 |
AVCodec | 具体的解码器 |
AVPacket | 存储流的帧数据 |
SwrContext | 重采样上下文 |
AudioTrack:仅仅能播放已经解码的PCM流,这个是安卓系统自带的一个类,主要是用来播放音乐的
具体流程和部分代码
一、首先需要在android中引入ffmepg,这个我在博客中已有说明,或者网上查资料,不是特别难
二、环境配置好就要开始代码的书写了,我们第一步需要注册所有的组件,av_register_all(),注册所有的组件。
三、在注册所有组件后,AVFormatContext *avFormatContext = avformat_alloc_context();来获取
四、使用avformat_open_input来打开音频文件,如果不是用的sd卡,而是用的默认路径,路径名称要这么写"/mnt/sdcard/",否则会找不到播放文件。
//注册所有的内容
av_register_all();
//转换路径为自己的内容
const char *input_cstr = (*env)->GetStringUTFChars(env, input_jstr, NULL);
//分配格式化上下文
AVFormatContext *avFormatContext =
avformat_alloc_context();
//打开音频文件
int a=avformat_open_input(&avFormatContext, input_cstr, NULL, NULL);
if ( a!= 0) {
LOGI("%s","无法打开音频文件");
return;
}
//检测是否获取到了音频文件的流信息
if (avformat_find_stream_info(avFormatContext, NULL) < 0) {
LOGI("%s","无法获取流信息");
return;
}
六、在打开文件之后,我们要用avcodec_find_decoder去找到音频文件的解码器,要对音频文件进行解码
//获取音频流位置
int i = 0, audio_stream_idx = -1;
//对音频进行循环
for (; i < avFormatContext->nb_streams; i++) {
//判断流的类型是不是音频类型的
if (avFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
audio_stream_idx = i;
}
}
//获取音频的解码器
AVCodecContext *codecCtx = avFormatContext->streams[audio_stream_idx]->codec;
//得到解码器
AVCodec *avCodec = avcodec_find_decoder(codecCtx->codec_id);
if (avCodec == NULL) {
LOGI("%s","无法获取解码器");
return;
}
if (avcodec_open2(codecCtx, avCodec, NULL) < 0) {
LOGI("%s","打开解码器失败");
return;
}
七、在解码之后要定义一义转换的参数,要把数据转换为自己要播放的格式
//分配包数据,
// 分配AVPacket结构体的大小
AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));
//分配帧空间
AVFrame *frame = av_frame_alloc();
//分配重采样空间
SwrContext *swrCtx = swr_alloc();
//输入采样格式
enum AVSampleFormat in_sample_fmt = codecCtx->sample_fmt;
//输出采样格式
enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
//输入采样率
int in_sample_rate = codecCtx->sample_rate;
//输出采样率
int out_sample_rate = in_sample_rate;
//声道布局(2个声道,默认立体声stereo)
uint64_t in_ch_layout = codecCtx->channel_layout;
//输出声道布局
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
/**
* 设置转换参数
*/
swr_alloc_set_opts(swrCtx,
out_ch_layout,
out_sample_fmt,
out_sample_rate,
in_ch_layout,
in_sample_fmt, in_sample_rate, 0, NULL);
//采样初始化
swr_init(swrCtx);
//获取频道的输出声道
int out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);
八、通过jni来调用java的播放方法,这里调用的是AudioTrack方法,先调用play方法,之后边播调用write方法写入数据,实现边解码边播放数据
/**********************************************************************
******************************开始播放操作****************************
**********************************************************************/
LOGI("开始播放操作");
//获取播放的类
jclass play_class = (*env)->GetObjectClass(env, instance);
if (!play_class) {
LOGE("player_class not found...");
}
//读取java类中的createAudioTrack方法
//上下文,类名,方法名,引用类
jmethodID audio_track_method =
(*env)->GetMethodID(env, play_class, "createAudioTrack",
"(II)Landroid/media/AudioTrack;");
if (!audio_track_method) {
LOGI("%s","没有找到audio_track_method方法");
}
//执行createAudioTrack方法 得到AudioTrack类,AudioTrack是执行播放的方法
jobject audio_track = (*env)->CallObjectMethod(env, instance, audio_track_method,
out_sample_rate, out_channel_nb);
//调用AudioTrack的播放方法
jclass audio_track_class = (*env)->GetObjectClass(env, audio_track);
jmethodID audio_tarck_play_mid = (*env)->GetMethodID(env, audio_track_class, "play", "()V");
//执行play方法
(*env)->CallVoidMethod(env, audio_track, audio_tarck_play_mid);
//获取AudioTrack的write方法
jmethodID audio_track_write_mid = (*env)->GetMethodID(env, audio_track_class, "write",
"([BII)I");
//16bit 44100 PCM 数据
uint8_t *out_buffer = (uint8_t *) av_malloc(MAX_AUDIO_FRAME_SIZE);
九、开始播放操作,通过av_read_frame读取帧的数据,判断是不是音频的数据,转换之后,调用AudioTrack的write方法进行播放
//定义三个参数
int got_frame = 0, index = 0, ret;
LOGI("%s","准备播放数据");
while (av_read_frame(avFormatContext, packet)>=0) {
//循环到了音频部分
if (packet->stream_index == audio_stream_idx) {
//解码
ret = avcodec_decode_audio4(codecCtx, frame, &got_frame, packet);
if (ret < 0) {
break;
}
//解码一帧成功
LOGI("%s","解码一帧成功");
if (got_frame > 0) {
//音频格式转换
swr_convert(swrCtx, &out_buffer, MAX_AUDIO_FRAME_SIZE,
(const u_int8_t **) frame->data, frame->nb_samples);
int out_buffer_size = av_samples_get_buffer_size(
NULL, out_channel_nb, frame->nb_samples, out_sample_fmt, 1
);
jbyteArray audio_sample_array = (*env)->NewByteArray(env, out_buffer_size);
jbyte *sample_byte_array = (*env)->GetByteArrayElements(env, audio_sample_array,
NULL);
//拷贝缓冲数据
memcpy(sample_byte_array, out_buffer, (size_t) out_buffer_size);
//释放数组
(*env)->ReleaseByteArrayElements(env, audio_sample_array, sample_byte_array, 0);
//调用AudioTrack的write方法进行播放
(*env)->CallIntMethod(env, audio_track, audio_track_write_mid,
audio_sample_array, 0, out_buffer_size);
//释放局部引用
(*env)->DeleteLocalRef(env, audio_sample_array);
usleep(1000 * 16);
}
}
av_free_packet(packet);
}
十、释放资源
LOGI("decode audio finish");
av_frame_free(&frame);
av_free(out_buffer);
swr_free(&swrCtx);
avcodec_close(codecCtx);
avformat_close_input(&avFormatContext);
//释放jni资源
(*env)->ReleaseStringUTFChars(env, input_jstr, input_cstr);
LOGI("%s","播放完成");
参考:
https://blog.csdn.net/leixiaohua1020/article/details/14214705