decklink:采集 DeckLink板卡 2024-04-27 11:12:10 0 0 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和60int 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; } 收藏(0)