android ffmepg 实现音乐的播放的例子(使用AudioTrack进行播放)

本文主要是写了一个demo,基于ffmpeg实现android音乐文件的解码,播放功能,主要是用来练习,大家也可以参考下,用来学习

基础知识

名称作用
AVFormatContextAVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数
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

https://www.cnblogs.com/mfmdaoyou/p/7348969.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值