FFmpeg音频解码总结

1. 解码流程

在这里插入图片描述

2. 代码实现

extern "C"
JNIEXPORT void JNICALL
Java_com_johan_player_Player_playAudio(JNIEnv *env, jobject instance, jstring path_) {
    
    
    // 记录结果
    int result;
    // R1 Java String -> C String
    const char *path = env->GetStringUTFChars(path_, 0);
    // 注册组件
    av_register_all();
    // R2 创建 AVFormatContext 上下文
    AVFormatContext *format_context = avformat_alloc_context();
    // R3 打开视频文件
    avformat_open_input(&format_context, path, NULL, NULL);
    // 查找视频文件的流信息
    result = avformat_find_stream_info(format_context, NULL);
    if (result < 0) {
    
    
        LOGE("Player Error : Can not find video file stream info");
        return;
    }
    // 查找音频编码器
    int audio_stream_index = -1;
    for (int i = 0; i < format_context->nb_streams; i++) {
    
    
        // 匹配音频流
        if (format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
    
    
            audio_stream_index = i;
        }
    }
    // 没找到音频流
    if (audio_stream_index == -1) {
    
    
        LOGE("Player Error : Can not find audio stream");
        return;
    }
    // 初始化音频编码器上下文
    AVCodecContext *audio_codec_context = avcodec_alloc_context3(NULL);
    avcodec_parameters_to_context(audio_codec_context, format_context->streams[audio_stream_index]->codecpar);
    // 初始化音频编码器
    AVCodec *audio_codec = avcodec_find_decoder(audio_codec_context->codec_id);
    if (audio_codec == NULL) {
    
    
        LOGE("Player Error : Can not find audio codec");
        return;
    }
    // R4 打开视频解码器
    result  = avcodec_open2(audio_codec_context, audio_codec, NULL);
    if (result < 0) {
    
    
        LOGE("Player Error : Can not open audio codec");
        return;
    }
    // 音频重采样准备
    // R5 重采样上下文
    struct SwrContext *swr_context = swr_alloc();
    // 缓冲区
    uint8_t *out_buffer = (uint8_t *) av_malloc(44100 * 2);
    // 输出的声道布局 (双通道 立体音)
    uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
    // 输出采样位数 16位
    enum AVSampleFormat out_format = AV_SAMPLE_FMT_S16;
    // 输出的采样率必须与输入相同
    int out_sample_rate = audio_codec_context->sample_rate;
    //swr_alloc_set_opts 将PCM源文件的采样格式转换为自己希望的采样格式
    swr_alloc_set_opts(swr_context,
                       out_channel_layout, out_format, out_sample_rate,
                       audio_codec_context->channel_layout, audio_codec_context->sample_fmt, audio_codec_context->sample_rate,
                       0, NULL);
    swr_init(swr_context);
    // 调用 Java 层创建 AudioTrack
    int out_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
    jclass player_class = env->GetObjectClass(instance);
    jmethodID create_audio_track_method_id = env->GetMethodID(player_class, "createAudioTrack", "(II)V");
    env->CallVoidMethod(instance, create_audio_track_method_id, 44100, out_channels);
    // 播放音频准备
    jmethodID play_audio_track_method_id = env->GetMethodID(player_class, "playAudioTrack", "([BI)V");
    // 声明数据容器 有2个
    // R6 解码前数据容器 Packet 编码数据
    AVPacket *packet = av_packet_alloc();
    // R7 解码后数据容器 Frame MPC数据 还不能直接播放 还要进行重采样
    AVFrame *frame = av_frame_alloc();
    // 开始读取帧
    while (av_read_frame(format_context, packet) >= 0) {
    
    
        // 匹配音频流
        if (packet->stream_index == audio_stream_index) {
    
    
            // 解码
            result = avcodec_send_packet(audio_codec_context, packet);
            if (result < 0 && result != AVERROR(EAGAIN) && result != AVERROR_EOF) {
    
    
                LOGE("Player Error : codec step 1 fail");
                return;
            }
            result = avcodec_receive_frame(audio_codec_context, frame);
            if (result < 0 && result != AVERROR_EOF) {
    
    
                LOGE("Player Error : codec step 2 fail");
                return;
            }
            // 重采样
            swr_convert(swr_context, &out_buffer, 44100 * 2, (const uint8_t **) frame->data, frame->nb_samples);
            // 播放音频
            // 调用 Java 层播放 AudioTrack
            int size = av_samples_get_buffer_size(NULL, out_channels, 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, play_audio_track_method_id, audio_sample_array, size);
            env->DeleteLocalRef(audio_sample_array);
        }
        // 释放 packet 引用
        av_packet_unref(packet);
    }
    // 调用 Java 层释放 AudioTrack
    jmethodID release_audio_track_method_id = env->GetMethodID(player_class, "releaseAudioTrack", "()V");
    env->CallVoidMethod(instance, release_audio_track_method_id);
    // 释放 R7
    av_frame_free(&frame);
    // 释放 R6
    av_packet_free(&packet);
    // 释放 R5
    swr_free(&swr_context);
    // 关闭 R4
    avcodec_close(audio_codec_context);
    // 关闭 R3
    avformat_close_input(&format_context);
    // 释放 R2
    avformat_free_context(format_context);
    // 释放 R1
    env->ReleaseStringUTFChars(path_, path);
}

猜你喜欢

转载自blog.csdn.net/wyw0000/article/details/130033488