录像回放流程



  • 录像回放流程

    录像搜索


    搜索条件获取

    在客户端中选择通道(最多同时选择4个),点击搜索。搜索的响应函数如下

    void CSearchPane::OnSearch()
    {
        //获取搜索参数,存于queryFactor中
    	StreamSearchMode::TVideoSearchCondition queryFactor;
    	if (0 != GetSearchParam(queryFactor))
    		return;
        //录像搜索需要调用StreamSearchModel
    	IStreamSearchMode* pInstance = GetInlPtr<IStreamSearchMode*>(_T("StreamSearchMode"));
    	if (pInstance == NULL)
    		return;
        //获取回放面板的指针
    	CWnd* videoWnd = NULL;
    	::SendMessage(CReplayUI::replayUI->GetSafeHwnd(), WM_GET_VIDEOWND, WPARAM(&videoWnd), 0);
    	//开始搜索
    	int ret;
    	std::vector<StreamSearchMode::TVideoSearchCondition*> params;
    	params.push_back(&queryFactor);
    	ret = pInstance->StartStreamSearch(LONG(StreamSearchMode::eReplay), params);
    	//错误处理
    }
    

    搜索录像需要结构体TVideoSearchCondition保存搜索条件

    struct TVideoSearchCondition 
    {
    	ERecordPlace                place;  //录像位置
    	ERecordType                 type;   //录像类型
    	SDPTIME                     begin;  //搜索条件起始时间
    	SDPTIME                     end;    //搜索条件结束时间
    	std::string                 source; //CMS源
    	std::vector<std::string> 	choid;	//搜索通道的oid
    	std::vector<std::string>	oidNru; //对nru设备上的录像回放时,记录nru源
    };
    

    搜索条件在GetSearchParam函数中获取

    int CSearchPane::GetSearchParam(StreamSearchMode::TVideoSearchCondition &queryFactor)
    {
        //获取选中的通道
    	ASSERT(m_tree);
    	std::vector<TreeNode*> nodes;
    	m_tree->GetCheckedNode(nodes);
        //录像存放的位置不同,搜索条件也不同。中心录像需要oid和nruOid,而前端录像只需要oid
        //ERecordPlace_centerServer——中心录像	ERecordPlace_equipment——前端录像
    	if (m_recordPlace == ERecordPlace_centerServer)
    	{
    		for(std::vector<TreeNode*>::iterator it = nodes.begin(); it != nodes.end(); it++)
    		{
    			NruMitObject *obj = dynamic_cast<NruMitObject*>(*it);
    			if (obj->info.kind == ENCKIND)
    			{
    				queryFactor.choid.push_back(Type_cast<std::string>(obj->info.oid));
    				queryFactor.oidNru.push_back(Type_cast<std::string>(obj->info.nruOid));
    			}
    		}
    	}
    	else if (m_recordPlace==ERecordPlace_equipment)
    	{
    		for (std::vector<TreeNode*>::iterator it = nodes.begin(); it != nodes.end(); it++)
    		{
    			MitObject *pMitObj = dynamic_cast<MitObject*>(*it);
    			if (pMitObj->info.kind == ENCKIND)
    			{
    				queryFactor.choid.push_back(Type_cast<std::string>(pMitObj->info.oid));
    			}
    		}
    	}
    	//CMS源
    	queryFactor.source = "\\";
    	//搜索时间,为所选日期当天的00:00:00-23:59:59
    	SYSTEMTIME searchDay;
    	COleDateTime refDateTime;
    	m_wndDatePicker.GetCurSel(refDateTime); //获取日历控件上的日期
    	TSDPLocalTime sdpTime;
    	memset(&sdpTime, 0, sizeof(TSDPLocalTime));
    	sdpTime.year=refDateTime.GetYear();
    	sdpTime.month=refDateTime.GetMonth()-1; //TSDPLocalTime这一结构体中月份的取值范围为0-11
    	sdpTime.day=UCHAR(refDateTime.GetDay());
    	sdpTime.hour=sdpTime.min=sdpTime.sec=0;
    	SDPTIME dwTime;
    	sdpMakeLocalTime(dwTime, sdpTime);
    	queryFactor.begin = dwTime; //开始时间
    	sdpTime.hour=23;
    	sdpTime.min=59;
    	sdpTime.sec=59;
    	sdpTime.wday=0;
    	sdpTime.yday=0;
    	sdpTime.ms=0;
    	sdpMakeLocalTime(dwTime, sdpTime);
    	queryFactor.end = dwTime; //结束时间
    	//搜索录像类型
    	int nSelect = m_com_event.GetCurSel();
    	ASSERT(nSelect>=int(ERecordType_AllRecord) && nSelect <= int(ERecordType_AlarmLinked));
    	switch (nSelect)
    	{
    	case 0:
    		queryFactor.type = ERecordType_AllRecord; //全部视频
    		break;
    	case 1:
    		queryFactor.type = ERecordType_TimerRecord; //定时录像
    		break;
    	case 2:
    		queryFactor.type = ERecordType_LinkedRecord;	//联动录像
    		break;
    	case 3:
    		queryFactor.type = ERecordType_MobileLinked; //移动侦测
    		break;
    	case 4:
    		queryFactor.type = ERecordType_AlarmLinked; //报警触发
    		break;
    	default:
    		queryFactor.type = ERecordType_AllRecord; //全部视频
    	}
    	//录像位置,即中心录像或前端录像
    	queryFactor.place = m_recordPlace;
    	return 0;
    }
    


  • 《明涛说代码》系列2333



  • 开始搜索

    调用StartStreamSearch函数,初步检查参数的有效性

    int CStreamSearchMode::StartStreamSearch(LONG id, std::vector<StreamSearchMode::TVideoSearchCondition*> &queryFactor)
    {
        //检查参数是否有效
    	if(queryFactor.size() == 0)
    		return -1;
    	bool bValid = false;
    	for(std::vector<StreamSearchMode::TVideoSearchCondition*>::iterator it = queryFactor.begin(); it!=queryFactor.end(); it++)
    	{
    		StreamSearchMode::TVideoSearchCondition *tmp = *it;
    		if (tmp->begin < tmp->end
    			&& tmp->source.compare("") != 0
    			&& (tmp->type >= ERecordType_AllRecord && tmp->type <= ERecordType_AlarmLinked)
    			&& (tmp->place == ERecordPlace_equipment || tmp->place == RecordPlace_centerServer)
    			&& tmp->choid.size() != 0)
    		{
    			bValid = true;
    			break;
    		}
    	}
    	if (!bValid)
    	{
    		return -1;
    	}
    	//检查该类型的搜索是否正在进行
    	m_lock.Acquire();
    	std::set<LONG>::iterator it = m_set_id.find(id);
    	if (it != m_set_id.end())
    	{
    		m_lock.Release();
    		return -1;
    	}
    	m_set_id.insert(id);
    	m_lock.Release();
    	//保存搜索参数
    	SearchParams params;
    	for (std::vector<StreamSearchMode::TVideoSearchCondition*>::iterator it = queryFactor.begin(); it != queryFactor.end(); it++)
    	{
    		params.m_queryFactor.push_back(**it);
    	}
    	params.id = id;
    	PostEvent(EVT_SEARCH_PARAMS, &params);
    	return 0;
    }
    

    SearchParams保存事件数据

    class SearchParams : public IEventData
    {
    public:
    	LONG id; //执行线程id
    	std::vector<StreamSearchMode::TVideoSearchCondition> m_queryFactor; //搜索条件
    };
    

    事件EVT_SEARCH_PARAMS的响应函数为OnQueryVideo,值得注意的是中心录像和前端录像搜索时所需的参数不同(分别对应ERecordPlace_centerServer和ERecordPlace_equipment)。搜索中心录像时需要同时提供id和channel,id格式为"\nru==%d",channel格式为"\hkdvr=%d\enc=%d"。前者为录像所属nru源的id,可通过channel获取;后者的hkdvr的值为所要搜索录像的摄像头的设备id,而enc的值一般为1。而搜索前端录像时只需要提供channel(group固定为0),这里的channel不再是所要搜索录像的摄像头的设备id,而是在录像存储设备中(cvr、nvr或dvr),存储对应摄像头录像的通道的id。此时channel格式虽然也为"\hkdvr=%d\enc=%d",但hkdvr的值是录像存储设备的设备id,enc的值则标识了设备下对应的通道id。

    void CStreamSearchMode::OnQueryVideo(IEventData* pData)
    {
    	std::set<LONG>::iterator it;
    	SearchParams* params = dynamic_cast<SearchParams*>(pData);
    	if(pData == NULL)
    	{
    		return;
    	}
    	LONG id = params->id;
    	//创建搜索消息结构体
    	StreamSearchMode::StreamSearchData srv_data; //保存搜索条件
    	StreamSearchMode::TVideoSearchResult result;
    	result.id = id;
    	//组件
    	IConnectModel *pInstance = GetInlPtr<IConnectModel*>(_T("ConnectModel"));
    	if(pInstance == NULL)
    	{
    		/*错误处理*/
    	}
    	SDPHANDLE connection =pInstance->GetHandle(); //typedef void* SDPHANDLE;
    	if(connection == NULL)
    	{
    		/*错误处理*/
    	}
    	//AVR Manager Instance
    	for(std::vector<StreamSearchMode::TVideoSearchCondition>::iterator it_condition = params->m_queryFactor.begin(); it_condition != params->m_queryFactor.end(); it_condition++)
    	{
    		if(it_condition->place == ERecordPlace_equipment)
    		{
                //设备端录像文件查询
    			MitObjInfo info;
    			IMitModel* mitModel = GetInlPtr<IMitModel*>(_T("MitModel"));
    			if (mitModel == NULL)
    			{
    				/*错误处理*/
    			}
    			bool bSuc = false; //是否成功
    			bool bFail = false; //是否失败
    			StreamSearchMode::TVideoSearchResult result;
    			result.id = params->id; //设备id
    			result.condition = *it_condition; //搜索条件
    			//搜索录像文件   
    			std::vector<std::string>::iterator itor = it_condition->choid.begin();
    			for (; itor != it_condition->choid.end(); itor++)
    			{
    				/*检查退出标志位*/
    				SDPSEQUENCE<TRecordFile> files; //SDPSEQUENCE为模板类
    				files.construct(tid_TRecordFile); //tid_TRecordFile为事件ID
    				//判断父结点类型
    				CString strParent;
    				mitModel->GetMitParent(Type_cast<CString>(*itor), strParent);
    				mitModel->GetMitObjInfo(strParent, info);
    				//搜索条件填入json
    				Json::Value param,resp;
    				param["channel"] = (*itor).c_str();
    				param["group"] = 0;
    				param["type"] = it_condition->type;
    				TSDPLocalTime localTime;
    				CString str;
    				sdpGetLocalTime(it_condition->begin, localTime);
    				str.Format(_T("%04d-%02d-%02d %02d:%02d:%02d"), localTime.year, localTime.month+1, localTime.day, localTime.hour, localTime.min, localTime.sec);
    				param["begin"] = std::string(str).c_str();
    				sdpGetLocalTime(it_condition->end, localTime);
    				str.Format(_T("%04d-%02d-%02d %02d:%02d:%02d"), localTime.year, localTime.month+1, localTime.day, localTime.hour, localTime.min, localTime.sec);
    				param["end"] = std::string(str).c_str();
    				LONG nRet = instanceRMIManager()->rest_get(connection,"/Channels/queryRecordDevice",param,resp); //调用接口
    				if (nRet != errNone)
    				{
    					/*错误处理*/
    				}
    				bSuc = true;
    				std::string source; //源oid
    				IMitModel *pModel = GetInlPtr<IMitModel*>(_T("MitModel"));
    				if (pModel)
    				{
    					CString poid;
    					pModel->GetMitParent(Type_cast<CString>((*itor)), poid);
    					source = Type_cast<std::string>(poid);
    				}
    				StreamSearchMode::TChannelVideoRecord vr;
    				vr.source = *itor;
                    //处理搜索结果
    				int size = resp.size();
    				for (ULONG j = 0; j < size; j++)
    				{
    					if (resp[j]["begin"].asDouble() >= resp[j]["end"].asDouble())
    					{
    						continue;
    					}
    					StreamSearchMode::TRecordFileEx record;
    					record.begin = resp[j]["begin"].asDouble();
    					record.end = resp[j]["end"].asDouble();
    					record.size = resp[j]["size"].asDouble();
    					record.source = source;
    					std::string tmp(resp[j]["name"].asCString());
    					record.name = tmp;
    					int size_1 = resp[j]["flags"].size();
    					for (ULONG m = 0; m < size_1; m++)
    					{
    						StreamSearchMode::TRecordFlags flags;
    						flags.time = resp[j]["flags"][m]["time"].asDouble();
    						flags.flags = resp[j]["flags"][m]["data"].asUInt();
    						if(flags.flags && (info.kind == HK_DVRKIND || info.kind == DH_DVRKIND))
    						{
    							flags.begTime = resp[j]["flags"][m]["beginTime"].asDouble();
    							flags.endTime = resp[j]["flags"][m]["endTime"].asDouble();
    							flags.fileName = resp[j]["flags"][m]["filename"].asCString();
    							flags.fileSize = resp[j]["flags"][m]["filesize"].asUInt();
    						}
    			//辨别预录和延时录像两种类型,预录类型替换成下一片段的类型,如果没有下一片段,替换成定时录像类型
    			//延时录像类型替换成上一片段的类型,如果没有上一片段,替换成定时录像类型
    						if (flags.flags & 1)
    						{
    							if (m+1 < size_1)
    								flags.flags |= resp[j]["flags"][m+1]["data"].asUInt();
    							else
    							{
    								flags.flags |= 4; //延时录像类型,替换成定时录像
    							}
    						}
    						else if (flags.flags & 2)
    						{
    							if (m != 0)
    								flags.flags |= resp[j]["flags"][m-1]["data"].asUInt();
    							else
    							{
    								flags.flags |= 4; //延时录像类型,替换成定时录像
    							}
    						}
    						record.flags.push_back(flags);
    					}
    					vr.files.push_back(record);
    				}
    				result.records.push_back(vr);
    			}
    			if ( bSuc && !bFail)
    				result.resultFlag = StreamSearchMode::VideoSearchSucceed;
    			else if (bSuc)
    				result.resultFlag = StreamSearchMode::VideoSubSucceed;
    			else
    				result.resultFlag = StreamSearchMode::VideoSearchFailed;
    			srv_data.result.push_back(result);
    		}
    		else if (it_condition->place == ERecordPlace_centerServer)
    		{
                //NRU录像文件查询
    			bool bSuc = false;
    			bool bFail = false;
    			StreamSearchMode::TVideoSearchResult result;
    			result.id = params->id; //设备id
    			result.condition = *it_condition; //搜索条件
    			//搜索录像文件   
    			std::vector<std::string>::iterator itor = it_condition->choid.begin();
    			std::vector<std::string>::iterator it_nru = it_condition->oidNru.begin();
    			for (; itor != it_condition->choid.end() && it_nru != it_condition->oidNru.end(); itor++, it_nru++)
    			{
    				/*检查退出标志位*/
                    //搜索条件填入json
    				Json::Value param,resp;
    				param["id"] = (*it_nru).c_str();
    				param["channel"] = (*itor).c_str();
    				param["type"] = it_condition->type;
    				TSDPLocalTime localTime;
    				CString str;
    				sdpGetLocalTime(it_condition->begin, localTime);
    				str.Format(_T("%04d-%02d-%02d %02d:%02d:%02d"), localTime.year, localTime.month+1, localTime.day, localTime.hour, localTime.min, localTime.sec);
    				param["begin"] = std::string(str).c_str();
    				sdpGetLocalTime(it_condition->end, localTime);
    				str.Format(_T("%04d-%02d-%02d %02d:%02d:%02d"), localTime.year, localTime.month+1, localTime.day, localTime.hour, localTime.min, localTime.sec);
    				param["end"] = std::string(str).c_str();
    				LONG nRet = instanceRMIManager()->rest_get(connection,"Channels/queryRecordServer", param, resp);
    				if (nRet != errNone)
    				{
    					/*错误处理*/
    				}
    				bSuc=true;
    				StreamSearchMode::TChannelVideoRecord vr;
    				vr.source = *itor;
                    //处理搜索结果
    				int size = resp.size();
    				for(ULONG j = 0; j < size; j++)
    				{
    					if(resp[j]["begin"].asDouble() >= resp[j]["end"].asDouble())
    					{
    						continue;
    					}
    					StreamSearchMode::TRecordFileEx record;
    					record.begin = resp[j]["begin"].asDouble();
    					record.end = resp[j]["end"].asDouble();
    					record.size = resp[j]["size"].asDouble();
    					record.source = *it_nru;
    					std::string tmp(resp[j]["name"].asCString());
    					record.name = tmp;
    					int size_1 = resp[j]["flags"].size();
    					for(ULONG m = 0; m < size_1; m++)
    					{
    						StreamSearchMode::TRecordFlags flags;
    						flags.time = resp[j]["flags"][m]["time"].asDouble();
    						flags.flags = resp[j]["flags"][m]["data"].asUInt();
    			//辨别预录和延时录像两种类型,预录类型替换成下一片段的类型,如果没有下一片段,替换成定时录像类型
    			//延时录像类型替换成上一片段的类型,如果没有上一片段,替换成定时录像类型
    						if(flags.flags & 1)
    						{
    							if(m + 1 < size_1)
    								flags.flags |= resp[j]["flags"][m+1]["data"].asUInt();
    							else
    							{
    								flags.flags |= 4; //延时录像类型,替换成定时录像
    							}
    						}
    						else if (flags.flags & 2)
    						{
    							if (m != 0)
    								flags.flags |= resp[j]["flags"][m-1]["data"].asUInt();
    							else
    							{
    								flags.flags |= 4; //延时录像类型,替换成定时录像
    							}
    						}
    						record.flags.push_back(flags);
    					}
    					vr.files.push_back(record);
    				}
    				result.records.push_back(vr);
    			}
    			if (bSuc && !bFail)
    				result.resultFlag = StreamSearchMode::VideoSearchSucceed;
    			else if(bSuc)
    				result.resultFlag = StreamSearchMode::VideoSubSucceed;
    			else
    				result.resultFlag = StreamSearchMode::VideoSearchFailed;
    			srv_data.result.push_back(result);
    		}
    		else
    		{
    			//录像查询,录像位置异常
    		}
    	}
    	/*检查退出标志位*/
    	//调用接口搜索录像完毕,发送通知
    	Notify(EVT_STREAMSEARCHMODE_RESULT,&srv_data);
    	/*清理相关标志*/
    }
    

    TVideoSearchResult为保存搜索结果的结构体

    struct TVideoSearchResult 
    {
    	LONG							  id;		  //搜索启动成功返回的标志ID
    	TVideoSearchCondition             condition;  //搜索条件
    	EVideoSearchResult                resultFlag; //搜索结果类型
    	std::vector<TChannelVideoRecord>  records;    //搜索结果
    };
    

    TChannelVideoRecord为保存通道视频记录的结构体

    struct TChannelVideoRecord
    {
    	std::string                 source; //通道源
    	std::vector<TRecordFileEx>  files;  //搜索结果文件集
    };
    

    TRecordFileEx为记录视频文件信息的结构体

    struct TRecordFileEx
    {
    	std::string     source; //源
    	std::string     name;   //文件名
    	ULONG           size;   //文件大小
    	SDPTIME         begin;  //起始时间
    	SDPTIME         end;    //结束时间
    	std::vector<TRecordFlags> flags; //录像段类型标记
    };
    

    TRecordFlags为记录视频录像段信息的结构体

    struct TRecordFlags
    {	
    	SDPTIME time;    		//时间
    	ULONG flags;     		//录像类型标记(ERecordTypeFlags枚举的或值,为0表示没有录像)
    	std::string fileName;	//文件名
    	ULONG fileSize;			//文件大小
    	SDPTIME begTime;		//起始时间
    	SDPTIME endTime;		//结束时间
    };
    

    MitObjInfo为记录设备节点信息的结构体

    struct MitObjInfo
    {
    	CString oid;			//对象ID
    	CString name;			//名称(区域,设备,通道对象名称)
    	CString fullname;		//全称
    	long kind;				//种类
    	long type;				//种类
    };
    



  • 这波确实详细嗷



  • 处理搜索结果

    事件EVT_STREAMSEARCHMODE_RESULT有好几处,分别为Alarm和Map项目中的ReplayDialog,CUModel中的VideoReplayModel,VideoDownloadModel和CUBase中的SearchResultWnd。而和录像回放中的录像搜索有关的是CUModel中的VideoReplayModel和CUBase中的SearchResultWnd

    CUModel/VideoReplayModel

    LRESULT CVideoReplayModel::OnRefreshSearchResult(IEventData *pData)
    {
    	IMitModel* pMitModel = GetInlPtr<IMitModel*>(_T("MitModel"));
    	if(NULL == pMitModel)
    	{
    		return S_FALSE;
    	}
    	StreamSearchMode::StreamSearchData *data = dynamic_cast<StreamSearchMode::StreamSearchData*>(pData);
    	if(data && data->result.size() != 0)
    	{
    		StreamSearchMode::TVideoSearchResult *result = &(*(data->result.begin()));
    		if(result == NULL || (result->id != StreamSearchMode::eReplay && result->id != StreamSearchMode::eEMap && result->id != StreamSearchMode::eAlarm))
            {   //result->id用于确定搜索的请求来源
    			return S_FALSE;
    		}	
    		//判断搜索成功与否
    		if(result->resultFlag == StreamSearchMode::VideoSubSucceed 
    			|| result->resultFlag == StreamSearchMode::VideoSearchSucceed)
    		{
    			CloseVideoReplayAll(); //关闭当前所有回放
    			m_sdpBaseTime = result->condition.begin;
    			//整理搜索结果
                //清空已有搜索结果
    			for(std::list<ReplayItemData>::iterator it_del = m_ItemBuf.begin(); it_del != m_ItemBuf.end(); it_del++)
    			{
    				for(std::vector<TRecordTimeSlice>::iterator it_tmp = it_del->slices.begin();
    					it_tmp != it_del->slices.end(); it_tmp++)
    				{
    					it_tmp->flags.clear();
    				}
    				it_del->slices.clear();
    			}
    			m_ItemBuf.clear();
    			for(std::vector<VideoResult>::iterator it_del1 = m_results.begin(); it_del1 != m_results.end(); it_del1++)
    			{
    				for(std::list<TRecordTimeSlice>::iterator it_file = it_del1->slices.begin();
    					it_file != it_del1->slices.end(); it_file++)
    				{
    					it_file->flags.clear();
    				}
    				it_del1->slices.clear();
    			}
    			m_results.clear();
    			//通道插入将没有录像文件的通道放在后面
    			//可以将没有文件的通道不显示出来且不参与分屏,目前显示
    			int i = 1;
    			for(std::vector<StreamSearchMode::TChannelVideoRecord>::iterator it = result->records.begin(); it != result->records.end(); it++)
    			{
    				if(it->files.size() == 0)
    				{
    					continue;
    				}
    				ReplayItemData item;
    				item.oid = Type_cast<CString>((*it).source);
    				item.nSpeed = 0;
    				item.bEdit = false;
    				item.nBright = 8;
    				item.nContrast = 8;
    				item.cur_time = 0;
    				//保存搜索结果
    				VideoResult oneCh;
    				oneCh.source = (*it).source;
    				oneCh.slices.clear();				
    				item.nIndexItem = i;
    				i++;
    				std::list<StreamSearchMode::TRecordFileEx> vTempFiles;
    				for(std::vector<StreamSearchMode::TRecordFileEx>::iterator iter = it->files.begin(); iter != it->files.end(); iter++)
    				{	//排序插入
    					TSDPLocalTime testBeg;
    					TSDPLocalTime testEnd;
    					sdpGetLocalTime(iter->begin, testBeg);
    					sdpGetLocalTime(iter->end, testEnd);
    					if(iter->begin == iter->end) //录像文件长度为0
    					{
    						continue;
    					}
    					if (vTempFiles.size()==0)
    					{	
                            //第一个插入
    						vTempFiles.push_back(*iter);
    						continue;
    					}
    					std::list<StreamSearchMode::TRecordFileEx>::iterator it_tmp = vTempFiles.begin();
    					for(; it_tmp != vTempFiles.end(); it_tmp++)
    					{
    						if (it_tmp->begin > iter->begin)
    						{
    							vTempFiles.insert(it_tmp, *iter);
    							break;
    						}
    					}
    					if (it_tmp == vTempFiles.end())
    					{
    						vTempFiles.push_back(*iter);
    					}
    				}
    				//合并文件时间片
    				std::list<StreamSearchMode::TRecordFileEx>::iterator it_cur = vTempFiles.begin();
    				std::list<StreamSearchMode::TRecordFileEx>::iterator it_next;
    				while(it_cur != vTempFiles.end())
    				{
    					TRecordTimeSlice slice;
    					slice.begin = it_cur->begin;
    					slice.end = it_cur->end;
    					slice.flags = it_cur->flags;
    					it_next = it_cur;
    					while (1)
    					{
    						it_next++;	
    						if(it_next == vTempFiles.end())
    						{
    							break;
    						}
    						if(it_next->begin > slice.end)
    						{
    							break;
    						}
    						else
    						{
    							for(std::vector<StreamSearchMode::TRecordFlags>::iterator it_flags=it_next->flags.begin(); it_flags!=it_next->flags.end(); ++it_flags)
    							{
    								slice.flags.push_back(*it_flags);
    							}
    							slice.end = it_next->end;
    						}
    					}
    					oneCh.slices.push_back(slice);
    					item.slices.push_back(slice);
    					it_cur = it_next;
    				}
    				//修正时间片的最后一个片段的结束时间
    				ULONG slicesLength = item.slices.size();
    				if(slicesLength > 0)
    				{
    					TRecordTimeSlice& tmp_slice = item.slices.at(slicesLength-1);
    					tmp_slice.end = tmp_slice.end > result->condition.end ? result->condition.end : tmp_slice.end;
    				}
    				if(oneCh.slices.size() > 0)
    				{
    					std::list<TRecordTimeSlice>::iterator it_oneCh = oneCh.slices.end();
    					it_oneCh--;
    					it_oneCh->end = it_oneCh->end > result->condition.end ? result->condition.end : it_oneCh->end;
    				}
    				it_cur = vTempFiles.begin();
    				SDPTIME temp_begin = it_cur->begin;
    				it_cur = vTempFiles.end();
    				it_cur--;
    				SDPTIME temp_end = it_cur->end > result->condition.end?result->condition.end:it_cur->end;
    				CHAR szPid[MITOIDLEN] = {0};
    				TMitObjIdKVPair kv;
    				mitGetPid(item.oid, MITOIDLEN, szPid, &kv);
    				IMitModel* pMitModel = GetInlPtr<IMitModel*>(_T("MitModel"));
    				if (NULL == pMitModel)
    				{
    					return S_FALSE;
    				}
    				EncoderInfo info;
    				if(!pMitModel->GetDevInfo(szPid,info))
    				{
    					return S_FALSE;
    				}
    				if (result->condition.place == ERecordPlace_centerServer)
    				{
    					item.fileRes.Format(_T("%s\\\\replay=%s\\%d\\%I64d\\%I64d"), it_cur->source.c_str(), info.sn, kv.rid, temp_begin, temp_end);
    				}
    				else
    				{
    					item.fileRes.Format(_T("%s%s"), it_cur->source.c_str(), it_cur->name.c_str());
    				}
    				item.bExistedFile = true;
    				item.oidSrc = Type_cast<CString>(it_cur->source);
    				oneCh.srcParent = it_cur->source;
    				item.tm_begin = item.slices.at(0).begin;
    				item.tm_end = item.slices.at(0).end;
    				if (item.tm_begin < m_sdpBaseTime)
    				{
    					item.jumpPos = (m_sdpBaseTime - item.tm_begin)/1000; // 从零点开始播放
    				}
    				item.streamId = -1;
    				item.status = eReplayStatusStop;
    				item.sliceIndex = 0;
    				item.bPlaying = false;
    				std::list<TRecordTimeSlice>::iterator tmp = oneCh.slices.begin();
    				while (tmp != oneCh.slices.end())
    				{	//搜索录像文件结果展示,升序排列
    					TSDPLocalTime begin,end;
    					sdpGetLocalTime((*tmp).begin, begin);
    					sdpGetLocalTime((*tmp).end, end);
    					CString str;
    					str.Format(_T("%2d-%02d-%02d %02d:%02d:%02d  --> %2d-%02d-%02d %02d:%02d:%02d	"),
    					begin.year, begin.month, begin.day, begin.hour, begin.min, begin.sec, 
    					end.year, end.month, end.day, end.hour, end.min, end.sec);
    					tmp++;
    				}
    				m_ItemBuf.push_back(item);
    				oneCh.nIndex=0;	// 有可能本通道并没有文件,但此索引不影响使用
    				m_results.push_back(oneCh);
    			}
    			//插入空白通道
    			for (std::vector<StreamSearchMode::TChannelVideoRecord>::iterator it = result->records.begin(); it!=result->records.end(); it++)
    			{
    				if (it->files.size()!=0)
    					continue;
    				ReplayItemData item;
    				item.oid = Type_cast<CString>((*it).source);
    				item.bExistedFile = false;
    				item.nIndexItem = i;
    				item.bEdit=false;
    				i++;
    				item.status = eReplayStatusStop; //重新设置标识
    				item.streamId = -1;
    				item.bSound = FALSE;
    				item.tm_begin=0;
    				item.tm_end = 0;
    				item.bValid = false;
    				item.bPlaying = false;
    				m_ItemBuf.push_back(item); //没有录像文件的通道尾部插入
    				//保存搜索结果
    				VideoResult oneCh;
    				oneCh.source = (*it).source;
    				oneCh.slices.clear();
    				oneCh.nIndex = 0;	//有可能本通道并没有文件,但此索引不影响使用
    				m_results.push_back(oneCh);
    			}
    			InitScreenSpace(m_ItemBuf.size()); //初始化分屏
    		}
    	}
    	Notify(EVT_QUERY_FINISH, pData);
    	return S_OK;
    }
    

    ReplayItemData为保存缓存回放数据的结构体

    struct ReplayItemData
    {
    	bool bValid;		//用于移除和添加指定通道
    	bool bExistedFile;	//该通道是否存在录像文件
    	CString oid;		//通道oid
    	CString oidSrc;		//通道oid的上一级,主要是针对nru回放,如果是非nru,则为通道所属的设备
    	CString fileRes; 	//用于启动回放的文件标识符
    	SDPTIME tm_begin;	//文件开始时间,以ms计算
    	SDPTIME tm_end;		//文件结束时间,以ms计算
    	SDPTIME cur_time;	//异步回放需要的参数,
    	int sliceIndex;	
    	std::vector<TRecordTimeSlice> slices; 
    	bool bEdit;			//是否剪辑该通道
    	bool bSound;		//该窗口视频是否开启音频
    	int	nBright;		//亮度
    	int	nContrast;		//对比度
    	SDPTIME jumpPos;
    	int nSpeed;			//取值范围[-4,+4]
    	UINT nIndexItem;	//回放窗口索引位
    	LONG streamId;		//流id
    	EReplayStreamStatus status;
    	bool bLocalZoom;	//是否处在本地放大状态
    	bool bPlaying;
    };
    

    TRecordTimeSlice为记录录像时间段的结构体

    struct TRecordTimeSlice
    {
    	SDPTIME	begin;		  //起始时间
    	SDPTIME	end;		  //结束时间
    	std::vector<StreamSearchMode::TRecordFlags> flags; //录像段类型标记
    };
    

    TRecordFlags为记录视频文件录像段类型标记的结构体

    struct TRecordFlags
    {	
    	SDPTIME time;   	 //时间
    	ULONG flags;     	 //录像类型标记(ERecordTypeFlags枚举的或值,为0表示没有录像)
    	std::string fileName;
    	ULONG fileSize;
    	SDPTIME begTime;
    	SDPTIME endTime;
    };
    

    VideoResult为保存通道视频记录的结构体

    struct VideoResult
    {
    	std::string                 source; 	//通道源
    	std::string					srcParent;	//上一级oid,nru则为纳入源,设备为设备oid
    	int                         nIndex;		//当前播放文件索引,从0开始
    	std::list<TRecordTimeSlice>	slices;  	//搜索结果时间片列表
    };
    

    事件EVT_QUERY_FINISH的响应函数位于项目Replay中的SearchPane

    void CSearchPane::OnQueryFinished(IEventData *data)
    {
    	StreamSearchMode::StreamSearchData *evtData = dynamic_cast<StreamSearchMode::StreamSearchData*>(data);
    	if (evtData && evtData->result.size() != 0)
    	{
    		StreamSearchMode::TVideoSearchResult *result = &(*(evtData->result.begin()));
    		if(result == NULL || result->id != (LONG)StreamSearchMode::eReplay)
    		{
    			return;
    		}
    		::SendMessage(m_tipsDlg->m_hWnd, QUERYFINISHED, 0,0); //关闭提示框
    		bool bNull = true;
    		CWnd * videoWnd = NULL;
    		::SendMessage(CReplayUI::replayUI->GetSafeHwnd(), WM_GET_VIDEOWND, WPARAM(&videoWnd), 0); //获取回放面板的句柄
    		CString str, strTips;
    		GetModuleCString(strTips, IDS_TIPS);
    		if (result->resultFlag==StreamSearchMode::VideoSearchSucceed
    			|| result->resultFlag==StreamSearchMode::VideoSubSucceed)
    		{
    			for (std::vector<StreamSearchMode::TChannelVideoRecord>::iterator it = result->records.begin(); it != result->records.end(); it++)
    			{
    				if ((*it).files.size()!=0)
    				{
    					bNull=false;
    					break;
    				}
    			}
    			if (bNull)
    			{
    				GetModuleCString(str, IDS_SEARCH_NO_VIDEO);
    				CenterMessageBox(videoWnd, str, strTips, MB_OK|MB_ICONINFORMATION);
    			}
    		}
    		else
    		{
    			GetModuleCString(str, IDS_SEARCH_FAILED);
    			CenterMessageBox(videoWnd, str, strTips, MB_OK|MB_ICONERROR);
    		}
    	}
    }
    

    CUBase/SearchResultWnd

    LRESULT CSearchResultWnd::OnStreamSearchResult(IEventData *pData)
    {
    	StreamSearchMode::StreamSearchData *evtData = dynamic_cast<StreamSearchMode::StreamSearchData*>(pData);
    	if(evtData &&  evtData->result.size() != 0)
    	{
    		StreamSearchMode::TVideoSearchResult *result = &(*(evtData->result.begin()));
    		if(result == NULL || result->id != (LONG)m_id)
    		{
    			return S_FALSE;
    		}
    		bool bSuc = false;	// true包含半成功
    		for(std::vector<StreamSearchMode::TVideoSearchResult>::iterator it=evtData->result.begin(); it != evtData->result.end(); it++)
    		{
    			if(it->resultFlag == StreamSearchMode::VideoSearchSucceed
    				|| it->resultFlag == StreamSearchMode::VideoSubSucceed)
    			{
    				bSuc = true;
    				break;
    			}
    		}
    		if (bSuc)
    		{	//全部通道搜索成功,或者部分通道搜索成功
    			//刷新结果显示区域
    			m_table_result.RefreshVideoSearchResult(evtData->result);
    			// 将筛选标志置为所有
    			m_pict_status = 0xFFFFFFFF;
    		}
    	}
    	return S_OK;
    }
    

    调用RefreshVideoSearchResult转换搜索结果

    void CVideoSearchResultTable::RefreshVideoSearchResult(std::vector<StreamSearchMode::TVideoSearchResult> &params)
    {
    	if(params.size() == 0)
    		return;
    	int i;
    	for(i = 0;i < MAX_NUM_REPLAY_SUP; i++)
    	{
    		m_SearchResult[i].bValid = false;
    		m_SearchResult[i].files.clear();
    		m_SearchResult[i].ch_oid = "";
    		m_SearchResult[i].nru_oid = "";
    	}
    	//刷新结果时初始化显示全天
    	m_dLeftTime = 0;
    	m_nDegreeResize = 0;
    	//当天的起始时间SDPTIME值
    	TSDPLocalTime tmpTime;
    	sdpGetLocalTime(params.begin()->condition.begin,tmpTime); //搜索条件里面的开始时间不一定是一天的凌晨
    	tmpTime.hour = 0;
    	tmpTime.min = 0;
    	tmpTime.sec = 0;
    	tmpTime.ms = 0;
    	sdpMakeLocalTime(m_sdptime_base, tmpTime);
    	TSDPLocalTime time;
    	sdpGetLocalTime(m_sdptime_base, time);
    	INT searchDay = time.day;
    	i = 0;
    	m_nValidChannel=0;
    	std::vector<StreamSearchMode::TVideoSearchResult>::iterator it_result = params.begin();
    	for(; it_result != params.end(); it_result++)
    	{
    		std::vector<StreamSearchMode::TChannelVideoRecord>::iterator it = it_result->records.begin();
    		for(;it != it_result->records.end(); it++)
    		{// 先填有录像的通道
    			if ((*it).files.size()==0)
    				continue;
    			m_nValidChannel++;
    			m_SearchResult[i].bValid=true;
    			m_SearchResult[i].nSearchDay=searchDay;
    			m_SearchResult[i].ch_oid=(*it).source;
    			if (it_result->condition.oidNru.size() != 0)
    			{	//nru录像
    				std::vector<std::string>::iterator it_nruoid = it_result->condition.oidNru.begin();
    				for (std::vector<std::string>::iterator it_oid = it_result->condition.choid.begin();
    					it_oid != it_result->condition.choid.end(); it_oid++,it_nruoid++)
    				{
    					if (m_SearchResult[i].ch_oid.compare(*it_oid) == 0)
    					{
    						m_SearchResult[i].nru_oid = *it_nruoid;
    						break;
    					}
    				}
    			}
    			for (std::vector<StreamSearchMode::TRecordFileEx>::iterator it_Rec = (*it).files.begin(); it_Rec != (*it).files.end(); it_Rec++)
    			{
    				m_SearchResult[i].files.push_back(*it_Rec);
    			}
    			i++;
    		}
    	}
    	it_result = params.begin();
    	for (; it_result != params.end(); it_result++)
    	{
    		std::vector<StreamSearchMode::TChannelVideoRecord>::iterator it = it_result->records.begin();
    		for (;it!=it_result->records.end(); it++)
    		{	//将没有录像的通道殿后
    			if ((*it).files.size()!=0)
    				continue;
    			m_nValidChannel++;
    			m_SearchResult[i].bValid=true;
    			m_SearchResult[i].nSearchDay=searchDay;
    			m_SearchResult[i].ch_oid=(*it).source;
    			if (it_result->condition.oidNru.size() != 0)
    			{	//nru录像
    				std::vector<std::string>::iterator it_nruoid = it_result->condition.oidNru.begin();
    				for (std::vector<std::string>::iterator it_oid = it_result->condition.choid.begin();
    					it_oid != it_result->condition.choid.end(); it_oid++,it_nruoid++)
    				{
    					if (m_SearchResult[i].ch_oid.compare(*it_oid)==0)
    					{
    						m_SearchResult[i].nru_oid = *it_nruoid;
    						break;
    					}
    				}
    			}
    			i++;
    		}
    	}
    	m_nBaseChannelIndex = 0; //优先使用前4个窗口
    	if (m_replayModel == eAsyncReplay)
    	{	//异步模式,切换当前显示的通道时,重新输入数据,中间最多存在1秒的空白,SetCurTime刷新当前时间
    		for (int i = 0; i < 4; i++)
    		{
    			m_timeCursor[i].strOid = Type_cast<CString>(m_SearchResult[i+m_nBaseChannelIndex].ch_oid);
    			m_timeCursor[i].bVisible=false;
    			m_timeCursor[i].curTime = 0;
    			m_timeCursor[i].posPre = 0;
    			m_timeCursor[i].posCur=0;
    		}
    	}
    	m_status_filter = 0XFFFFFFFF;
    	CalcDisplayRect();
    	CRect rect;
    	GetClientRect(&rect);
    	InvalidateRect(&rect);
    	UpdateWindow();
    	m_btn_UpPage.EnableWindow(FALSE); // 初始显示前4路
    	BOOL bStatus = m_nValidChannel <= 4 ? FALSE : TRUE;
    	::SendMessage(GetParent()->GetSafeHwnd(), RT_DOWNBTN_STATUS, WPARAM(bStatus),0);
    }
    


  • 录像播放

    选中需要播放录像的通道后,单击播放按钮,响应函数如下

    void CReplayPaneDlg::OnPlay()
    {
    	IVideoReplayModel* pModel = GetInlPtr<IVideoReplayModel*>(_T("VideoReplayModel"));
    	if(pModel == NULL)
    	{
    		return;
    	}
    	if(m_bPlaying)
    	{	//解除暂停
    		if (pModel->IsSycReplay())
    		{
    			pModel->ResumeReplayAll();
    		}
    		else
    		{
    			pModel->ResumeReplayChannel();
    		}
    		m_bPause = false;
    	}
    	else
    	{	//播放
    		int ret;
    		if (pModel->IsSycReplay())
    		{
    			ret = pModel->OpenVideoReplayAll();
    		}
    		else
    		{
    			ret = pModel->OpenVideoReplayChannel();
    		}
    		if (ret == 1)
    		{
    			/*播放前请先勾选通道并进行录像文件的搜索*/
    		}
    		else if (ret==2)
    		{	
                /*没有录像文件*/
    		}
    		m_bPlaying = true;
    		m_bLinked = false;
    		m_slider_speed.SetPos(4);
    	}
    	RefreshBtnStatus(); //更新播放控制面板上的按钮状态
    }
    

    主要调用的是VideoReplayModel里的几个函数,先看OpenVideoReplayChannel

    LONG CVideoReplayModel::OpenVideoReplayChannel()
    {
    	if(!m_bAsyncReplayFlag) //异步回放
    	{
    		return errError;
    	}
    	if(m_selectedItemIndex < 1 || m_selectedItemIndex > 4)
    	{
    		return errBadArg;
    	}
    	ReplayCmdData cmdData;
    	cmdData.cmd = eReplayStart;
    	m_modelLock.Acquire();
    	//判断是否搜索过
    	if(m_ItemBuf.size() == 0)
    	{
    		m_modelLock.Release();
    		return errNotExist;
    	}
    	for(std::list<ReplayItemData>::iterator it = m_ItemBuf.begin(); it != m_ItemBuf.end(); it++)
    	{
    		if(it->bExistedFile && m_selectedItemIndex == it->nIndexItem && !it->bPlaying)
    		{
    			cmdData.listItem.push_back(*it);
    			it->status = eReplayStatusLinking;
    			it->bPlaying = true;
    			it->bValid = true;
    			break;
    		}
    	}
    	m_modelLock.Release();
    	if(cmdData.listItem.size()==0)
    	{
    		return errNotExist;
    	}
    	Notify(EVT_REPLAY_CMD, &cmdData);
    	return errNone;
    }
    

    事件EVT_REPLAY_CMD的响应函数位于Replay中的PlaybackManager,这里对播放命令汇总再转发出去,这里先看eReplayStart

    LRESULT CPlaybackManager::OnReplayCmd(IEventData *pData)
    {
    	ReplayCmdData *cmdData = dynamic_cast<ReplayCmdData*>(pData);
    	if (cmdData)
    	{
    		switch (cmdData->cmd)
    		{
    		case eReplayStart:
    			StartReplay(cmdData->listItem);
    			break;
    		case eReplayStop:
    			StopReplay(cmdData->listItem);
    			break;
    		case eReplayJump:
    			JumpReplay(cmdData->listItem);
    			break;
    		case eReplayPause:
    			PauseReplay(cmdData->listItem);
    			break;
    		case eReplayResume:
    			ResumeReplay(cmdData->listItem);
    			break;
    		case eReplayChangeSpeed:
    			SetSpeed(cmdData->listItem, cmdData->param);
    			break;
    		case eReplayFast:
    			PlayFast(cmdData->listItem);
    			break;
    		case eReplaySlow:
    			PlaySlow(cmdData->listItem);
    			break;
    		case eReplayCapture:
    			CaptureImage(cmdData->listItem);
    			break;
    		case eReplayStartEditVideo:
    			EditVideo(cmdData->listItem, true);
    			break;
    		case eReplayStopEditVideo:
    			EditVideo(cmdData->listItem, false);
    			break;
    		case eReplayIncreaseBright:
    			IncreaseBright();
    			break;
    		case eReplayReduceBright:
    			ReduceBright();
    			break;
    		case eReplayIncreaseContrast:
    			IncreaseContrast();
    			break;
    		case eReplayReduceContrast:
    			ReduceContrast();
    			break;
    		default:
    			break;
    		}
    	}
    	return S_OK;
    }
    

    调用StartReplay

    int CPlaybackManager::StartReplay(std::list<ReplayItemData> &listItem)
    {
    	IVideoReplayControl* replayControl = GetInlPtr<IVideoReplayControl*>(_T("VideoReplayControl"));
    	if (replayControl==NULL)
    	{
    		return 1;
    	}
    	IVideoReplayModel* pModel = GetInlPtr<IVideoReplayModel*>(_T("VideoReplayModel"));
    	if (pModel==NULL)
    	{
    		return 1;
    	}
    	IMitModel * pMitObj = GetInlPtr<IMitModel*>(_T("MitModel"));
    	if (pMitObj==NULL)
    	{
    		return 1;
    	}
    	{
    		std::list<ReplayItemData>::iterator it = listItem.begin();
    		for (; it != listItem.end(); it++)
    		{
    			CPlaybacker *wnd = dynamic_cast<CPlaybacker*>(GetItem(it->nIndexItem));
    			HWND hWnd = wnd->GetPlayWnd();
    			if (hWnd)
    			{
    				LONG streamId;
    				LONG ret;
    				ret = replayControl->Play(it->fileRes, it->oid, ULONG((it->tm_end-it->tm_begin)/1000), hWnd, NULL, it->tm_begin+it->jumpPos*1000, streamId, DrawZoomInFun, (LONG)wnd);
    				if (ret == errNone)
    				{
    					pModel->SetReplayStreamID(it->nIndexItem, streamId);
    					//设置title状态
    					wnd->SetStreamStatus(eReplayStatusLinking, streamId);
    				}
    				else
    				{
    					wnd->SetStreamStatus(eReplayStatusMaxLimit,-1);
    				}
    			}
    		}
    	}
    	return 0;
    }
    

    主要看调用的play函数

    LONG CVideoReplayControl::Play(CString  szOid, CString szEncOid, ULONG nTotalTime,HWND hWnd, void* pParam, SDPTIME jumpPos, LONG &id, DrawFunEx func, LONG drawData)
    {
    	IAVStream *pAVStream = GetInlPtr<IAVStream*>(_T("AVStream"));
    	if (pAVStream==NULL)
    	{
    		return errNotExist;
    	}	
    	//note未知修改,但是回放、电子地图有可能同时回放同一段录像文件
    	//先判断是否重复,依据窗口句柄
    	m_lockDataList.Acquire();
    	std::list<ReplayInf>::iterator it = m_listReplayInf.begin();
    	for (; it != m_listReplayInf.end(); it++)
    	{
    		if (it->hWnd == hWnd)
    		{
    			DEBUGINFO(DEBUG_WARNING, _T("warning -- 在同一窗口中重复开启录像文件"), szOid);
    			m_lockDataList.Release();
    			return 1;
    		}
    	}
    	m_lockDataList.Release();
    	ULONG maxwidth = 0;
    	ULONG maxheight = 0;
    	if(pAVStream->Connect(szOid, ENEAVProtocol_all,ReplayStream,CB_RecvStream,hWnd,false,id,NULL,maxwidth,maxheight) == true)
    	{
    		ReplayInf rp;
    		rp.id = id;
    		rp.strOid = szOid;
    		rp.nBright = 8;
    		rp.nContrast = 8;	
    		rp.nSpeed = 0;
    		rp.hWnd = hWnd;
    		rp.nStatus = 0;
    		rp.bSound = FALSE;
    		rp.bFull = FALSE;
    		rp.nTm = jumpPos;
    		rp.nTotalTime=nTotalTime;
    		rp.bLink = FALSE;
    		rp.strEncOid = szEncOid;
    		rp.nNoDataCome = 0;
    		rp.width=maxwidth;
    		rp.height=maxheight;
    		rp.drawFunc=func;
    		rp.drawData=drawData;
    
    		addReplayInf(rp);
    
    		ReplayDataInf rpDataInf;
    		rpDataInf.id = id;
    		rpDataInf.nPacket = 0;
    		rpDataInf.bPlay = TRUE;
    		rpDataInf.pData = NULL;
    		rpDataInf.bCheck = FALSE;
    		rpDataInf.bCanInputData = TRUE;
    		rpDataInf.bFirst = false;
    		rpDataInf.fEdit=NULL;
    		addReplayDataInf(rpDataInf);
    
    		m_mapAvStreamStatus[id] = 1;
    		return errNone;
    	}
    	return errError;
    }
    

    在后面的处理都位于AVStream中,在此不再深究。


登录后回复
 

Copyright © 2018 bbs.dian.org.cn All rights reserved.

与 Dian 的连接断开,我们正在尝试重连,请耐心等待