ffmpeg(四)——音频解码

2020-06-29  本文已影响0人  王志强_9380

音频解码和视频解码有很多是相似的,我们直接贴代码

Java_com_example_ffmpegapplication_MyAudioPlayer_playaudio(JNIEnv *env, jobject instance,
                                                           jstring src_, jstring des_) {
    const char *src = env->GetStringUTFChars(src_, 0);
    const char *des = env->GetStringUTFChars(des_, 0);

    avformat_network_init();
    //总上下文
    AVFormatContext *pContext = avformat_alloc_context();

    //字典
    AVDictionary *pDictionary = NULL;

    av_dict_set(&pDictionary, "timeout", "3000000", 0);

    // 打开源文件输入流
    if(avformat_open_input(&pContext, src, NULL, &pDictionary) != 0) {
        avformat_free_context(pContext);
        return;
    }
    // 获取视频流信息,通知ffmpeg把流解析出来
    if(avformat_find_stream_info(pContext,NULL) < 0){
        LOGI("%s","无法获取输入文件信息");
        return;
    }

    //音频流索引
    int audio_stream_index = -1;
    //nb_streams:视频里面流的数量,比如流0是视频,流1是音频,流2是字幕
    for(int i = 0; i < pContext->nb_streams; i++) {
        //codecpar:解码器参数,旧版本的是codec,  codec_type:解码器类型。
        if(pContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
            //如果是音频流,保存索引
            audio_stream_index = i;
            break;
        }
    }
    //音频流的解码参数
    AVCodecParameters *pParameters = pContext->streams[audio_stream_index]->codecpar;

    //通过解码器id找到解码器
    AVCodec *pCodec = avcodec_find_decoder(pParameters->codec_id);

    //创建解码器上下文
    AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec);

    //将解码器参数copy到解码器上下文
    avcodec_parameters_to_context(pCodecContext, pParameters);

    //打开解码器
    avcodec_open2(pCodecContext, pCodec, &pDictionary);

    //音频转换上下文
    SwrContext *pSwrContext = swr_alloc();

    //输入参数
    //输入采样格式
    AVSampleFormat in_sample =  pCodecContext->sample_fmt;
    //输入采样率
    int in_sample_rate = pCodecContext->sample_rate;
    //输入声道布局
    uint64_t in_ch_layout = pCodecContext->channel_layout;

    //输出参数 固定
    //输出采样格式
    AVSampleFormat out_sample = AV_SAMPLE_FMT_S16;
    //输出采样率
    int out_sample_rate = 44100;
    //输出声道布局 AV_CH_LAYOUT_STEREO 双通道
    uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
    //设置转换参数
    swr_alloc_set_opts(pSwrContext,out_ch_layout,out_sample,out_sample_rate
            ,in_ch_layout,in_sample,in_sample_rate,0,NULL);
    //初始化转换器
    swr_init(pSwrContext);

    //为输出文件申请内存
    uint8_t *out_buffer = (uint8_t *)(av_malloc(2 * 44100));
    FILE *fp_pcm = fopen(des, "wb");

    //数据存储在一个AVPacket中 旧版本需要用户自己去malloc。新版本的可以理解为:待解码队列
    AVPacket *pPacket = av_packet_alloc();

    //解码后的yuv数据容器
    AVFrame *pFrame = av_frame_alloc();

    while (av_read_frame(pContext, pPacket) >= 0) {
        //AVCodecContext *avctx, const AVPacket *avpkt  从待解码队列中取出视频流数据发送给解码器
        avcodec_send_packet(pCodecContext, pPacket);
        //AVCodecContext *avctx, AVFrame *frame 从解码器取出解码后到数据(yuv)到frame
        int ret = avcodec_receive_frame(pCodecContext, pFrame);
        if (ret == AVERROR(EAGAIN)) {
            continue;
        } else if (ret < 0) {
            LOGI("%s","解码完成");
            break;
        }

        //frame  ---->统一的格式
        swr_convert(pSwrContext, &out_buffer, 2 * 44100,
                    (const uint8_t **)pFrame->data, pFrame->nb_samples);
        int out_channerl_nb= av_get_channel_layout_nb_channels(out_ch_layout);
        //缓冲区的 大小
        int out_buffer_size=  av_samples_get_buffer_size(NULL, out_channerl_nb, pFrame->nb_samples, out_sample, 1);
        fwrite(out_buffer,1, out_buffer_size, fp_pcm);
    }

    fclose(fp_pcm);
    av_free(out_buffer);
    swr_free(&pSwrContext);
    //释放容器
    av_frame_free(&pFrame);
    //释放AVPacket
    av_packet_free(&pPacket);

    //释放解码器上下文
    avcodec_free_context(&pCodecContext);
    //关闭文件
    avformat_close_input(&pContext);
    //释放总上下文
    avformat_free_context(pContext);

    env->ReleaseStringUTFChars(src_, src);
    env->ReleaseStringUTFChars(des_, des);
}

我们来分析一下代码,相同的就不在看了

初始化网路
打开文件流
初始化解码器
转换器的初始化和使用
输出文件

最终在输出目录下生成一个pcm文件

上一篇 下一篇

猜你喜欢

热点阅读