使用Gstreamer 作为数据源输出视频数据 VI 集成gst
VcamSource 接口
在VcamSource的数据源接口中,我们提供了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, 它应该在dll被启动的时候调用,并且在dll退出的时候调用unref.
vcam_source_get_mediatype用来获取媒体类型,用于directshow组件的连接。
其余4个都是获取数据接口。我们主要使用vcam_source_pull_sample2来获取自动生成的数据。
我们需要将库和头文件引入directshow项目中,同时将该项目的静态图,加入连接配置:
image.png
image.png
gst-vcam集成VcamSource
dllmain的修改
在dllmain.cpp中引入头文件VcamSource.h, 在入口函数dllmain处,我们需要调用gstrreamer的初始化函数,确保只调用一次。
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的引用,另外增加析构函数的声明:
class CVCam : public CSource
{
......
CVCam::~CVCam();
protected:
CVCamStream* stream = nullptr;
//reference to gstreamer source
VcamSource* source=nullptr;
};
CVCam类的修改
修改CVCam,在返回cvcam的时候,同时创建一个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);
}
新增一个析构函数,用来在cvcam退出时,减少VcamSource的引用:
CVCam::~CVCam()
{
g_object_unref(source);
}
CVCamStream类的修改
在CVCamStream类中,我们首先需要完成fillBuffer的工作,source接口提供了vcam_source_pull_sample2方法,方便我们来获取gstreamer生成的数据。
HRESULT CVCamStream::FillBuffer(IMediaSample* pms) {
REFERENCE_TIME rtNow;
REFERENCE_TIME avgFrameTime = ((VIDEOINFOHEADER*)m_mt.pbFormat)->AvgTimePerFrame; //获取每帧播放间隔
rtNow = m_rtLastTime; //获取上次计算的最后时间
m_rtLastTime += avgFrameTime; //计算本帧结束时间
pms->SetTime(&rtNow, &m_rtLastTime); //这只本真起始时间和结束时间
pms->SetSyncPoint(TRUE); //按本帧时间同步
BYTE* pData;
long lDataLen;
pms->GetPointer(&pData); //获取buffer数据块指针
/***
在本初填充真实数据
**/
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的数据,然后把他填充到directshow的buffer里。二外做的循环,时为了测试数据帧大小不一致时,使用随机数填充,方便界面发现。实际验证时可去除。
除了FillBuffer外,另外两个重要修改的方法时GetStreamCaps和GetMediaType,这里暂时不调用vcam_source_get_mediatype,减少验证复杂性:
HRESULT STDMETHODCALLTYPE CVCamStream::GetStreamCaps(int iIndex,
AM_MEDIA_TYPE** pmt, BYTE* pSCC)
{
if (iIndex < 0)
return E_INVALIDARG;
*pmt = CreateMediaType(&m_mt); //创建媒体类型
DECLARE_PTR(VIDEOINFOHEADER, pvi, (*pmt)->pbFormat); //创建format类型
//设置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));
//设置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);
//创建VIDEO_STREAM_CONFIG_CAPS结构体
DECLARE_PTR(VIDEO_STREAM_CONFIG_CAPS, pvscc, pSCC);
//设置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; //最小可裁剪宽度 相当于不允许水平裁剪
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); //最小输出数据bit单位
pvscc->MaxBitsPerSecond = pvi->bmiHeader.biWidth * pvi->bmiHeader.biHeight
* 2 * 8 * (10000000 / pvi->AvgTimePerFrame); // 最大输出数据bit单位
return S_OK;
}
上面的媒体类型和format格式,对应在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虚拟摄像头信息:
image.png
使用graphedit, 创建一个测试graph,可以看到,AVI Decompressor被用来作为转化器:
image.png
同时,使用gst-launch-1.0工具和grapEdit启动流画面:
image.png
基本显示一致,只有一个小区域颜色不一致。