关于

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播放音频就是这么回事。通过和上篇的播放视频相比较,流程就差不多了:注册->解封装->获取流->解码->播放。
源码地址

更多相关文章

  1. Android学习笔记(4)——Android(安卓)Application是如何运行的
  2. Android应用正确使用扩展SD卡
  3. android使用http协议上传文件
  4. Android(安卓)Studio使用手册
  5. android传递参数
  6. 软键盘显示及属性android:windowSoftInputMode使用总结 & [转一
  7. Android设置Settings:ListPreference【3】
  8. Android(安卓)NDK开发之旅35--FFmpeg+AudioTrack音频播放
  9. Android修行之路——Android程序设计基础(三)

随机推荐

  1. Android的应用程序结构分析:HelloActivity
  2. android 布局文件中xmlns:android="http:
  3. android 属性
  4. android横竖屏切换的一点感想
  5. Android架构分析之Android驱动程序开发
  6. Android访问资源与属性之 ? , @
  7. ArcGIS for Android地图控件的5大常见操
  8. Android基础概念
  9. 【Android-002】【常见布局】
  10. android 如何获得系统权限 android.uid.s