decklink:采集 DeckLink板卡

1,板卡的作用

DeckLinkSDK为Blackmagic Design的稳定的跨平台界面提供了捕获和回放产品。

2,板卡的对象接口

API中的功能可以通过“对象接口”进行访问。系统中的每个对象都可以继承自多个对象接口并被访问。通常,开发人员能够与对象接口进行交互,并让底层对象自己对自己进行管理。

3,如何访问DeckLink设备?

大多数DeckLinkAPI对象接口都可以通过IDeckLinkIterator对象进行访问。

windows:

IDeckLinkIterator *deckLinkIterator = NULL; CoCreateInstance (CLSID _ CDeckLinkIterator, NULL, CLSCTX _ ALL, IID _ IDeckLinkIterator, (void*)&deckLinkIterator);

4,如何进行采集

执行标准流媒体捕获操作的应用程序应执行以下步骤:

(1)IDeckLinkInput::GetDisplayModeIterator

        如果需要,通过该函数枚举支持的采集视频模式。

(2)IDeckLinkInput::DoesSupportVideoMode

        调用该函数检查是否支持视频模式和像素格式的组合。

(3)IDeckLinkInput::EnableVideoInput
(4)IDeckLinkInput::EnableAudioInput
(5)IDeckLinkInput::SetCallback
(6)IDeckLinkInput::StartStreams

(8) 回调VideoInputFrameArrived:  当视频输入帧或音频输入包到达时回调。该方法在基本接口中是抽象,必须由应用程序开发人员来实现。

        1) IDeckLinkVideoInputFrame::GetTimecod

            (非编封装该函数为getTimecodeDataFromFrame)

(7)IDeckLinkInput::StopStreams

        如果需要,音频可以从一个单独的线程中“拉出”。

如果音频是不需要的,调用IDeckLinkInput::EnableAudioInput可以被省略并且IDeckLinkInputCallback::VideoInputFrameArrived回调将接收空音频包。

5,播放

执行标准流媒体播放操作的应用程序应执行以下步骤:

(1)IDeckLinkOutput::DoesSupportVideoMode 检查是否支持视频模式和像素格式的组合。
(2)IDeckLinkOutput::EnableVideoOutput
(3)IDeckLinkOutput::EnableAudioOutput
(4)IDeckLinkOutput::SetScheduledFrameCompletionCallback
(5)IDeckLinkOutput::SetAudioCallback
(6)IDeckLinkOutput::BeginAudioPreroll 

当更多的视频音频帧需要预卷:
调用IDeckLinkOutput::CreateVideoFrame函数创建视频帧
调用IDeckLinkMutableVideoFrame::SetTimecode 或者 IDeckLinkMutableVideoFrame::SetTimecodeFromComponents函数设置视频帧的时码

(7)IDeckLinkOutput::ScheduleVideoFrame
        用于在指定的时间调度一个可进行异步播放的帧。
        从IDeckLinkAudioOutputCallback::RenderAudioSamples返回音频数据
        当音频预卷完成,调用IDeckLinkOutput::EndAudioPreroll
(8)IDeckLinkOutput::StartScheduledPlayback
当在播放的时候:

创建视频帧,并且设置时码。(创建视频帧这个将会在非编的initFrameList函数里面调用,写到list里面,设置时码将会在fillvideo里面调用封装好的函数)

setRP188LTCTimecodeOnFrame(m_deckLinkOutputFrame, (UINT)m_pSrcIO->GetVideoPlayCurPos());setRP188VitcTimecodeOnFrame(m_deckLinkOutputFrame, (UINT)m_pSrcIO->GetVideoPlayCurPos());

安排更多的视频帧:IDeckLinkVideoOutputCallback::ScheduledFrameCompleted(非编中会调用fillvideo函数)
安排更多的音频帧:IDeckLinkAudioOutputCallback::RenderAudioSamples
如果不需要音频, IDeckLinkOutput::EnableAudioOutput, 
IDeckLinkOutput::SetAudioCallback 和 IDeckLinkOutput::BeginAudioPreroll 可被忽略。
如果不需要预卷,初始化IDeckLinkOutput::ScheduleVideoFrame和 IDeckLinkOutput::BeginAudioPreroll 和IDeckLinkOutput::EndAudioPreroll可被忽略。

官方sample中,RenderAudioSample函数的内部实现是这样的:

在音频和视频预卷的帧数都达到设置数值的时候,停止预卷,开始播放

HRESULTDeckLinkOutputDevice::RenderAudioSamples(BOOL preroll){{std::lock_guard<std::mutex> lock(m_playbackMutex);if (m_deckLinkOutput->GetBufferedAudioSampleFrameCount(&m_bufferedAudioSampleCount) != S_OK)return E_FAIL;if (m_bufferedAudioSampleCount > m_bufferedAudioWaterLevel){if (m_prerollingAudio){// Ensure that video preroll is also complete, then commence scheduled playbackif (m_bufferedVideoFrameCount >= m_bufferedVideoWaterLevel){m_deckLinkOutput->EndAudioPreroll();m_prerollingAudio = false;if (m_deckLinkOutput->StartScheduledPlayback(m_scheduledStartTime, m_frameTimescale, 1.0) != S_OK)return E_FAIL;}}return S_OK;}}m_scheduleAudioPacketCondition.notify_one();return S_OK;}

6,获取时码和帧数

获取时码的代码如下:

1,通过GetTimecode函数获取时码

2,拆分出hours,minutes,seconds和frames

3,判断帧率是否为59.94,50和60,那么转换之后需要将帧数乘2。

inline UINTgetTimecodeDataFromFrame(IDeckLinkVideoFrame* videoFrame, BMDTimecodeFormat timecodeFormat, LONG rate, LONG scale){ UINT frameRet = UINT_MAX; IDeckLinkTimecode * pTimecode = NULL; if (videoFrame && videoFrame->GetTimecode(timecodeFormat, &pTimecode) == S_OK) { unsigned char hours = 0; unsigned char minutes = 0; unsigned char seconds = 0; unsigned char frames = 0; HRESULT hr = pTimecode->GetComponents( &hours, &minutes, &seconds, &frames ); if (SUCCEEDED(hr)) { CE7Timecode convert; if (g_pConfig->isDBTimecode()) { convert.setStandard( rate/2, scale ); frameRet = convert.convertHMSFToFrame( hours, minutes, seconds, frames ) * 2; } else { convert.setStandard( rate, scale ); frameRet = convert.convertHMSFToFrame( hours, minutes, seconds, frames ); } } } if (pTimecode) { pTimecode->Release(); pTimecode = NULL; } return frameRet;}

 判断帧率是否为59.94,50和60

int isDBTimecode() const { // 50P TC { const double fps = (double)vsI_rate/100.0; if (fabs(fps-50) <= 0.01) { return true; } else if (fabs(fps-60) <= 0.01) { return true; } else if (fabs(fps-59.94) <= 0.01) { return true; } } return false; }

时码和帧率的转换 

int convertHMSFToFrame(int nH, int nM, int nS, int nF) const { int frameIdx; if (tcRate_ == TC_RATE_UNKNOWN_E7) { frameIdx = nF; } else if (tcRate_ == TC_RATE_NTSC_DF_E7) { // 107892*nH + 17982*(nM/10) + 1798*(nM) + nS*30 + nF // EQUAL frameIdx = nH * 107892 + (nM * 60 + nS) * 30 + nF - ((nM - (nM / 10)) * 2); } else if (tcRate_ == TC_RATE_5997_DF_E7) { frameIdx = nH * 107892 + (nM * 60 + nS) * 30 + nF/2 - ((nM - (nM / 10)) * 2); frameIdx *= 2; if (nF & 1) { frameIdx++; } } else { frameIdx = nH * 60 * 60 * tcRate_ + nM * 60 * tcRate_ + nS * tcRate_ + nF; } return frameIdx; }

相关推荐

相关文章