FFmpeg用C++多线程解码音频数据

版权声明:本文为卫伟学习总结文章,转载请注明出处!
上几篇文章详解的介绍了使用MediaRecord和AudioRecord两种对音频进行采集的技术,以及FFmpeg包的编辑和裁剪,生成安卓使用的.so库的技术。本文章将详细介绍使用FFmpeg进行音频解码操作。
一、音频解码过程
音频解码过程如下图所示:

FFmpeg用C++多线程解码音频数据_第1张图片

  • av_register_all():的作用是初始化所有组件,只有调用了该函数,才能使用复用器和编解码器(源码)
  • avformat_open_input()/avformat_close_input(): 函数会读文件头,对 mp4 文件而言,它会解析所有的 box。但它知识把读到的结果保存在对应的数据结构下。这个时候,AVStream 中的很多字段都是空白的。
  • av_dump_format(): 打印视音频信息
  • avformat_find_stream_info():读取一部分视音频数据并且获得一些相关的信息,会检测一些重要字段,如果是空白的,就设法填充它们。因为我们解析文件头的时候,已经掌握了大量的信息,avformat_find_stream_info 就是通过这些信息来填充自己的成员,当重要的成员都填充完毕后,该函数就返回了。这中情况下,该函数效率很高。但对于某些文件,单纯的从文件头中获取信息是不够的,比如 video 的 pix_fmt 是需要调用 h264_decode_frame 才可以获取其pix_fmt的。
  • av_find_best_stream(): 获取音视频及字幕的 stream_index , 以前没有这个函数时,我们一般都是写的 for 循环。
  • av_packet_free(): 首先将 AVPacket 指向的数据域的引用技术减1(数据域的引用技术减为0时会自动释放) 接着,释放为 AVPacket 分配的空间。
  • av_packet_unref(): 减少数据域的引用技术,当引用技术减为0时,会自动释放数据域所占用的空间。

二、获取音频Meta信息
对于amr格式的音频文件而言,如何去获取它的采样率以及是否单声道。如果用ffmpeg那很easy就能解决。但问题是不会,那么我们就只能用其他的第三方库。当然如果你了解其原理,甚至可以自己分析二进制文件。

int audio_stream_idx;
AVStream *audio_stream;

av_register_all();
avformat_network_init();
pFormatCtx = avformat_alloc_context();
if(avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0)
{
    if(LOG_DEBUG)
    {
        LOGE("can not open url :%s", url);
    }
    return;
}
if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
{
    if(LOG_DEBUG)
    {
        LOGE("can not find streams from %s", url);
    }
    return;
}

// 获取采样率和通道
audio_stream_idx = av_find_best_stream(pFormatCtx, AVMediaType::AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
audio_stream = pFormatCtx->streams[audio_stream_idx];
LOGE("采样率:%d, 通道数: %d", audio_stream->codecpar->sample_rate, audio_stream->codecpar->channels);


二、解码音频数据
关于解码函数 avcodec_decode_audio4 已经过时了,取而代之的是 avcodec_send_packet 和 avcodec_receive_frame 。

void WlFFmpeg::start() {

if(audio == NULL)
{
    if(LOG_DEBUG)
    {
        LOGE("audio is null");
        return;
    }
}

int count = 0;

while(1)
{
    AVPacket *avPacket = av_packet_alloc();
    if(av_read_frame(pFormatCtx, avPacket) == 0)
    {
        if(avPacket->stream_index == audio->streamIndex) //音频帧数据
        {
            //解码操作
            count++;
            if(LOG_DEBUG)
            {
                LOGE("解码第 %d 帧", count);
            }
            av_packet_free(&avPacket);
            av_free(avPacket);

        } else{
            av_packet_free(&avPacket);
            av_free(avPacket);
        }
    } else{
        if(LOG_DEBUG)
        {
            LOGE("decode finished");
        }
        av_packet_free(&avPacket);
        av_free(avPacket);
        break;
    }
}

三、子线程下进行解码操作

WlFFmpeg::WlFFmpeg(WlCallJava *callJava, const char *url) {
this->callJava = callJava;
this->url = url;
}

void *decodeFFmpeg(void *data)
{
WlFFmpeg *wlFFmpeg = (WlFFmpeg *) data;
wlFFmpeg->decodeFFmpegThread();
pthread_exit(&wlFFmpeg->decodeThread);
}

void WlFFmpeg::parpared() {

pthread_create(&decodeThread, NULL, decodeFFmpeg, this);

}

FFmpeg用C++多线程解码音频数据_第2张图片

[代码](链接: https://pan.baidu.com/s/1RrqmBf2adXygilH5PEMnaQ)
密码:v8ef

你可能感兴趣的:(FFmpeg用C++多线程解码音频数据)