Android使用FFmpeg(四)--ffmpeg实现音频播放(使用AudioTrack进行播放)
16lz
2021-12-04
关于
Android使用FFmpeg(一)--编译ffmpeg
Android使用FFmpeg(二)--Android Studio配置ffmpeg
Android使用FFmpeg(三)--ffmpeg实现视频播放
Android使用FFmpeg(四)--ffmpeg实现音频播放(使用AudioTrack进行播放)
Android使用FFmpeg(五)--ffmpeg实现音频播放(使用openSL ES进行播放)
Android使用FFmpeg(六)--ffmpeg实现音视频同步播放
Android使用FFmpeg(七)--ffmpeg实现暂停、快退快进播放
准备工作
jni调用Java方法
AudioTrack使用
关于音频
正文
ffmpeg实现音频播放大概流程图.png和视频播放一样我们依照流程图来实现,从这个流程图可以看出,音频播放和视频播放的大概流程并没有多大差别,只是细节之处需要处理。
依照流程来实现
1.注册组件,打开音频文件并获取内容,找到音频流:
av_register_all(); AVFormatContext *pFormatCtx = avformat_alloc_context(); //open if (avformat_open_input(&pFormatCtx, input, NULL, NULL) != 0) { LOGE("%s","打开输入视频文件失败"); return; } //获取视频信息 if(avformat_find_stream_info(pFormatCtx,NULL) < 0){ LOGE("%s","获取视频信息失败"); return; } int audio_stream_idx=-1; int i=0; for (int i = 0; i < pFormatCtx->nb_streams; ++i) { if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { LOGE(" 找到音频id %d", pFormatCtx->streams[i]->codec->codec_type); audio_stream_idx=i; break; } }
2.获取解码器,申请avframe和avpacket:
//获取解码器上下文 AVCodecContext *pCodecCtx=pFormatCtx->streams[audio_stream_idx]->codec; //获取解码器 AVCodec *pCodex = avcodec_find_decoder(pCodecCtx->codec_id); //打开解码器 if (avcodec_open2(pCodecCtx, pCodex, NULL)<0) { } //申请avpakcet,装解码前的数据 AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket)); //申请avframe,装解码后的数据 AVFrame *frame = av_frame_alloc();
3.初始化SwrContext,进行重采样
//得到SwrContext ,进行重采样,具体参考http://blog.csdn.net/jammg/article/details/52688506 SwrContext *swrContext = swr_alloc(); //缓存区 uint8_t *out_buffer = (uint8_t *) av_malloc(44100 * 2);//输出的声道布局(立体声) uint64_t out_ch_layout=AV_CH_LAYOUT_STEREO;//输出采样位数 16位 enum AVSampleFormat out_formart=AV_SAMPLE_FMT_S16;//输出的采样率必须与输入相同 int out_sample_rate = pCodecCtx->sample_rate;//swr_alloc_set_opts将PCM源文件的采样格式转换为自己希望的采样格式 swr_alloc_set_opts(swrContext, out_ch_layout, out_formart, out_sample_rate, pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0, NULL); swr_init(swrContext);
4.通过while循环读取内容,并通过AudioTrack进行播放:
// 获取通道数 2 int out_channer_nb = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);// 反射得到Class类型 jclass david_player = env->GetObjectClass(instance);// 反射得到createAudio方法 jmethodID createAudio = env->GetMethodID(david_player, "createTrack", "(II)V");// 反射调用createAudio env->CallVoidMethod(instance, createAudio, 44100, out_channer_nb); jmethodID audio_write = env->GetMethodID(david_player, "playTrack", "([BI)V");int got_frame; while (av_read_frame(pFormatCtx, packet) >= 0) { if (packet->stream_index == audio_stream_idx) {// 解码 mp3 编码格式frame----pcm frame avcodec_decode_audio4(pCodecCtx, frame, &got_frame, packet); if (got_frame) { LOGE("解码"); swr_convert(swrContext, &out_buffer, 44100 * 2, (const uint8_t **) frame->data, frame->nb_samples);// 缓冲区的大小 int size = av_samples_get_buffer_size(NULL, out_channer_nb, frame->nb_samples, AV_SAMPLE_FMT_S16, 1); jbyteArray audio_sample_array = env->NewByteArray(size); env->SetByteArrayRegion(audio_sample_array, 0, size, (const jbyte *) out_buffer); env->CallVoidMethod(instance, audio_write, audio_sample_array, size); env->DeleteLocalRef(audio_sample_array); } } }
5.释放需要释放的资源:
av_frame_free(&frame); swr_free(&swrContext); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); env->ReleaseStringUTFChars(input_, input);
java层创建AudioTrack方法。
jni通过调用java层的audiotrack方法来实现播放,但是java层的audiotrack方法又是通过调用底层的openesl es来进行播放,相当于绕了一个圈,所以在下篇文章中将实现使用opensl es来直接播放音频。
public class MusicPlay { static{ System.loadLibrary("avcodec-56"); System.loadLibrary("avdevice-56"); System.loadLibrary("avfilter-5"); System.loadLibrary("avformat-56"); System.loadLibrary("avutil-54"); System.loadLibrary("postproc-53"); System.loadLibrary("swresample-1"); System.loadLibrary("swscale-3"); System.loadLibrary("native-lib"); } public native void playSound(String input); private AudioTrack audioTrack; // 这个方法 是C进行调用 public void createTrack(int sampleRateInHz,int nb_channals) { int channaleConfig;//通道数 if (nb_channals == 1) { channaleConfig = AudioFormat.CHANNEL_OUT_MONO; } else if (nb_channals == 2) { channaleConfig = AudioFormat.CHANNEL_OUT_STEREO; }else { channaleConfig = AudioFormat.CHANNEL_OUT_MONO; } int buffersize=AudioTrack.getMinBufferSize(sampleRateInHz, channaleConfig, AudioFormat.ENCODING_PCM_16BIT); audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,sampleRateInHz,channaleConfig, AudioFormat.ENCODING_PCM_16BIT,buffersize,AudioTrack.MODE_STREAM); audioTrack.play(); } //C传入音频数据 public void playTrack(byte[] buffer, int lenth) { if (audioTrack != null && audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { audioTrack.write(buffer, 0, lenth); } }}
小结
按照流程,使用AudioTrack播放音频就是这么回事。通过和上篇的播放视频相比较,流程就差不多了:注册->解封装->获取流->解码->播放。
源码地址
更多相关文章
- Android学习笔记(4)——Android(安卓)Application是如何运行的
- Android应用正确使用扩展SD卡
- android使用http协议上传文件
- Android(安卓)Studio使用手册
- android传递参数
- 软键盘显示及属性android:windowSoftInputMode使用总结 & [转一
- Android设置Settings:ListPreference【3】
- Android(安卓)NDK开发之旅35--FFmpeg+AudioTrack音频播放
- Android修行之路——Android程序设计基础(三)