关键词:OSDK视频播放,OSDK解码,camera-stream-callback
通过无人机获取视频流及图像,再通过机载计算机拓展算力,对图像或视频进行处理是一个应用场景很广泛的需求,这篇文章记录OSDK的高级视觉功能,即通过官方代码sample了解怎样通过DJI OSDK来获取挂载在DJI无人机上的视频或图像数据。
首先,先给个高级视觉功能官方链接:
https://developer.dji.com/cn/document/4f9bb193-3929-4a71-a6ec-e879f5dbd353
关于官方支持的产品特性,就可以参考这个链接上的说明了。。。
再来看代码文件夹,找到sample中的advance-sensing部分,如下:
/Onboard-SDK-4.1/sample/platform/linux/advanced-sensing
文件夹中sample比较多,包含获取裸流播放,获取裸流保存成H264文件,视觉图像获取等。这篇文章先聊聊最基本的功能获取FPV/主相机视频裸流。
涉及到的三个文件夹:
camera_h264_callback_sample //保存成H264裸流文件
camera_stream_callback_sample //回调方式获取视频流,并解码播放
camera_stream_poll_sample //poll方式获取视频流,并解码播放
这篇文章主要聊聊camera_h264_callback_sample,先了解一下代码结构和原理,具体的sample再对应看细节应该问题也不大。先看这个代码main.c
/*! @brief
*
* Start the FPV or Camera H264 Stream
*
* @platforms M210V2, M300
* @Note For M210 V2 series, only OSDK_CAMERA_POSITION_NO_1 and
* OSDK_CAMERA_POSITION_FPV are supported.
* @note For M300, all the poss are supported.
* @param pos point out which camera to output the H264 stream
* @param cb callback function that is called in a callback thread when a new
* h264 frame is received
* @param cbParam a void pointer that users can manipulate inside the callback
* @Return Errorcode of liveivew, ref to DJI::OSDK:: LiveView:: LiveViewErrCode
*/
LiveView:: LiveViewErrCode startH264Stream(LiveView:: LiveViewCameraPosition pos, H264Callback cb, void *userData);
跟代码可以看到这个函数的实现需要分机型,M300和M210的调用接口不一样,本文就以M300的例子说明一下了,大致查看一下,基本实现思路应该是一样的,方式上有差别,看官方的发布
和更新记录,M300的方案应该比较成熟点,毕竟经过几次迭代。。。。。
函数调用:
dji_liveview.cpp
liveview->startH264Stream(pos, cb, userData);
继续:
dji_liveview_impl.cpp
impl->startH264Stream(pos, cb, userData)
我们看到了传进来的回调和参数使用:
...
if (h264CbHandlerMap.find(pos) != h264CbHandlerMap.end()) {
h264CbHandlerMap[pos].cb = cb;
h264CbHandlerMap[pos].userData = userData;
}
...
有点绕,到这里,貌似还没有找到获取裸流的源头,但是可以看到是将回调附给了h264CbHandlerMap,那就继续追h264CbHandlerMap。
在这个文件:dji_liveview_impl.cpp
std::map<LiveView:: LiveViewCameraPosition, LiveViewImpl::H264CallbackHandler> LiveViewImpl::h264CbHandlerMap = {
{LiveView::OSDK_CAMERA_POSITION_NO_1, {defaultH264CB, (void *)&(defUserData[LiveView::OSDK_CAMERA_POSITION_NO_1])}},
{LiveView::OSDK_CAMERA_POSITION_NO_2, {defaultH264CB, (void *)&(defUserData[LiveView::OSDK_CAMERA_POSITION_NO_2])}},
{LiveView::OSDK_CAMERA_POSITION_NO_3, {defaultH264CB, (void *)&(defUserData[LiveView::OSDK_CAMERA_POSITION_NO_3])}},
{LiveView::OSDK_CAMERA_POSITION_FPV, {defaultH264CB, (void *)&(defUserData[LiveView::OSDK_CAMERA_POSITION_FPV])}},
};
T_RecvCmdItem LiveViewImpl::bulkCmdList[] = {
PROT_CMD_ITEM(0, 0, LIVEVIEW_TEMP_CMD_SET, LIVEVIEW_FPV_CAM_TEMP_CMD_ID, MASK_HOST_DEVICE_SET_ID, (void *)&h264CbHandlerMap, RecordStreamHandler),
PROT_CMD_ITEM(0, 0, LIVEVIEW_TEMP_CMD_SET, LIVEVIEW_MAIN_CAM_TEMP_CMD_ID, MASK_HOST_DEVICE_SET_ID, (void *)&h264CbHandlerMap, RecordStreamHandler),
PROT_CMD_ITEM(0, 0, LIVEVIEW_TEMP_CMD_SET, LIVEVIEW_VICE_CAM_TEMP_CMD_ID, MASK_HOST_DEVICE_SET_ID, (void *)&h264CbHandlerMap, RecordStreamHandler),
PROT_CMD_ITEM(0, 0, LIVEVIEW_TEMP_CMD_SET, LIVEVIEW_TOP_CAM_TEMP_CMD_ID, MASK_HOST_DEVICE_SET_ID, (void *)&h264CbHandlerMap, RecordStreamHandler),
};
阅读到这里,可以看到这里开始将应用端获取视频流关联到linker层了,再继续到底层,我们可以了解的信息有限,个人认为不用继续往下追了,看一下RecordStreamHandler的实现核心部分。
...
std::map<LiveView:: LiveViewCameraPosition, H264CallbackHandler> handlerMap =
*(std::map<LiveView:: LiveViewCameraPosition, H264CallbackHandler> *)userData;
...
if((handlerMap.find(pos) != handlerMap.end()) && (handlerMap[pos].cb != NULL)) {
handlerMap[pos].cb((uint8_t *)cmdData, cmdInfo->dataLen, handlerMap[pos].userData);
} else {
//DERROR("Can't find valid cb in handlerMap, pos = %d", pos);
}
...
h264CbHandlerMap 和 userData就对应起来了,即
handlerMap[pos].cb执行的回调就是我们从main开始一直传过来的回调,所以当函数RecordStreamHandler触发时,我们sample camera_h264_callback_sample就对应执行了保存数据流至文本文件中。
所以到这里,数据流的获取过程大致就厘清了。我个人的理解就是OSDK提供从飞机获取视频流的接口,所有对视频流的处理均可以看做是应用端处理。比如我们传入的回调是写文本,这就是应用。到这里我们再看camera_stream_callback_sample, 也就是传入的回调将文本记录改成了解码和播放。
我们继续看看这个sample:
先找相关代码:
sample代码源文件为:
camera-stream-callback-sample.cpp
camResult = vehicle->advancedSensing->startMainCameraStream(&show_rgb, &mainName);
贴出代码实现:
dji_advanced_sensing.cpp
bool AdvancedSensing::startMainCameraStream(CameraImageCallback cb, void * cbParam)
{
// Use the keep_camera_x5s_state to prevent x5s become a storage device, otherwise could not get the stream
if (vehicle_ptr->isM300()) {
auto deocderPair = streamDecoder.find(LiveView::OSDK_CAMERA_POSITION_NO_1);
if ((deocderPair != streamDecoder.end()) && deocderPair->second) {
deocderPair->second->init();
deocderPair->second->registerCallback(cb, cbParam);
return (LiveView::OSDK_LIVEVIEW_PASS
== startH264Stream(LiveView::OSDK_CAMERA_POSITION_NO_1, H264ToRGBCb,
deocderPair->second));
} else {
return false;
}
} else {
return mainCam_ptr->startCameraStream(cb, cbParam);
}
}
先直观看下参数和接口:
(仅关注M300机型接口)除了我们经过上面提到的startH264Stream,还有引入了一个新的streamDecoder
参数:
H264Callback: H264ToRGBCb
userData: deocderPair->second
我们外部传入的回调:show_rgb,通过streamDecoder:deocderPair->second->registerCallback(cb, cbParam);给了一个线程调,功能就是将解码后的数据进行渲染显示。
H264ToRGBCb这个回调仅做了一个动作:
decoder->decodeBuffer
也就是解码,结合获取H264裸流的介绍,这个就是应用端解码的回调实现。
所以,sample:camera_stream_callback_sample的逻辑也就清晰了,即:通过startH264Stream的回调获取H264裸流数据,获取后直接解码成RGB格式,然后再使用传入的show_rgb对解码的RGB图像进行显示播放。
我们现在跟的代码与取流写文本区别就在于应用端是一个解码部分,源码文件:dji_camera_stream_decoder.cpp
这部分是使用ffmpeg解码,所以在编译OSDK时会依赖ffmeg是因为sample中会使用到,如果不需要这个功能,可以通过修改编译文件和代码将这部分移除也是可以的。希望这部分记录对DJI OSDK视频流部分还不太熟悉的开发者朋友们有点点帮助。
评论
0 条评论
请登录写评论。