以下是rv1108-evb-v12 SDK中的视频拍照的代码流程梳理:
当飞机接收到地面段发送的拍照指令后,回调函数tcp_func_take_photo开始执行。
tcp_func_take_photo主要实现两部分的功能:
首先通过struct photo_param *photo_param = parameter_get_photo_param();获取地面端用户设置的拍照的参数:包括是否连续拍照,以及如果连续牌照,连续拍摄的次数。
1.检查sd卡是否异常----if(access("/mnt/sdcard", W_OK))//判断sd卡是否可写
如果不可写,点用函数hg_api_send_udp_response向地面端发送DRONE_CHECK_SDCARD_EXCEPTION命令,并return -1结束回调。
可写的情况下调用函数hg_api_getSdSizeInfo(&u32SdTotalMBytes, &u32SdFreeMBytes);获取当前sd卡的总的容量的大小,以及剩余的容量大小。如果剩余的容量大小小于100bytes,就通过hg_api_send_udp_response向地面端发送TF_STORAGE_SPACE_NOT_ENOUGH_FAIL的命令,并return -1结束回调。
2.通过上一步的操作,sd检测完毕,接下来就可以执行拍照的流程
通过if (0 == photo_param->photo_mode)查看拍照参数以确定用户拍照是连续拍照还是单次牌照。如果 photo_param->photo_mode为0,就是单次拍照,在拍照函数video_record_takephoto(1);参数为1,如果是多次拍照,就将video_record_takephoto(photo_param->continuous_photo_num);就用户设置的连续拍照的次数作为参数传递。
tcp_func_take_photo
hg_api_getSdSizeInfo(&u32SdTotalMBytes, &u32SdFreeMBytes);//获取当前设备的总的sdcard的大小和剩余的sdcard的大小。如果剩余的sdcard的大小小于100bytes,就会结束回调。并向地面端发送TF_STORAGE_SPACE_NOT_ENOUGH_FAIL的指令
video_record_takephoto(1);//拍照的具体实现。
接下来分析video_record_takephoto函数:
这个函数在app/video/process/video.cpp中实现的extern "C" int video_record_takephoto(unsigned int num),由于实在cpp文件中实现的,在前面加上了extern "C"使用c编译器编译。
这个函数的主要实现功能:
1.通过getfastvideo();获取video的链表头。
2.遍历链表中的所有节点
将本次拍照需要拍摄的次数,做一个设置。
3.如果回调函数rec_event_call不为空,则执行毁掉函数(*rec_event_call)(CMD_PHOTOEND, (void *)0, (void *)1);
回调函数rec_event_call的具体的定义实在同一个文件video.cpp中:static void (*rec_event_call)(int cmd, void *msg0, void *msg1);这个函数指针的赋值是在同一文件video.cpp中:
extern "C" int REC_RegEventCallback(void (*call)(int cmd, void *msg0, void *msg1))
{
rec_event_call = call;
return 0;
}
通过上面对tcp_func_take_photo和video_record_takephoto的分析大概知道了当地面断用户发送拍照指令后,飞机端回调的执行情况,但是对拍照文件的保存 命名和传输还没有设计到。接下来分析上述的REC_RegEventCallback也就是回调函数的注册是在代码的那个地方。
在文件app/video/public_interface.c中有对REC_RegEventCallback(record_event_callback);的调用。注册的回调函数record_event_callback:
void record_event_callback(int cmd, void *msg0, void *msg1)
{
switch (cmd) {
case CMD_UPDATETIME:
api_send_msg(MSG_VIDEO_UPDATETIME, TYPE_BROADCAST, msg0, NULL);
break;
break;
case CMD_PHOTOEND:
api_send_msg(MSG_VIDEO_PHOTO_END, TYPE_BROADCAST, msg0, msg1);
break;
}
}
根据上面回调函数(*rec_event_call)(CMD_PHOTOEND, (void *)0, (void *)1);执行的参数来看本次回调的执行是api_send_msg(MSG_VIDEO_PHOTO_END, TYPE_BROADCAST, msg0, msg1);继续跟进:
void api_send_msg(int id, int type, void *msg0, void *msg1)
{
struct public_message msgdata;
msgdata.id = id;
msgdata.type = type;
dispatch_msg(&msgdata, msg0, msg1);
}
从函数的具体实现可以发现里面主要调用了dispatch_msg(&msgdata, msg0, msg1);函数,这个函数就是轮讯链表,并且执行链表中的节点成员函数指针指向的函数。所以接下来就是要分析,链表中的节点成员函数指针是在哪里赋值注册的。
void dispatch_msg(void *msg, void *prama0, void *param1)
{
struct type_node *p = g_listHead;
if (!p) {
printf("list is NULL \n");
return;
}
do {
if (p->callback)
p->callback(msg, prama0, param1);
p = p->next;
} while (p);
}
上面讨论到了链表中的成员函数指针的执行,下面分析链表中的函数指针的赋值,也就是回调函数的注册。
与函数dispatch_msg在同一个文件msg_list_manager.c中的appendList(struct type_node *l, fun_cb cb)就是对链表中的回调函数的注册,所以接下来找到调用appendList(struct type_node *l, fun_cb cb)函数的地方。
struct type_node *reg_entry(fun_cb cb)
{
return appendList(g_listHead, cb);
}
接着查找调用reg_entry(fun_cb cb)地方:
通过搜索发现有两个地方调用了这个函数,分别是:
1.app/video/public_interface.c 中的函数api_poweron_init(fun_cb cb)的参数,也就是调用这个函数的camera_ui.c中的ui_msg_manager_cb函数。
2.app/video/wifi_setting_interface.c中的reg_msg_manager_cb注册的回调函数是wifi_msg_manager_cb函数。
下面具体分析,当地面端的用户点击拍照后,飞机上具体执行的是上面哪一个回调。