VcamSource 接口
在VcamSource的數(shù)據(jù)源接口中,我們提供了6個必要的接口 :
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接口用來啟動gstreamer的pipleLine, 它應(yīng)該在dll被啟動的時候調(diào)用,并且在dll退出的時候調(diào)用unref.
vcam_source_get_mediatype用來獲取媒體類型,用于directshow組件的連接。
其余4個都是獲取數(shù)據(jù)接口。我們主要使用vcam_source_pull_sample2來獲取自動生成的數(shù)據(jù)。
我們需要將庫和頭文件引入directshow項目中,同時將該項目的靜態(tài)圖,加入連接配置:


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的修改
增加一個私有屬性, VcamSource指針,用來維護對vcamsoure的引用,另外增加析構(gòu)函數(shù)的聲明:
class CVCam : public CSource
{
......
CVCam::~CVCam();
protected:
CVCamStream* stream = nullptr;
//reference to gstreamer source
VcamSource* source=nullptr;
};
CVCam類的修改
修改CVCam,在返回cvcam的時候,同時創(chuàng)建一個VcamSource的實例,這個實例有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òu)函數(shù),用來在cvcam退出時,減少VcamSource的引用:
CVCam::~CVCam()
{
g_object_unref(source);
}
CVCamStream類的修改
在CVCamStream類中,我們首先需要完成fillBuffer的工作,source接口提供了vcam_source_pull_sample2方法,方便我們來獲取gstreamer生成的數(shù)據(jù)。
HRESULT CVCamStream::FillBuffer(IMediaSample* pms) {
REFERENCE_TIME rtNow;
REFERENCE_TIME avgFrameTime = ((VIDEOINFOHEADER*)m_mt.pbFormat)->AvgTimePerFrame; //獲取每幀播放間隔
rtNow = m_rtLastTime; //獲取上次計算的最后時間
m_rtLastTime += avgFrameTime; //計算本幀結(jié)束時間
pms->SetTime(&rtNow, &m_rtLastTime); //這只本真起始時間和結(jié)束時間
pms->SetSyncPoint(TRUE); //按本幀時間同步
BYTE* pData;
long lDataLen;
pms->GetPointer(&pData); //獲取buffer數(shù)據(jù)塊指針
/***
在本初填充真實數(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;
}
這里通過GstMapInfo map獲取來在gstreamer的數(shù)據(jù),然后把他填充到directshow的buffer里。二外做的循環(huán),時為了測試數(shù)據(jù)幀大小不一致時,使用隨機數(shù)填充,方便界面發(fā)現(xiàn)。實際驗證時可去除。
除了FillBuffer外,另外兩個重要修改的方法時GetStreamCaps和GetMediaType,這里暫時不調(diào)用vcam_source_get_mediatype,減少驗證復(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)建媒體類型
DECLARE_PTR(VIDEOINFOHEADER, pvi, (*pmt)->pbFormat); //創(chuàng)建format類型
//設(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; //媒體類型為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; //水平對其
pvscc->CropAlignY = 0; //垂直對齊
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; //最小幀間隔=平均幀播放時間
pvscc->MaxFrameInterval = pvi->AvgTimePerFrame; //最大幀間隔=平均幀播放時間
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;
}
上面的媒體類型和format格式,對應(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項目,生成 gstvcam.dll ; 使用regsvr32 gstvcam.dll 進行注冊,生成gst-vcam虛擬攝像頭信息:

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

同時,使用gst-launch-1.0工具和grapEdit啟動流畫面:

基本顯示一致,只有一個小區(qū)域顏色不一致。