美文网首页Android音视频系列
FFmpeg用C++多线程解码音频数据

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

作者: ZebraWei | 来源:发表于2019-11-11 17:11 被阅读0次

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

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

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

    相关文章

      网友评论

        本文标题:FFmpeg用C++多线程解码音频数据

        本文链接:https://www.haomeiwen.com/subject/ynitictx.html