专栏首页曾大稳的博客ffmpeg android音频解码

ffmpeg android音频解码

音频解码就是将mp3 aac等格式这些文件解析为pcm格式的过程。 和视频解码流程一样,只是有些函数不一样

#include "lang.h";
#include <string>
//封装格式
//解码
#include "log.h"

extern "C" {
#include <libswresample/swresample.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

};

#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio

static int audio_decode_example(const char *input, const char *output) {
    AVCodec *pCodec;
    AVCodecContext *pCodecContext;
    AVFormatContext *pFormatContext;
    struct SwrContext *au_convert_ctx;


    uint8_t *out_buffer;

    //1. 注册
    av_register_all();
    //2.打开解码器 <-- 拿到解码器  <-- 拿到id <-- 拿到stream和拿到AVCodecContext <-- 拿到AVFormatContext

    //2.1 拿到AVFormatContext
    pFormatContext = avformat_alloc_context();
    //2.1.1 打开文件
    if (avformat_open_input(&pFormatContext, input, NULL, NULL) != 0) {
        LOGE("打开文件失败!");
        return -1;
    }
    //2.2 拿到AVCodecContext
    //2.2.1 拿到流信息
    if (avformat_find_stream_info(pFormatContext, NULL) < 0) {
        LOGE("AVFormatContext获取流信息失败!");
        return -1;
    }
    //打印信息
//    av_dump_format(pFormatContext, 0, input, false);

    //2.2.2 通过streams找到audio的索引下标 也就获取到了stream
    int audioStream = -1;
    int i = 0;
    for (; i < pFormatContext->nb_streams; i++)
        if (pFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            audioStream = i;
            break;
        }

    if (audioStream == -1) {
        LOGE("AVMEDIA_TYPE_AUDIO索引没找到!");
        return -1;
    }
    //2.2.3 获取到AVCodecContext
    pCodecContext = pFormatContext->streams[audioStream]->codec;

    //2.2.4 通过AVCodecContext拿到id ,拿到解码器
    pCodec = avcodec_find_decoder(pCodecContext->codec_id);
    if (pCodec == NULL) {
        LOGE("AVCodec获取失败!");
        return -1;
    }
    //2.2.5 打开解码器
    if (avcodec_open2(pCodecContext, pCodec, NULL) < 0) {
        LOGE("打开解码器失败!");
        return -1;
    }

    //3. 解码  将解码数据封装在AVFrame <-- 拿到编码的数据AVPacket  <-- 读取数据源 <-- 解码文件参数设置

    //3.1 AVPacket初始化
    AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));
    av_init_packet(packet);

    //3.2 解码文件参数设置
    uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;

    //nb_samples: AAC-1024 MP3-1152
    //音频帧中每个声道的采样数
    int out_nb_samples = pCodecContext->frame_size;

    //音频采样格式 量化精度
    AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
    //采样率
    int out_sample_rate = 44100;
    //声道
    int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);

    //获取到 缓冲大小
    int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples,
                                                     out_sample_fmt, 1);
    out_buffer = (uint8_t *) av_malloc(MAX_AUDIO_FRAME_SIZE * 2);

    //3.3 初始化AVFrame
    AVFrame *pFrame = av_frame_alloc();


    //3.4 获取到编码文件的参数信息
    //声道
    int64_t in_channel_layout = av_get_default_channel_layout(pCodecContext->channels);

    //3.5 参数设置
    au_convert_ctx = swr_alloc();
    au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, out_channel_layout, out_sample_fmt,
                                        out_sample_rate,
                                        in_channel_layout, pCodecContext->sample_fmt,
                                        pCodecContext->sample_rate, 0, NULL);
    swr_init(au_convert_ctx);

    //4. 读取编码数据到AVPacket 然后将数据解码存储到AVFrame  转换存储数据
    //4.1 读取编码数据到AVPacket
    int got_picture;
    int index = 0;
    FILE *outputFile = fopen(output, "wb");
    while (av_read_frame(pFormatContext, packet) >= 0) {
        if (packet->stream_index == audioStream) {
            //4.2 将数据解码存储到AVFrame
            if (avcodec_decode_audio4(pCodecContext, pFrame, &got_picture, packet) < 0) {
                LOGE("解码失败");
                return -1;
            }

            if (got_picture > 0) {
                //4.3 转换音频数据
                swr_convert(au_convert_ctx, &out_buffer, MAX_AUDIO_FRAME_SIZE,
                            (const uint8_t **) pFrame->data, pFrame->nb_samples);
                LOGE("index:%5d\t pts:%lld\t packet size:%d\n",index,packet->pts,packet->size);              
                            
                //4.4 存储数据
                fwrite(out_buffer, 1, static_cast<size_t>(out_buffer_size), outputFile);
                index++;  
            }
        }
        //5. 释放相关资源
        av_packet_unref(packet);
    }

    swr_free(&au_convert_ctx);
    fclose(outputFile);
    av_free(out_buffer);
    // Close the codec
    avcodec_close(pCodecContext);
    // Close the video file
    avformat_close_input(&pFormatContext);

    return 0;
}


extern "C"
JNIEXPORT jint JNICALL
Java_zzw_com_ffmpegdemo_VideoUtils_audio_1decode(JNIEnv *env, jclass type, jstring input_,
                                                 jstring output_) {
    const char *input = env->GetStringUTFChars(input_, 0);
    const char *output = env->GetStringUTFChars(output_, 0);
    int flog = audio_decode_example(input, output);

    env->ReleaseStringUTFChars(input_, input);
    env->ReleaseStringUTFChars(output_, output);

    return flog;
}

参考地址: https://blog.csdn.net/leixiaohua1020/article/details/46890259

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ffmpeg 视频解码h264和yuv

    之前学习 ffmpeg 在 android 平台上,发现很不方便,所以打算在 vs 上重新搭建环境,然后重新学习,之后如果需要用到的话在移植到其他平台。环境搭建...

    曾大稳
  • 手写 ButterKnife BindView

    先建三个module,分别为Butterknife ButterKnife-Annotions ButterKnife-compiler,其中butterk...

    曾大稳
  • ffmpeg添加水印和滤镜效果

    更多的特效使用: http://www.ffmpeg.org/ffmpeg-filters.html

    曾大稳
  • HTML5新特性

    HTML5是下一代HTML标准,是HTML最新的修订版本,2014年10月由万维网联盟W3C完成标准制定,HTML5将HTML从用于构造一个文档的一个简单标记,...

    WindrunnerMax
  • 使用Lambda和API网关在Java中开发RESTful微服务

    原题:Developing RESTful APIs in Java using Amazon APIGateway and AWS Lambda

    yuanyi928
  • 移动端Web页面常见问题解决

    经过研究,是devicePixelRatio作怪,因为手机分辨率太小,如果按照分辨率来显示网页,这样字会非常小,所以苹果当初就把iPhone 4的960*640...

    空空云
  • 利用shell脚本生成动态sql(67天)

    在一些分布式环境中,可能涉及到的数据库有很多,相关的数据库用户也不少,有些看似简单的变更可能需要在不同的库,不同的用户间要进行复杂的操作。 现在我们有3套环境,...

    jeanron100
  • Neo4J:spring-boot-starter-data-neo4j简单应用

    http://localhost:8080/person/addPerson/qiang

    程裕强
  • CNN卷积算法应用---手写数字识别

    98k
  • 【观点】用数据分析的方法来研究历史

    ▌量化历史 之前对历史的探究要么过于意识形态化,要么又过于侧重朝代史、政治史,就如电视古装戏几乎走不出宫廷斗争的话题;还有就是过于定性,停留在史料整理和描述性层...

    小莹莹

扫码关注云+社区

领取腾讯云代金券