UE4 接入海康威视 SDK, 好像这是第一篇UE4接入海康威视的贴

32 篇文章 42 订阅
1 篇文章 1 订阅

原创文章,转载请注明出处。

本文主要讲UE4接入海康的硬盘录像机的视频流,目前是单路,多路调整中

第一步, 去海康威视官网下载C++ Win64 SDK,

第二步,接入到UE4

1>build.cs部分, 加载lib, 加载Include路径

2>直接贴出代码吧, 关于ffmpeg的结合UE4的使用,链接:

3>dll拷贝到你的Binaries目录下,打包之后放到相应的exe目录下

第一步, 去海康威视官网下载C++ Win64 SDK,

下载下来自己有VS2008/2010/2012建议的先把Demo跑一遍,熟悉一遍代码。海康威视SDK链接官网, 如下图

下载下来之后Demo文件夹内部文件夹分的很详细,想接入到UE4那么建议先把Demo跑起来,我是将MFC的工程跑了一遍,基于QT的网上也有些例子也可以学习下。

第二步,接入到UE4

1>build.cs部分, 加载lib, 加载Include路径

2>直接贴出代码吧, 关于ffmpeg的结合UE4的使用,链接:

3>dll拷贝到你的Binaries目录下,打包之后放到相应的exe目录下

1>build.cs部分, 加载lib, 加载Include路径

private string HCPath
    {
        get { return Path.GetFullPath(Path.Combine(ModulePath, "HCNetSDK/lib/")); }
    }

    public bool LoadHC(ReadOnlyTargetRules Target)
    {
        if (Target.Platform == UnrealTargetPlatform.Win64)
        {
            
            PublicAdditionalLibraries.Add(Path.Combine(HCPath, "HCCore.lib"));
            PublicAdditionalLibraries.Add(Path.Combine(HCPath, "HCNetSDK.lib"));
            PublicAdditionalLibraries.Add(Path.Combine(HCPath, "PlayCtrl.lib"));
        }
        return true;
    }

//调用LoadHC即可

2>直接贴出代码吧, 关于ffmpeg的结合UE4的使用,链接: 

// Fill out your copyright notice in the Description page of Project Settings.
/*
*	Author:田华健 tianhj
*	Data:2020-11-23 13:57:26
*	摄像头布置数据管理器
*	Description: 摄像头布置数据管理器
*/
#pragma once

#include "CoreMinimal.h"
#include "Tickable.h"
#include "GeneralDef.h"
#include "../../../../Plugins/UFFmpeg/Source/UFFmpeg/Public/FFmpegDirector.h"

//extern "C" {
//#include <libavcodec/avcodec.h>
//#include <libavformat/avformat.h>
//#include <libavformat/avio.h>
//#include <libavutil/file.h>
//#include "libswresample/swresample.h"
//#include <libavutil/frame.h>
//#include <libavutil/mem.h>
//#include <libavutil/imgutils.h>
//#include <libavutil/samplefmt.h>
//#include <libavutil/timestamp.h>
//#include <libavformat/avformat.h>
//#include <libswscale/swscale.h>
//}

#include "SPCameraManager.generated.h"

class UEditMode_MainUIClass;

UCLASS()
class SPPROJECT_API USPCameraManager : public UObject, public FTickableGameObject
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	USPCameraManager();
	virtual ~USPCameraManager();

	//从视频文件获取封面 sMovieFullPath:要解析的视频全路径, 直接在后面拼一个.jpg
	void SaveJPEGFromMovie(const FString& sImageSavePath);

	//初始化
	void Init();
	void UnInit();

	//Tick
	virtual void Tick(float DeltaTime) override;
	virtual bool IsTickable() const override;
	virtual TStatId GetStatId() const override;

	//UI
	UEditMode_MainUIClass* EditModeMainUI;
	UEditMode_MainUIClass* GetEditModeMainUI();

	//尝试登录硬盘录像机
	bool TrytoLoginDiskCamera();

	//获取DeviceInfo, 如果returnnull的话证明没有login硬盘录像机成功
	pLOCAL_DEVICE_INFO GetDeviceInfo()
	{ 
		return m_struDeviceInfo;
	}

	//获取登录硬盘录像机是否成功了
	bool GetLoginDiskCameraSusccessed() { return m_bIsLogin; }

	void SetCurChanIndex(int val) { m_iCurChanIndex = val; }
	//int GetCurChanIndex() { return m_iCurChanIndex; }

	//当前选中的Index
	void SetCurChanIndex_Select(int val) { m_iCurChanIndex = val; }
	int GetCurChanIndex_Select() { return m_iCurChanIndex; }
	int m_iCurChanIndex_Select;

	bool DbPlayChannel(int ChanIndex);
	bool StopPlay();

private:

	//对应SaveJPEGFromMovie()保存封面
	int saveMovieAsJPEG(AVFrame* pFrame, int width, int height, int index, FString sName);

	void InitDecoderReferCtrl();
	void GetDecoderCfg();
	void StopRecord();
	bool StartPlay(int iChanIndex);
	bool DoLogin();
	void DoGetDeviceResoureCfg();

	bool m_bIsLogin;
	bool m_bIsPlaying;
	bool m_bIsRecording;
	UINT	m_nDevPort;
	pLOCAL_DEVICE_INFO m_struDeviceInfo;	//如果returnnull的话证明没有login硬盘录像机成功
	int m_iCurChanIndex;               //当前通道在数组中索引
	LONG m_lPlayHandle;
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "SPCameraManager.h"
#include "Kismet/KismetSystemLibrary.h"
#include "../../UtilsLibrary.h"
#include "../../SPGameInstance.h"
#include "Misc/MessageDialog.h"
#include "Blueprint/WidgetBlueprintLibrary.h"
#include "../../UI/EditMode/EM_Main.h"
#include "../../GamePlay/SPDelegates.h"
#include "../../GetRegValue.h"
#include "../../UI/DoubleMode/DM_CameraStreamPlayer.h"

#pragma   warning(disable: 4996)//忽略被否决的警告

//从视频文件获取封面
void USPCameraManager::SaveJPEGFromMovie(const FString& sImageSavePath)
{
	/*FString szMsg = sMovieFullPath;
	TCHAR* pSendData = szMsg.GetCharArray().GetData();
	int32 nDataLen = FCString::Strlen(pSendData);
	char* sDest = new char[sizeof(char) * nDataLen];
	TcharToChar(pSendData, sDest);
	std::string sPath = sDest;
	delete[]sDest;*/
	AVFormatContext *pFormatCtx = avformat_alloc_context();
	int res;
	std::string sPath = TCHAR_TO_UTF8(*sImageSavePath);
	res = avformat_open_input(&pFormatCtx, sPath.c_str(), nullptr, nullptr);
	if (res) {
		return;
	}
	avformat_find_stream_info(pFormatCtx, nullptr);
	int videoStream = -1;
	for (int i = 0; i < (int)(pFormatCtx->nb_streams); i++) {
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
			videoStream = i;
			break;
		}
	}
	if (videoStream == -1) {
		return;
	}
	AVCodecContext *pCodecCtxOrig = nullptr;
	// Get a pointer to the codec context for the video stream
	pCodecCtxOrig = pFormatCtx->streams[videoStream]->codec;
	AVCodec *pCodec = nullptr;
	// Find the decoder for the video stream
	pCodec = avcodec_find_decoder(pCodecCtxOrig->codec_id);
	if (pCodec == nullptr) {
		fprintf(stderr, "Unsupported codec!\n");
		return; // Codec not found
	}
	AVCodecContext *pCodecCtx = nullptr;
	// Copy context
	pCodecCtx = avcodec_alloc_context3(pCodec);
	if (avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) {
		fprintf(stderr, "Couldn't copy codec context");
		return; // Error copying codec context
	}
	// Open codec
	if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) {
		return;// Could not open codec
	}
	AVFrame *pFrameRGB = nullptr;
	pFrameRGB = av_frame_alloc();
	res = av_seek_frame(pFormatCtx, -1, 10 * AV_TIME_BASE, AVSEEK_FLAG_BACKWARD);//10(second)
	if (res < 0) {
		return;
	}
	AVPacket packet;
	while (1) {
		av_read_frame(pFormatCtx, &packet);
		if (packet.stream_index == videoStream) {
			res = avcodec_send_packet(pCodecCtx, &packet);
			int gotPicture = avcodec_receive_frame(pCodecCtx, pFrameRGB); //gotPicture = 0 success, a frame was returned
			if (gotPicture == 0) {
				SwsContext* swsContext = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, (AVPixelFormat)AV_PIX_FMT_RGB24,
					SWS_BICUBIC, nullptr, nullptr, nullptr);
				AVFrame* frameRGB = av_frame_alloc();
				avpicture_alloc((AVPicture*)frameRGB, (AVPixelFormat)AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
				sws_scale(swsContext, pFrameRGB->data, pFrameRGB->linesize, 0, pCodecCtx->height, frameRGB->data, frameRGB->linesize);
				saveMovieAsJPEG(pFrameRGB, pCodecCtx->width, pCodecCtx->height, 10, sImageSavePath);
				avformat_close_input(&pFormatCtx);
				return;
			}
		}
	}
}

int USPCameraManager::saveMovieAsJPEG(AVFrame* pFrame, int width, int height, int index, FString sName)
{
	char out_file[256] = { 0 };
	sprintf_s(out_file, sizeof(out_file), "%s%d.jpg", "", index);
	sName.Append(".jpg");
	std::string cstr(TCHAR_TO_UTF8(*sName));

	AVFormatContext* pFormatCtx = avformat_alloc_context();
	pFormatCtx->oformat = av_guess_format("mjpeg", nullptr, nullptr);
	if (avio_open(&pFormatCtx->pb, cstr.c_str(), AVIO_FLAG_READ_WRITE) < 0)
	{
		printf("Couldn't open output file.");
		return -1;
	}
	AVStream* pAVStream = avformat_new_stream(pFormatCtx, 0);
	if (pAVStream == nullptr)
	{
		return -1;
	}
	AVCodecContext* pCodecCtx = pAVStream->codec;
	pCodecCtx->codec_id = pFormatCtx->oformat->video_codec;
	pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
	pCodecCtx->pix_fmt = (AVPixelFormat)AV_PIX_FMT_YUVJ420P;
	pCodecCtx->width = width;
	pCodecCtx->height = height;
	pCodecCtx->time_base.num = 1;
	pCodecCtx->time_base.den = 25;
	//打印输出相关信息
	av_dump_format(pFormatCtx, 0, out_file, 1);
	//================================== 查找编码器 ==================================//
	AVCodec* pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
	if (!pCodec)
	{
		printf("Codec not found.");
		return -1;
	}
	if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0)
	{
		printf("Could not open codec.");
		return -1;
	}
	//================================Write Header ===============================//
	avformat_write_header(pFormatCtx, nullptr);
	int y_size = pCodecCtx->width * pCodecCtx->height;
	AVPacket pkt;
	av_new_packet(&pkt, y_size * 3);

	//
	int got_picture = 0;
	int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
	if (ret < 0)
	{
		printf("Encode Error.\n");
		return -1;
	}
	if (got_picture == 1)
	{
		pkt.stream_index = pAVStream->index;
		ret = av_write_frame(pFormatCtx, &pkt);
	}
	av_free_packet(&pkt);
	av_write_trailer(pFormatCtx);
	if (pAVStream)
	{
		avcodec_close(pAVStream->codec);
	}
	avio_close(pFormatCtx->pb);
	avformat_free_context(pFormatCtx);
	return 0;
}

/*
*	使用FFmpeg库来进行图片数据, 速度是所有转换里面最快的.需要注意的是UE4是RGB32而不是RGB24, 如果用RGB24你会发现怎么转都不对.卡了半天
*	转换效率的对比, 尝试过的方法和时间
	Method					Time(ms)
	YV12ToBGR24_Native		83.7263
	YV12ToBGR24_Table		54.2376
	YV12ToBGR24_OpenCV		26.0529
	YV12ToBGR24_FFmpeg		3.41499
	YV12ToBGR24_Pinknoise	14.1215
*	https://www.cnblogs.com/yuandaozhe/p/5766717.html
*	ffmpeg集成:https://www.cnblogs.com/xiexinbei0318/p/11426110.html  &  https://ffmpeg.zeranoe.com/builds/
*	https://blog.csdn.net/shangtao1989/article/details/50260661		https://www.cnblogs.com/dwdxdy/p/3713990.html 使用ffmpeg解码的贴子
*	注意这里ffmpeg会出现函数被否决,需要用新接口替换,也可以忽略这个警告,并且头文件需要用extern "C"包含
*	extern "C"
*	{
*		#include <libavcodec\avcodec.h>
*		#include <libswscale\swscale.h>
*		#include <libavutil\pixfmt.h>
*		#include <libavutil\imgutils.h>
*	}
*	#pragma   warning(disable: 4996)//忽略被否决的警告
*/
bool YV12ToBGR32_FFmpeg(unsigned char* pYUV, unsigned char* pBGR24, int width, int height)
{
	if (width < 1 || height < 1 || pYUV == NULL || pBGR24 == NULL)
		return false;
	AVPicture pFrameYUV, pFrameBGR;
	avpicture_fill(&pFrameYUV, pYUV, (AVPixelFormat)AV_PIX_FMT_YUV420P, width, height);
	//U,V互换
	uint8_t * ptmp = pFrameYUV.data[1];
	pFrameYUV.data[1] = pFrameYUV.data[2];
	pFrameYUV.data[2] = ptmp;
	avpicture_fill(&pFrameBGR, pBGR24, (AVPixelFormat)AV_PIX_FMT_BGRA, width, height);

	struct SwsContext* imgCtx = NULL;
	imgCtx = sws_getContext(width, height, (AVPixelFormat)AV_PIX_FMT_YUV420P, width, height, (AVPixelFormat)AV_PIX_FMT_BGRA, SWS_BILINEAR, 0, 0, 0);

	if (imgCtx != NULL) {
		sws_scale(imgCtx, pFrameYUV.data, pFrameYUV.linesize, 0, height, pFrameBGR.data, pFrameBGR.linesize);
		if (imgCtx) {
			sws_freeContext(imgCtx);
			imgCtx = NULL;
		}
		return true;
	}
	else {
		sws_freeContext(imgCtx);
		imgCtx = NULL;
		return false;
	}
}

//1s/25帧	解码回调 视频为YUV数据(YV12),音频为PCM数据
void CALLBACK g_DecCBFun(long nPort, char* pBuf, long nSize, FRAME_INFO* pFrameInfo, long nReserved1, long nReserved2)
{
	//GHardDiskVideoPort.Add(nPort);
	GHardDiskVideoBuf.Add(pBuf);
	GHardDiskVideoSize.Add(nSize);
	//PFrameInfo = pFrameInfo;
	GHardDiskVideoWidth.Add(pFrameInfo->nWidth);
	GHardDiskVideoHeight.Add(pFrameInfo->nHeight);
	//GHardDiskVideoStamp.Add(pFrameInfo->nStamp);
	GHardDiskVideoType.Add(pFrameInfo->nType);
	//GHardDiskVideoFrameRate.Add(pFrameInfo->nFrameRate);
	//GHardDiskVideoFrameNum.Add(pFrameInfo->dwFrameNum);
	//GHardDiskVideoReserved1.Add(nReserved1);
	//GHardDiskVideoReserved2.Add(nReserved2);
}

/*********************************************************
  Function:	g_RealDataCallBack_V30
  Desc:		use the player interface to decode the stream
  Input:	lRealHandle, player port;
			dwDataType,data type;
			pBuffer, stream data;
			dwBufSize, data length;
			pUser, user data,here is the current play output window index;
  Output:
  Return:
**********************************************************/
static LONG nGetPort = -1;
void CALLBACK g_RealDataCallBack(LONG IRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void* pUser)
{
	//DWORD dRet = 0;
	bool inData = false;

	switch (dwDataType)
	{
	case NET_DVR_SYSHEAD:
		//一一对应PlayM4_Play/PlayM4_OpenStream/PlayM4_GetPort.不调用的话会有内存泄漏, 天坑, 海康威视文档连个介绍都没有
		/*PlayM4_StopSound();*/
		PlayM4_Stop(nGetPort);
		PlayM4_CloseStream(nGetPort);
		PlayM4_FreePort(nGetPort);

		if (!PlayM4_GetPort(&nGetPort))
		{
			break;
		}
		//if (dwBufSize > 0)
		//{
		if (!PlayM4_SetStreamOpenMode(nGetPort, STREAME_REALTIME))
		{
			break;
		}

		if (!PlayM4_OpenStream(nGetPort, pBuffer, dwBufSize, 1024 * 1024))
		{
			//dRet = PlayM4_GetLastError(nPort);
			break;
		}

		if (!PlayM4_Play(nGetPort, NULL))
		{
			//dRet = PlayM4_GetLastError(nPort);
			break;
		}

		打开音频解码, 需要码流是复合流
		//if (!PlayM4_PlaySound(nGetPort))
		//{
		//	//dRet = PlayM4_GetLastError(nPort);
		//	break;
		//}

		if (!PlayM4_SetDecCallBack(nGetPort, g_DecCBFun))
		{
			//dRet = PlayM4_GetLastError(nPort);
			break;
		}
		//}
		break;
	case NET_DVR_STREAMDATA:
		if (dwBufSize > 0 && nGetPort != -1)
		{
			inData = PlayM4_InputData(nGetPort, pBuffer, dwBufSize);
			while (!inData)
			{
				Sleep(10);
				inData = PlayM4_InputData(nGetPort, pBuffer, dwBufSize);
				//printf("PlayM4_InputData faild %d\n", PlayM4_GetLastError(nPort));

			}
		}
		break;
	default:
	{
		inData = PlayM4_InputData(nGetPort, pBuffer, dwBufSize);
		while (!inData)
		{
			Sleep(10);
			inData = PlayM4_InputData(nGetPort, pBuffer, dwBufSize);
			//OutputDebugString("PlayM4_InputData failed 2\n");
		}
	}
	break;
	}
}

void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
	//char tempbuf[256] = { 0 };
	switch (dwType)
	{
	case EXCEPTION_RECONNECT:    //预览时重连
		//FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("HaiKangWeiShi SDK TIPS_g_ExceptionCallBack EXCEPTION_RECONNECT")));
		//printf("----------reconnect--------%d\n", time(NULL));
		break;
	default:
		break;
	}
}

// Sets default values
USPCameraManager::USPCameraManager() 
	: EditModeMainUI(nullptr)
	, m_iCurChanIndex_Select(-1)
	, m_bIsLogin(false)
	, m_bIsPlaying(false)
	, m_bIsRecording(false)
	, m_nDevPort(8000)
	, m_struDeviceInfo(nullptr)
	, m_iCurChanIndex(-1)
	, m_lPlayHandle(-1)
{

}

USPCameraManager::~USPCameraManager()
{
	UnInit();
	if (m_struDeviceInfo)
	{
		delete m_struDeviceInfo;
		m_struDeviceInfo = nullptr;
	}
}

void USPCameraManager::Init()
{
	//Init SDK
	NET_DVR_Init();

	//Set reconnect time
	NET_DVR_SetConnectTime(2000, 1);
	NET_DVR_SetReconnect(10000, true);

	//设置异常消息回调函数
	NET_DVR_SetExceptionCallBack_V30(0, NULL, g_ExceptionCallBack, NULL);	
}

void USPCameraManager::UnInit()
{
	NET_DVR_Cleanup();
}

UEditMode_MainUIClass* USPCameraManager::GetEditModeMainUI()
{
	if (!EditModeMainUI)
	{
		TArray<UUserWidget*> FoundWidgets;
		UWidgetBlueprintLibrary::GetAllWidgetsOfClass(GGameInstance, FoundWidgets, UEditMode_MainUIClass::StaticClass());
		if (FoundWidgets.Num() == 1)
		{
			EditModeMainUI = Cast<UEditMode_MainUIClass>(FoundWidgets[0]);
		}
	}
	return EditModeMainUI;
}

void USPCameraManager::Tick(float DeltaTime)
{
	//解码回调 视频为YUV数据(YV12),音频为PCM数据
	if (GHardDiskVideoType.Num() <= 0)
		return;
	long lFrameType = GHardDiskVideoType[0];
	char* pBuf = GHardDiskVideoBuf[0];
	long nSize = GHardDiskVideoSize[0];
	long nWidth = GHardDiskVideoWidth[0];
	long nHeight = GHardDiskVideoHeight[0];

	if (lFrameType == T_YV12)
	{
		//new function 1 无硬盘读取, 直接采用NV12转RGB32来解决
		int sizeRGB = nWidth * nHeight * 4;
		char* pBGR32 = new char[sizeRGB];
		memset(pBGR32, 0, sizeRGB);
		YV12ToBGR32_FFmpeg((unsigned char*)pBuf, (unsigned char*)pBGR32, nWidth, nHeight);
		UTexture2D* texture = UTexture2D::CreateTransient(nWidth, nHeight, PF_B8G8R8A8);
		//textureArr.Add(texture);
		// Fill in the source data from the file
		void* TextureData = texture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
		FMemory::Memcpy(TextureData, pBGR32, sizeRGB);
		texture->PlatformData->Mips[0].BulkData.Unlock();
		// Update the rendering resource from data.
		texture->UpdateResource();

		//当前主UI上面的滑动CameraItem
		if (GCameraItemIsPlayingStatus)
		{
			GetEditModeMainUI()->Image_CameraStream->SetBrushResourceObject(texture);
		}

		//当前是世界中播放
		if (GDM_CameraStreamPlayerUIClassArray.Num() > 0)
		{
			for (auto widget : GDM_CameraStreamPlayerUIClassArray)
			{
				CONTINUE_IF_NULL(widget);
				CONTINUE_IF_NULL(widget->Image_Movie);				
				widget->Image_Movie->SetBrushResourceObject(texture);
			}
		}


		FMemory::Free(pBGR32);
		//FMemory::Free(TextureData);
		pBGR32 = nullptr;

		new function 2, 有硬盘的读取
		//FString csDir = "D:\\qrcode\\SPProject\\Source\\1.jpg";
		//if (!PlayM4_ConvertToJpegFile(pBuf, nSize, nWidth, nHeight, lFrameType, TCHAR_TO_UTF8(*csDir))) {
		//	UUtilsLibrary::Log("Failed PlayM4_ConvertToJpegFile", true, 5.f, FColor::Red);
		//	return;
		//}
		//UTexture2D* t1 = UMedusaLibrary::LoadTexture(csDir);
		//if (ParentUI)
		//{
		//	ParentUI->Image_Streaming->SetBrushResourceObject(t1);
		//}
	}
	/*else if(lFrameType == T_AUDIO16)
	{
		//处理声音部分
	}*/
	GHardDiskVideoBuf.Remove(pBuf);
	GHardDiskVideoSize.Remove(nSize);
	GHardDiskVideoWidth.Remove(nWidth);
	GHardDiskVideoHeight.Remove(nHeight);
	GHardDiskVideoType.Remove(lFrameType);
}

bool USPCameraManager::IsTickable() const
{
	return true;
}

TStatId USPCameraManager::GetStatId() const
{
	RETURN_QUICK_DECLARE_CYCLE_STAT(USPCameraManager, STATGROUP_Tickables);
}

//获取登录硬盘录像机是否成功了
bool USPCameraManager::TrytoLoginDiskCamera()
{
	//如果已经登录成功了, 再改了参数又去Login的情况没有处理
	//todo...

	//try to login
	if (!m_bIsLogin)
	{
		if (!DoLogin())
			return false;
		DoGetDeviceResoureCfg();	//获取设备资源信息

		m_bIsLogin = true;

		//广播硬盘录像机登录成功
		FSPDelegates::DiskCameraLoginSuccessedDelegate.Broadcast();
	}
	return m_bIsLogin;
}

/*************************************************
函数名:    	DoLogin
函数描述:	向设备注册
输入参数:
输出参数:
返回值:
**************************************************/
bool USPCameraManager::DoLogin()
{
	RETURN_FALSE_IF_NULL(GGameInstance);
	RETURN_FALSE_IF_NULL(GGameInstance->GetLocalSave_CameraInfo());

	FString DeviceIp = GGameInstance->GetLocalSave_CameraInfo()->GetIP();
	RETURN_FALSE_IF(DeviceIp.IsEmpty());
	FString account = GGameInstance->GetLocalSave_CameraInfo()->GetAccount();
	RETURN_FALSE_IF(account.IsEmpty());
	FString password = GGameInstance->GetLocalSave_CameraInfo()->GetPassword();
	RETURN_FALSE_IF(password.IsEmpty());

	char* ip = TCHAR_TO_ANSI(*DeviceIp);
	m_nDevPort = GGameInstance->GetLocalSave_CameraInfo()->GetPort();
	char* accountstr = TCHAR_TO_ANSI(*account);
	char* passwordstr = TCHAR_TO_ANSI(*password);


	//register device
	LONG IUserID;
	NET_DVR_USER_LOGIN_INFO struLoginInfo = { 0 };
	NET_DVR_DEVICEINFO_V40 struDeviceInfo = { 0 };

	strcpy_s((char*)struLoginInfo.sDeviceAddress, sizeof(struLoginInfo.sDeviceAddress), ip);
	strcpy_s((char*)struLoginInfo.sUserName, sizeof(struLoginInfo.sUserName), accountstr);
	strcpy_s((char*)struLoginInfo.sPassword, sizeof(struLoginInfo.sPassword), passwordstr);
	struLoginInfo.wPort = m_nDevPort;
	struLoginInfo.bUseAsynLogin = 0;

	IUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfo);
	if (IUserID == -1)
	{
		DWORD err = NET_DVR_GetLastError();
		FString error = "login faild errror, error_code:";
		error.Append(FString::FromInt(err));
		error.Append(".SDK Tips:");
		error.Append(NET_DVR_GetErrorMsg());
		FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(error));
		return false;
	}
	if (!m_struDeviceInfo)
	{
		m_struDeviceInfo = new STRU_DEVICE_INFO();
	}

	m_struDeviceInfo->lLoginID = IUserID;
	m_struDeviceInfo->iDeviceChanNum = struDeviceInfo.struDeviceV30.byChanNum;
	m_struDeviceInfo->iIPChanNum = struDeviceInfo.struDeviceV30.byIPChanNum;
	m_struDeviceInfo->iStartChan = struDeviceInfo.struDeviceV30.byStartChan;
	m_struDeviceInfo->iIPStartChan = struDeviceInfo.struDeviceV30.byStartDChan;

	return true;
}

/*************************************************
函数名:    	DoGetDeviceResoureCfg
函数描述:	获取设备的通道资源
输入参数:
输出参数:
返回值:
**************************************************/
void USPCameraManager::DoGetDeviceResoureCfg()
{
	NET_DVR_IPPARACFG_V40 IpAccessCfg;
	memset(&IpAccessCfg, 0, sizeof(IpAccessCfg));
	DWORD  dwReturned;

	int iGroupNO = 0;
	m_struDeviceInfo->bIPRet = NET_DVR_GetDVRConfig(m_struDeviceInfo->lLoginID, NET_DVR_GET_IPPARACFG_V40, iGroupNO, &IpAccessCfg, sizeof(NET_DVR_IPPARACFG_V40), &dwReturned);

	int i;
	if (!m_struDeviceInfo->bIPRet)   //不支持ip接入,9000以下设备不支持禁用模拟通道
	{
		for (i = 0; i < MAX_ANALOG_CHANNUM; i++)
		{
			if (i < m_struDeviceInfo->iDeviceChanNum)
			{
				sprintf_s(m_struDeviceInfo->struChanInfo[i].chChanName, "camera%d", i + m_struDeviceInfo->iStartChan);
				m_struDeviceInfo->struChanInfo[i].iChanIndex = i + m_struDeviceInfo->iStartChan;  //通道号
				m_struDeviceInfo->struChanInfo[i].bEnable = true;

			}
			else
			{
				m_struDeviceInfo->struChanInfo[i].iChanIndex = -1;
				m_struDeviceInfo->struChanInfo[i].bEnable = false;
				sprintf_s(m_struDeviceInfo->struChanInfo[i].chChanName, "");
			}
		}
	}
	else        //支持IP接入,9000设备
	{
		for (i = 0; i < MAX_ANALOG_CHANNUM; i++)  //模拟通道
		{
			if (i < m_struDeviceInfo->iDeviceChanNum)
			{
				sprintf_s(m_struDeviceInfo->struChanInfo[i].chChanName, "camera%d", i + m_struDeviceInfo->iStartChan);
				m_struDeviceInfo->struChanInfo[i].iChanIndex = i + m_struDeviceInfo->iStartChan;
				if (IpAccessCfg.byAnalogChanEnable[i])
				{
					m_struDeviceInfo->struChanInfo[i].bEnable = true;
				}
				else
				{
					m_struDeviceInfo->struChanInfo[i].bEnable = false;
				}

			}
			else//clear the state of other channel
			{
				m_struDeviceInfo->struChanInfo[i].iChanIndex = -1;
				m_struDeviceInfo->struChanInfo[i].bEnable = false;
				sprintf_s(m_struDeviceInfo->struChanInfo[i].chChanName, "");
			}
		}

		//数字通道
		for (i = 0; i < MAX_IP_CHANNEL; i++)
		{
			if (IpAccessCfg.struStreamMode[i].uGetStream.struChanInfo.byEnable)  //ip通道在线
			{
				int byIPID = IpAccessCfg.struStreamMode[i].uGetStream.struChanInfo.byIPID;
				int byIPIDHigh = IpAccessCfg.struStreamMode[i].uGetStream.struChanInfo.byIPIDHigh;
				int iDevInfoIndex = byIPIDHigh * 256 + byIPID - 1 - iGroupNO * 64;
				//FString SIP(IpAccessCfg.struIPDevInfo[iDevInfoIndex].struIP.sIpV4);
				//printf("通道号%d 在线 摄像头取流, IP: %s\n", i + 1, IpAccessCfg.struIPDevInfo[iDevInfoIndex].struIP.sIpV4);

				int tempChanIndex = i + IpAccessCfg.dwStartDChan;
				m_struDeviceInfo->struChanInfo[i + MAX_ANALOG_CHANNUM].bEnable = true;
				m_struDeviceInfo->struChanInfo[i + MAX_ANALOG_CHANNUM].iChanIndex = tempChanIndex;
				sprintf_s(m_struDeviceInfo->struChanInfo[i + MAX_ANALOG_CHANNUM].chChanName, "IP Camera %d", i + 1);
				sprintf_s(m_struDeviceInfo->struChanInfo[i + MAX_ANALOG_CHANNUM].chAccessChanIP, IpAccessCfg.struIPDevInfo[iDevInfoIndex].struIP.sIpV4);

				int nDeviceIDLen = sizeof(IpAccessCfg.struIPDevInfo[iDevInfoIndex].szDeviceID);
				memcpy(m_struDeviceInfo->struChanInfo[i + MAX_ANALOG_CHANNUM].szDeviceID, IpAccessCfg.struIPDevInfo[iDevInfoIndex].szDeviceID, nDeviceIDLen);

				//视频上叠加的通道名称、OSD时间等显示使能、属性等
				NET_DVR_PICCFG_V40 net_dvr_piccfg_v40;
				memset(&net_dvr_piccfg_v40, 0, sizeof(net_dvr_piccfg_v40));
				DWORD dwPiccfg_Returned;
				if (NET_DVR_GetDVRConfig(m_struDeviceInfo->lLoginID, NET_DVR_GET_PICCFG_V40, tempChanIndex, &net_dvr_piccfg_v40, sizeof(NET_DVR_PICCFG_V40), &dwPiccfg_Returned))
				{
					int nLen = sizeof(net_dvr_piccfg_v40.sChanName);
					char* name = (char*)net_dvr_piccfg_v40.sChanName;
					memcpy(m_struDeviceInfo->struChanInfo[i + MAX_ANALOG_CHANNUM].chChanName, name, nLen);
					m_struDeviceInfo->struChanInfo[i + MAX_ANALOG_CHANNUM].chChanName[nLen] = '\0';
				}
				else
				{
					//get failed
				}
			}
			else
			{
				m_struDeviceInfo->struChanInfo[i + MAX_ANALOG_CHANNUM].bEnable = false;
				m_struDeviceInfo->struChanInfo[i + MAX_ANALOG_CHANNUM].iChanIndex = -1;
			}
		}


	}
}

/*************************************************
函数名:    	GetDecoderCfg
函数描述:	获取云台解码器信息
输入参数:
输出参数:
返回值:
**************************************************/
void USPCameraManager::GetDecoderCfg()
{
	NET_DVR_DECODERCFG_V30 DecoderCfg;
	DWORD  dwReturned;
	bool bRet;

	//获取通道解码器信息
	for (int i = 0; i < MAX_CHANNUM_V30; i++)
	{
		if (m_struDeviceInfo->struChanInfo[i].bEnable)
		{
			memset(&DecoderCfg, 0, sizeof(NET_DVR_DECODERCFG_V30));
			bRet = NET_DVR_GetDVRConfig(m_struDeviceInfo->lLoginID, NET_DVR_GET_DECODERCFG_V30, \
				m_struDeviceInfo->struChanInfo[i].iChanIndex, &DecoderCfg, sizeof(NET_DVR_DECODERCFG_V30), &dwReturned);
			if (!bRet)
			{
				auto str = FString::Printf(TEXT("Get DecderCfg failed,Chan:%d\n"), m_struDeviceInfo->struChanInfo[i].iChanIndex);
				UUtilsLibrary::Log(str, true);
				//TRACE("Get DecderCfg failed,Chan:%d\n", m_struDeviceInfo->struChanInfo[i].iChanIndex);
				continue;
			}

			memcpy(&m_struDeviceInfo->struChanInfo[i].struDecodercfg, &DecoderCfg, sizeof(NET_DVR_DECODERCFG_V30));
		}

	}

}

/*************************************************
函数名:    	InitDecoderReferCtrl
函数描述:	初始化云台控制相关控件
输入参数:
输出参数:
返回值:
**************************************************/
void USPCameraManager::InitDecoderReferCtrl()
{
	int i = 0;
	FString tmp = "";
	//设置预置点
	for (i = 0; i < MAX_PRESET_V30; i++)
	{
		tmp = "";
		tmp.Append(FString::FromInt(i + 1));
//		ComboxPreset->AddOption(tmp);
	}
//	ComboxPreset->SetSelectedIndex(0);

	//巡航轨迹
	for (i = 0; i < MAX_CRUISE_SEQ; i++)
	{
		tmp = "";
		tmp.Append(FString::FromInt(i + 1));
//		ComboxSeq->AddOption(tmp);
	}
//	ComboxSeq->SetSelectedIndex(0);
}

/*************************************************
函数名:    	StartPlay
函数描述:	开始一路播放
输入参数:   ChanIndex-通道号
输出参数:
返回值:
**************************************************/
bool USPCameraManager::StartPlay(int iChanIndex)
{
	NET_DVR_SDKMEMPOOL_CFG struSDKMemPoolCfg = { 0 };

	if (!NET_DVR_ReleaseSDKMemPool(&struSDKMemPoolCfg))
	{
		UUtilsLibrary::Log("Set Release Sdk MemPool fail", true, 5.f, FColor::Red);
	}
	else
	{
		UUtilsLibrary::Log("Set Release Sdk MemPool successful", true);
	}

	/*ClientInfo.hPlayWnd = NULL;
	ClientInfo.lChannel = m_iCurChanIndex + 1;
	ClientInfo.lLinkMode = 0;
	ClientInfo.sMultiCastIP = NULL;*/

	//display
	NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
	struPlayInfo.hPlayWnd = NULL;
	struPlayInfo.lChannel = m_iCurChanIndex + 1;
	struPlayInfo.dwStreamType = 0;
	struPlayInfo.dwLinkMode = 0;
	struPlayInfo.bBlocked = 0;
	struPlayInfo.dwDisplayBufNum = 15;

	SetCurChanIndex_Select(iChanIndex);

	//m_lPlayHandle = NET_DVR_RealPlay_V30(m_struDeviceInfo->lLoginID, &ClientInfo, g_RealDataCallBack, NULL, true);
	m_lPlayHandle = NET_DVR_RealPlay_V40(m_struDeviceInfo->lLoginID, &struPlayInfo, g_RealDataCallBack, NULL);
	if (m_lPlayHandle < 0)
	{
		FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(NET_DVR_GetErrorMsg()));
		//printf("play failed, error code:%d\n", NET_DVR_GetLastError());
		NET_DVR_Logout(m_struDeviceInfo->lLoginID);
		return false;
	}

	m_bIsPlaying = true;
	//	TextBlock_PlayStatus->SetText(FText::FromString("StopPlay"));
	FSPDelegates::DiskCameraStartPlayDelegate.Broadcast();
	return true;
}

/*************************************************
函数名:    	StopPlay
函数描述:	停止播放
输入参数:
输出参数:
返回值:
**************************************************/
bool USPCameraManager::StopPlay()
{
	if (m_lPlayHandle != -1)
	{
		if (m_bIsRecording)  //正在录像,先停止
		{
			StopRecord();
		}
		if (NET_DVR_StopRealPlay(m_lPlayHandle))
		{
			SetCurChanIndex_Select(-1);

			NET_DVR_SDKMEMPOOL_CFG struSDKMemPoolCfg = { 0 };
			if (!NET_DVR_ReleaseSDKMemPool(&struSDKMemPoolCfg))
			{
				UUtilsLibrary::Log("Set Release Sdk MemPool fail", true, 5.f, FColor::Red);
			}
			else
			{
				UUtilsLibrary::Log("Set Release Sdk MemPool successful", true);
			}

			m_lPlayHandle = -1;
			m_bIsPlaying = false;
			//			TextBlock_PlayStatus->SetText(FText::FromString("Play"));
			FSPDelegates::DiskCameraStopPlayDelegate.Broadcast();
		}
		else
		{
			FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("StopPlay failed"));
			return false;
		}
	}
	return true;
}

/*************************************************
函数名:    	StopRecord
函数描述:	停止录像
输入参数:
输出参数:
返回值:
**************************************************/
void USPCameraManager::StopRecord()
{
	if (!NET_DVR_StopSaveRealData(m_lPlayHandle))
	{
		//MessageBox("停止录像失败");
		FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("StopRecord failed"));
		return;
	}
	m_bIsRecording = false;
//	TextBlock_RecordStatus->SetText(FText::FromString("Record"));
}

/*************************************************
函数名:    	DbPlayChannel
函数描述:	双击播放
输入参数:   ChanIndex-通道号
输出参数:
返回值:
**************************************************/
bool USPCameraManager::DbPlayChannel(int ChanIndex)
{
	StopPlay();
	return StartPlay(ChanIndex);
}

3>dll拷贝到你的Binaries目录下,打包之后放到相应的exe目录下

谢谢,创作不易,大侠请留步… 动起可爱的双手,来个赞再走呗 <( ̄︶ ̄)>

  • 27
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WhiteTian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值