zoukankan      html  css  js  c++  java
  • Android 音视频深入 十一 FFmpeg和AudioTrack播放声音(附源码下载)

    项目地址,求star
    https://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpeg%E6%92%AD%E6%94%BE%E9%9F%B3%E4%B9%90%EF%BC%88%E4%BF%9D%E7%A8%8B%E5%BA%8F%E4%B8%8D%E6%AD%BB%EF%BC%89


    这个是FFmpeg解码出音频,给AudioTrack播放,这回才算是java与c语言之间合作

    这回我们将会从c++里调用java函数,下面就是关于c++使用AudioTrack的代码

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

    我们再来看看c++的代码

    首先注册组件,然后得到音频流

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

    获取解码器

        //获取解码器上下文
        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();
    

    设置解码出的声音一系列的属性,比如:单声道、双声道、采集点大小、采集率,还可以在这里对声音添加特效,
     

        //得到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);
    //    获取通道数  2
        int out_channer_nb = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);

    通过反射能够运行java函数

    //    反射得到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");


    在一边解码的时候一边给数据给AudioTrack播放

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

    释放资源

        av_frame_free(&frame);
        swr_free(&swrContext);
        avcodec_close(pCodecCtx);
        avformat_close_input(&pFormatCtx);
        env->ReleaseStringUTFChars(input_, input);


    FFmpeg只是音视频处理的工具,他没有播放视频和音频的能力,所以我们需要SurfaceView显示视频,AudioTrack播放声音,而且OpenGLES也能播放声音,这个后面说

    下一次就是说如何将视频的声音给听换掉,也就是将音视频的解码和编码都来搞一次

  • 相关阅读:
    mysql
    makefile
    KNN算法(k-nearest neighbor)
    时间序列
    python易错题之lambda 以及 for循环中内嵌函数
    python易错题之作用域
    html----不常见标签
    js----DOM对象(3
    js----DOM对象(事件)
    js----对象
  • 原文地址:https://www.cnblogs.com/jianpanwuzhe/p/8427415.html
Copyright © 2011-2022 走看看