FFmpeg安卓流媒體播放項(xiàng)目05 : IDecode和FFDecode模塊代碼創(chuàng)建

音視頻解碼接口類 :IDecode

#include "XParameter.h"
#include "IObserver.h"
#include <list>

//解碼接口, 支持硬解碼
class IDecode : public IObserver {
public:
    //打開解碼器
    virtual bool open(XParameter parameter) = 0;

    //future模式 發(fā)送數(shù)據(jù)到線程解碼
    virtual bool sendPacket(XData pkt) = 0;

    //從線程中獲取解碼結(jié)果, 并不會阻塞, 再次調(diào)用會復(fù)用上次空間, 線程不安全
    virtual XData recvFrame() = 0;

    //由主體notift的數(shù)據(jù) 達(dá)到最大隊(duì)列緩沖則阻塞
    virtual void update(XData xData);

    bool isAudio = false;

    //最大的隊(duì)列緩沖
    int maxList = 100;
protected:
    virtual void main();
    //讀取緩沖
    std::list<XData> xDataList;
    std::mutex xDataListMutex;
};

#endif //BOPLAY_IDECODE_H
  • IDecode.cpp
#include "IDecode.h"
#include "XLog.h"

void IDecode::main() {
    while(!isExit){
        xDataListMutex.lock();
        if (xDataList.empty()){
            xDataListMutex.unlock();
            XSleep(1);
            continue;
        }
        //取出packet 消費(fèi)者
        XData xData = xDataList.front();
        xDataList.pop_front();

        //開始解碼
        //發(fā)送數(shù)據(jù)到解碼線程, 音頻一個數(shù)據(jù)包可能解碼多個結(jié)果
        if(sendPacket(xData)){
            while(!isExit){
                //獲取解碼數(shù)據(jù)
                XData frame = recvFrame();
                if (!frame.data){
                    break;
                }
                //發(fā)送數(shù)據(jù)給觀察者,解碼模塊得觀察者是音頻重采樣模塊IResample或視頻渲染模塊IVideoView
                this->Notify(frame);
            }
        }
        //因?yàn)檫@是從xDataList取出的?
        xData.Drop();
        xDataListMutex.unlock();
    }

}

void IDecode::update(XData xData) {
    if (xData.isAudio != isAudio){
        return;
    }
    while(!isExit){
        xDataListMutex.lock();
        if (xDataList.size() < maxList){
            //生產(chǎn)者
            xDataList.push_back(xData);
            xDataListMutex.unlock();
            break;
        }
        xDataListMutex.unlock();
        //休眠1毫秒防止消耗資源
        XSleep(1);
    }
}

音視頻解碼適配器類 :FFDecode

  • 編碼器初始化函數(shù)
    主要流程:1、查找解碼器;2、創(chuàng)建編碼器上下文臣嚣,并復(fù)制參數(shù);3剥哑、打開解碼器
bool FFDecode::open(XParameter parameter, bool isHard) {
    //在重新打開時優(yōu)先釋放掉上次打開的資源硅则,
    close();
    if(nullptr == parameter.para){
        return false;
    }
    //解碼器參數(shù)
    AVCodecParameters *para = parameter.para;
    //1.查找解碼器
    //AVCodec是存儲編解碼器信息的結(jié)構(gòu)體
    AVCodec *avc = avcodec_find_decoder(para->codec_id);
    if (isHard){
        //如果移植到其他平臺代碼要做調(diào)整
        avc = avcodec_find_decoder_by_name("h264_mediacodec");
    }

    if(!avc){
        XLOGI("avcodec_find_decoder %d failed! %d", para->codec_id, isHard);
    }
    XLOGI("avcodec_find_decoder %d successfully! %d", para->codec_id, isHard);
    //2.創(chuàng)建解碼器上下文, 并復(fù)制參數(shù)
    std::unique_lock<std::mutex> lock{codecContextMutex};
    //AVCodecContex結(jié)構(gòu)體除了包含解碼器之外還包含視音頻流相關(guān)的信息,如寬高株婴,比特率怎虫,聲道數(shù)等信息
    codecContext = avcodec_alloc_context3(avc);
    //參數(shù) AVCodecParameters為編解碼器的相關(guān)參數(shù)暑认,是從AVCodecContext分離出來,其結(jié)構(gòu)體中沒有函數(shù)
    avcodec_parameters_to_context(codecContext, para);

    //多線程解碼
    codecContext->thread_count = 8;
    //3.打開解碼器
    //該函數(shù)用于初始化一個視音頻編解碼器的AVCodecContext
    int re = avcodec_open2(codecContext, nullptr, nullptr);
    if (re != 0){
        char buf[1024] = {0};
        av_strerror(re, buf, sizeof(buf) - 1);
        XLOGE("%s", buf);
        return false;
    }
    XLOGI("avcodec_open2 successfully!");

    if (AVMEDIA_TYPE_VIDEO == codecContext->codec_type){
        this->isAudio = false;
    } else if (AVMEDIA_TYPE_AUDIO == codecContext->codec_type){
        this->isAudio = true;
    }
    return true;
}
  • 發(fā)送AVPacket編碼數(shù)據(jù)包到ffmpeg的解碼線程
bool FFDecode::sendPacket(XData xData) {
    //臨時變量
    if (xData.size <= 0 || !xData.data){
        return false;
    }

    //codecContext類成員, 多線程訪問的變量
    std::unique_lock<std::mutex> lock(codecContextMutex);
    if (!codecContext){
        return false;
    }

    int re = avcodec_send_packet(codecContext, (AVPacket *)xData.data);
    return re == 0;
}
  • 接收ffmpeg解碼后的幀數(shù)據(jù)
XData FFDecode::recvFrame() {
    std::unique_lock<std::mutex> lock(codecContextMutex);
    if (!codecContext){
        return XData{};
    }
    if (!frame){
        frame = av_frame_alloc();
    }
    //再次調(diào)用frame會復(fù)用上次空間, 線程不安全
    int re = avcodec_receive_frame(codecContext, frame);
    if (re != 0){
        return XData{};
    }
    XData xData;
    xData.data = (unsigned char*)frame;
    if (AVMEDIA_TYPE_VIDEO == codecContext->codec_type){
        //視頻幀大小大审,幀寬蘸际,幀高
        xData.size = (frame->linesize[0] + frame->linesize[1] + frame->linesize[2]) * frame->height;
        xData.width = frame->width;
        xData.height = frame->height;
    }else if (AVMEDIA_TYPE_AUDIO == codecContext->codec_type){
        //音頻幀大小 = 樣本字節(jié)數(shù) * 單通道樣本數(shù) * 通道數(shù)
        xData.size = av_get_bytes_per_sample((AVSampleFormat)frame->format) * frame->nb_samples * frame->channels;
    }
    //遇到問題記錄, 這邊把xData.datas寫成xData.data導(dǎo)致uv數(shù)據(jù)沒有復(fù)制, 只有y, 有圖像運(yùn)行但是是綠色的
    xData.format = frame->format;
    memcpy(xData.datas, frame->data, sizeof(xData.datas));
    xData.pts = (int)frame->pts;
    pts = xData.pts;
    return xData;
}
  • 硬解碼初始化
//硬解碼初始化
//https://blog.csdn.net/zhangpengzp/article/details/88943867
void FFDecode::initHard(void *vm) {
    av_jni_set_java_vm(vm, nullptr);
}
  • 關(guān)閉解碼模塊
void FFDecode::close() {
    //清空AVPacket緩沖
    IDecode::clear();
    codecContextMutex.lock();
    pts = 0;
    if (frame){
        av_frame_free(&frame);
    }
    if (codecContext){
        avcodec_close(codecContext);
        avcodec_free_context(&codecContext);
    }
    codecContextMutex.unlock();
}
  • 丟掉解碼器中緩存幀
void FFDecode::clear() {
    IDecode::clear();
    mux.lock();
    //丟掉解碼器中緩存幀
    if (codecContext){
        avcodec_flush_buffers(codecContext);
    }
    mux.unlock();
}
  • FFDecode.h
#ifndef BOPLAY_FFDECODE_H
#define BOPLAY_FFDECODE_H

#include "IDecode.h"

struct AVCodecContext;
struct AVFrame;

class FFDecode : public IDecode {
public:
    static void initHard(void *vm);

    virtual bool open(XParameter parameter, bool isHard = false);
    virtual void close();

    //future模型 發(fā)送數(shù)據(jù)到線程解碼
    virtual bool sendPacket(XData pkt);

    //從線程中獲取解碼結(jié)果, 并不會阻塞, 再次調(diào)用會復(fù)用上次空間, 線程不安全
    virtual XData recvFrame();

    virtual void clear();

protected:
    AVCodecContext *codecContext = 0;
    AVFrame *frame = 0;
    std::mutex codecContextMutex;
};


#endif //BOPLAY_FFDECODE_H
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市徒扶,隨后出現(xiàn)的幾起案子粮彤,更是在濱河造成了極大的恐慌,老刑警劉巖姜骡,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異圈澈,居然都是意外死亡惫周,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門士败,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闯两,“玉大人褥伴,你說我怎么就攤上這事谅将。” “怎么了重慢?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵饥臂,是天一觀的道長。 經(jīng)常有香客問我似踱,道長隅熙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任核芽,我火速辦了婚禮囚戚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘轧简。我一直安慰自己驰坊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布哮独。 她就那樣靜靜地躺著拳芙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪皮璧。 梳的紋絲不亂的頭發(fā)上舟扎,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天,我揣著相機(jī)與錄音悴务,去河邊找鬼睹限。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的羡疗。 我是一名探鬼主播删窒,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼顺囊!你這毒婦竟也來了肌索?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤特碳,失蹤者是張志新(化名)和其女友劉穎诚亚,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體午乓,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡站宗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了益愈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梢灭。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蒸其,靈堂內(nèi)的尸體忽然破棺而出敏释,到底是詐尸還是另有隱情,我是刑警寧澤摸袁,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布钥顽,位于F島的核電站,受9級特大地震影響靠汁,放射性物質(zhì)發(fā)生泄漏蜂大。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一蝶怔、第九天 我趴在偏房一處隱蔽的房頂上張望奶浦。 院中可真熱鬧,春花似錦踢星、人聲如沸澳叉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耳高。三九已至,卻和暖如春所踊,著一層夾襖步出監(jiān)牢的瞬間泌枪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工秕岛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碌燕,地道東北人误证。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像修壕,于是被迫代替她去往敵國和親愈捅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348