使用Gstreamer 作為數(shù)據(jù)源輸出視頻數(shù)據(jù) VI 集成gstreamer

VcamSource 接口

在VcamSource的數(shù)據(jù)源接口中,我們提供了6個(gè)必要的接口 :

gshort vcam_source_start(VcamSource* self);

void  vcam_source_get_mediatype(VcamSource* self, VcamMediaInfo* info);

void  vcam_source_pull_sample(VcamSource* self, GstSample* sample);

void  vcam_source_pull_preroll(VcamSource* self, GstSample* sample);

void  vcam_source_pull_sample2(VcamSource* self, GstMapInfo* map);

void  vcam_source_pull_preroll2(VcamSource* self, GstMapInfo* map);

start接口用來(lái)啟動(dòng)gstreamer的pipleLine, 它應(yīng)該在dll被啟動(dòng)的時(shí)候調(diào)用枕赵,并且在dll退出的時(shí)候調(diào)用unref.
vcam_source_get_mediatype用來(lái)獲取媒體類(lèi)型饰剥,用于directshow組件的連接湃密。
其余4個(gè)都是獲取數(shù)據(jù)接口占调。我們主要使用vcam_source_pull_sample2來(lái)獲取自動(dòng)生成的數(shù)據(jù)正什。

我們需要將庫(kù)和頭文件引入directshow項(xiàng)目中,同時(shí)將該項(xiàng)目的靜態(tài)圖,加入連接配置:


image.png

image.png

gst-vcam集成VcamSource

dllmain的修改

在dllmain.cpp中引入頭文件VcamSource.h, 在入口函數(shù)dllmain處裹唆,我們需要調(diào)用gstrreamer的初始化函數(shù)誓斥,確保只調(diào)用一次。

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    gst_init(NULL, NULL);
    return DllEntryPoint((HINSTANCE)(hModule), ul_reason_for_call, lpReserved);
}

gst-vcam.h的修改

增加一個(gè)私有屬性许帐, VcamSource指針劳坑,用來(lái)維護(hù)對(duì)vcamsoure的引用,另外增加析構(gòu)函數(shù)的聲明:

class CVCam : public CSource
{
......
       CVCam::~CVCam();
protected:
    CVCamStream* stream = nullptr;
        //reference to gstreamer source
    VcamSource* source=nullptr;

};

CVCam類(lèi)的修改

修改CVCam,在返回cvcam的時(shí)候成畦,同時(shí)創(chuàng)建一個(gè)VcamSource的實(shí)例距芬,這個(gè)實(shí)例有CVCam保存:

CVCam::CVCam(LPUNKNOWN lpunk, HRESULT* phr, const GUID id) :
    CSource(NAME("GST Virtual CAM"), lpunk, id)
{
    ASSERT(phr);
        .......
     source =(VcamSource*) g_object_new(VCAM_TYPE_SOURCE, NULL);
         vcam_source_start(source);
}

新增一個(gè)析構(gòu)函數(shù),用來(lái)在cvcam退出時(shí)循帐,減少VcamSource的引用:

CVCam::~CVCam()
{   
    g_object_unref(source);
}

CVCamStream類(lèi)的修改

在CVCamStream類(lèi)中框仔,我們首先需要完成fillBuffer的工作,source接口提供了vcam_source_pull_sample2方法拄养,方便我們來(lái)獲取gstreamer生成的數(shù)據(jù)离斩。

HRESULT CVCamStream::FillBuffer(IMediaSample* pms) {
    REFERENCE_TIME rtNow;

    REFERENCE_TIME avgFrameTime = ((VIDEOINFOHEADER*)m_mt.pbFormat)->AvgTimePerFrame;  //獲取每幀播放間隔

    rtNow = m_rtLastTime;  //獲取上次計(jì)算的最后時(shí)間
    m_rtLastTime += avgFrameTime;  //計(jì)算本幀結(jié)束時(shí)間
    pms->SetTime(&rtNow, &m_rtLastTime);  //這只本真起始時(shí)間和結(jié)束時(shí)間
    pms->SetSyncPoint(TRUE);  //按本幀時(shí)間同步

    BYTE* pData;
    long lDataLen;
    pms->GetPointer(&pData);  //獲取buffer數(shù)據(jù)塊指針
   /***
      在本初填充真實(shí)數(shù)據(jù)
 **/
    GstMapInfo map;
    vcam_source_pull_sample2(parent->source, &map);
    if (map.data == NULL) {
        return ERROR_EMPTY;
    }
    int leinght= map.size;
    if (pms->GetSize() < map.size) leinght = pms->GetSize();

    for (int i= 0; i < leinght; ++i) {
        pData[i] = map.data[i];
    }
    for (int i = leinght; i < pms->GetSize(); ++i) {
        pData[i] = rand();
    }

    //g_object_unref(map);
    return NOERROR;
}

這里通過(guò)GstMapInfo map獲取來(lái)在gstreamer的數(shù)據(jù),然后把他填充到directshow的buffer里瘪匿。二外做的循環(huán)跛梗,時(shí)為了測(cè)試數(shù)據(jù)幀大小不一致時(shí),使用隨機(jī)數(shù)填充棋弥,方便界面發(fā)現(xiàn)茄袖。實(shí)際驗(yàn)證時(shí)可去除。

除了FillBuffer外嘁锯,另外兩個(gè)重要修改的方法時(shí)GetStreamCaps和GetMediaType,這里暫時(shí)不調(diào)用vcam_source_get_mediatype聂薪,減少驗(yàn)證復(fù)雜性:

HRESULT STDMETHODCALLTYPE CVCamStream::GetStreamCaps(int iIndex,
    AM_MEDIA_TYPE** pmt, BYTE* pSCC)
{
    if (iIndex < 0)
        return E_INVALIDARG;

    *pmt = CreateMediaType(&m_mt); //創(chuàng)建媒體類(lèi)型
    DECLARE_PTR(VIDEOINFOHEADER, pvi, (*pmt)->pbFormat); //創(chuàng)建format類(lèi)型

        //設(shè)置format
    pvi->bmiHeader.biWidth = 320;
    pvi->bmiHeader.biHeight = 240;
    pvi->AvgTimePerFrame = 333333;
    pvi->bmiHeader.biCompression = MAKEFOURCC('Y', 'U', 'Y', '2');
    //pvi->bmiHeader.biCompression = BI_RGB;
    pvi->bmiHeader.biBitCount = 16;
    //pvi->bmiHeader.biBitCount = 24;
    pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pvi->bmiHeader.biPlanes = 1;
    //pvi->bmiHeader.biSizeImage = pvi->bmiHeader.biWidth *
    //  pvi->bmiHeader.biHeight * 2;
    pvi->bmiHeader.biSizeImage = GetBitmapSize(&pvi->bmiHeader);
    pvi->bmiHeader.biClrImportant = 0;

    SetRectEmpty(&(pvi->rcSource));
    SetRectEmpty(&(pvi->rcTarget));
    //設(shè)置meida Type
    (*pmt)->majortype = MEDIATYPE_Video;
    (*pmt)->subtype = MEDIASUBTYPE_YUY2;
    //(*pmt)->subtype = MEDIASUBTYPE_RGB24;
    (*pmt)->formattype = FORMAT_VideoInfo;
    (*pmt)->bTemporalCompression = FALSE;
    (*pmt)->bFixedSizeSamples = FALSE;
    (*pmt)->lSampleSize = pvi->bmiHeader.biSizeImage;
    (*pmt)->cbFormat = sizeof(VIDEOINFOHEADER);
    //創(chuàng)建VIDEO_STREAM_CONFIG_CAPS結(jié)構(gòu)體
    DECLARE_PTR(VIDEO_STREAM_CONFIG_CAPS, pvscc, pSCC);
    //設(shè)置VIDEO_STREAM_CONFIG_CAPS
    pvscc->guid = FORMAT_VideoInfo;  //媒體類(lèi)型為FORMAT_VideoInfo
    pvscc->VideoStandard = AnalogVideo_None;  //不是模擬視頻
    pvscc->InputSize.cx = pvi->bmiHeader.biWidth;  //輸入寬度
    pvscc->InputSize.cy = pvi->bmiHeader.biHeight; //輸入高度
    pvscc->MinCroppingSize.cx = pvi->bmiHeader.biWidth;  //最小可裁剪寬度 相當(dāng)于不允許水平裁剪
    pvscc->MinCroppingSize.cy = pvi->bmiHeader.biHeight;//最小可裁剪高度
    pvscc->MaxCroppingSize.cx = pvi->bmiHeader.biWidth;//最大可裁剪寬度
    pvscc->MaxCroppingSize.cy = pvi->bmiHeader.biHeight;//最大可裁剪高度
    pvscc->CropGranularityX = pvi->bmiHeader.biWidth;  //水平裁剪增量
    pvscc->CropGranularityY = pvi->bmiHeader.biHeight; //水平裁剪增量
    pvscc->CropAlignX = 0;  //水平對(duì)其
    pvscc->CropAlignY = 0;  //垂直對(duì)齊

    pvscc->MinOutputSize.cx = pvi->bmiHeader.biWidth;   //最小輸出寬度
    pvscc->MinOutputSize.cy = pvi->bmiHeader.biHeight;   //最小輸出高度度
    pvscc->MaxOutputSize.cx = pvi->bmiHeader.biWidth; // 最大輸出寬度
        pvscc->MaxOutputSize.cy = pvi->bmiHeader.biHeight;  //最大輸出高度度
    pvscc->OutputGranularityX = 0;  //水平輸出變化增量
    pvscc->OutputGranularityY = 0;  //垂直輸出變化增量
    pvscc->StretchTapsX = 0;   //不允許tretching
    pvscc->StretchTapsY = 0;    //不允許tretching
    pvscc->ShrinkTapsX = 0;     //不允許Shrink
    pvscc->ShrinkTapsY = 0;     //不允許Shrink
    pvscc->MinFrameInterval = pvi->AvgTimePerFrame;   //最小幀間隔=平均幀播放時(shí)間
    pvscc->MaxFrameInterval = pvi->AvgTimePerFrame; //最大幀間隔=平均幀播放時(shí)間
    pvscc->MinBitsPerSecond = pvi->bmiHeader.biWidth * pvi->bmiHeader.biHeight
        * 2 * 8 * (10000000 / pvi->AvgTimePerFrame);  //最小輸出數(shù)據(jù)bit單位
    pvscc->MaxBitsPerSecond = pvi->bmiHeader.biWidth * pvi->bmiHeader.biHeight
        * 2 * 8 * (10000000 / pvi->AvgTimePerFrame); // 最大輸出數(shù)據(jù)bit單位

        return S_OK;
}

上面的媒體類(lèi)型和format格式家乘,對(duì)應(yīng)在gstramer重,如下:

gst-launch-1.0 -v videotestsrc ! video/x-raw,width=320,height=240,FORMAT=YUY2,framerate=30/1 ! autovideosink

完成后藏澳,build gst-vcam項(xiàng)目仁锯,生成 gstvcam.dll ; 使用regsvr32 gstvcam.dll 進(jìn)行注冊(cè)翔悠,生成gst-vcam虛擬攝像頭信息:


image.png

使用graphedit, 創(chuàng)建一個(gè)測(cè)試graph,可以看到业崖,AVI Decompressor被用來(lái)作為轉(zhuǎn)化器:


image.png

同時(shí),使用gst-launch-1.0工具和grapEdit啟動(dòng)流畫(huà)面:
image.png

基本顯示一致蓄愁,只有一個(gè)小區(qū)域顏色不一致双炕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市撮抓,隨后出現(xiàn)的幾起案子妇斤,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件站超,死亡現(xiàn)場(chǎng)離奇詭異荸恕,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)死相,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)融求,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人算撮,你說(shuō)我怎么就攤上這事生宛。” “怎么了钮惠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵茅糜,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我素挽,道長(zhǎng)蔑赘,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任预明,我火速辦了婚禮缩赛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘撰糠。我一直安慰自己酥馍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開(kāi)白布阅酪。 她就那樣靜靜地躺著旨袒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪术辐。 梳的紋絲不亂的頭發(fā)上砚尽,一...
    開(kāi)封第一講書(shū)人閱讀 51,215評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音辉词,去河邊找鬼必孤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瑞躺,可吹牛的內(nèi)容都是我干的敷搪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼幢哨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼赡勘!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起捞镰,我...
    開(kāi)封第一講書(shū)人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤狮含,失蹤者是張志新(化名)和其女友劉穎顽悼,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體几迄,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蔚龙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了映胁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片木羹。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖解孙,靈堂內(nèi)的尸體忽然破棺而出坑填,到底是詐尸還是另有隱情,我是刑警寧澤弛姜,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布脐瑰,位于F島的核電站,受9級(jí)特大地震影響廷臼,放射性物質(zhì)發(fā)生泄漏苍在。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一荠商、第九天 我趴在偏房一處隱蔽的房頂上張望寂恬。 院中可真熱鬧,春花似錦莱没、人聲如沸初肉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)牙咏。三九已至,卻和暖如春嘹裂,著一層夾襖步出監(jiān)牢的瞬間妄壶,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工焦蘑, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盒发。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓例嘱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親宁舰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拼卵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容