天天看點

FFmpeg音視訊解碼及單多線程測試

一:解碼器初始化步驟

AVCodec *codec = avcodec_find_decoder(ic->streams[videoStream]->codecpar->codec_id);
 AVCodecContext *vc = avcodec_alloc_context3(codec)
        avcodec_parameters_to_context(vc,ic->streams[videoStream]->codecpar)
        vc->thread_count=1;
        re=avcodec_open2(vc, 0, 0);
           

avcodec_find_decoder擷取解碼器方法,avcodec_alloc_context3擷取視訊解碼器上下文,thread_count設定解碼器線程數,

avcodec_open2打開解碼器。

同樣音頻解碼器初始化方式一樣:

AVCodec *acodec = avcodec_find_decoder(ic->streams[audioStream]->codecpar->codec_id);
 AVCodecContext *ac = avcodec_alloc_context3(acodec);
  avcodec_parameters_to_context(ac,ic->streams[audioStream]->codecpar);
 ac->thread_count=1;
 re=avcodec_open2(ac, 0, 0);
           

注意avcodec_find_decoder中所傳參數。

二:解碼

解碼部分主要涉及兩個重要步驟,上傳和擷取。上傳是将原始資料上傳解碼器進行解碼,擷取是接受解碼好的幀資料。

主要方法:avcodec_send_packet(cc,pkt); avcodec_receive_frame(cc,frame); 第一個參數都是AVCodecContext 解碼上下文。

AVPacket和AVFrame聲明

AVPacket *pkt=av_packet_alloc();

AVFrame *frame=av_frame_alloc();

具體代碼:

AVPacket *pkt=av_packet_alloc();
        AVFrame  *frame=av_frame_alloc();
        long long start=getNowMs();
        int frameCount=0;
        for(;;)
        {
            if(getNowMs()-start>=3000)
            {
                LOGW("now decode fps is %d",frameCount/3);
                start=getNowMs();
                frameCount=0;
            }

            int re = av_read_frame(ic,pkt);
            if(re != 0)
            {

                LOGW("讀取到結尾處!");
                int pos = 20 * r2d(ic->streams[videoStream]->time_base);
                av_seek_frame(ic,videoStream,pos,AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME );
                continue;
            }

            AVCodecContext *cc = vc;
            if(pkt->stream_index == audioStream)
                cc=ac;

            //發送到線程中解碼
            re = avcodec_send_packet(cc,pkt);
            //清理
            int p = pkt->pts;
            av_packet_unref(pkt);

            if(re != 0)
            {
                LOGW("avcodec_send_packet failed!");
                continue;
            }
            for(;;)
            {
                re = avcodec_receive_frame(cc,frame);
                if(re !=0)
                {
                    //LOGW("avcodec_receive_frame failed!");
                    break;
                }
                LOGW("avcodec_receive_frame %lld",frame->pts);
                if (cc == vc) {
                    frameCount++;
                }
            }
        }
    }else{
        LOGW("avformat_open_input %s error", av_err2str(re));
    }
           
FFmpeg音視訊解碼及單多線程測試

這塊要判斷是音頻流還是視訊流,上下文參數不一樣。

frameCount是用于記錄幀數。上面使用的線程數是1,三秒鐘列印一次幀數:

FFmpeg音視訊解碼及單多線程測試

日志中看到的fps為100左右 ,這個是在開啟neon下線程數為1的速度。如果測試不支援neon可以替換之前編譯的未開啟neon庫。

此時的Cpu占用情況:

FFmpeg音視訊解碼及單多線程測試

把解碼器的thread_count 修改為8再測試:

FFmpeg音視訊解碼及單多線程測試

此時fps為300左右,解碼速度明顯提升了很多。

再看下Cpu情況:

FFmpeg音視訊解碼及單多線程測試

Cpu的使用率上升到70-80。

三:硬解碼測試

FFmpeg音視訊解碼及單多線程測試

将codec初始化為硬解碼,調用java的mediacodec(反射),需要手動配置虛拟機環境

FFmpeg音視訊解碼及單多線程測試

注意引用的so庫要是支援硬解碼的,相關的編譯參數配置正确,不然會報錯。

FFmpeg音視訊解碼及單多線程測試

看到Cpu的使用率下降到8

再看下fps:

FFmpeg音視訊解碼及單多線程測試

關于軟硬解碼的差別網上有很多詳細的文章,軟解碼使用Cpu進行是以對于處理器來說占用率很大,硬解碼使用非cpu解碼如Gpu等。軟解碼Cpu負載率重性能比硬解碼低,低碼率下品質不如硬解碼。

項目連接配接:編譯好的So庫