1.客户端(unity)构建一个查询界面,传给服务端要查询哪段时间的哪个摄像头,因为是通过录像机取的,就是哪个通道。
2.服务端调用SDK代码,下载文件。
3.客户端同时轮询下载进度,SDK的Demo就是这么干的,Timer里面轮询进度。
4.下载完成后用ump播放。
客户端没有下载,是服务端下载的,客户端只是根据下载结束后给点一个url,进行hls播放。
一.接口定位(WCF)
[ServiceContract]
public interface INVSPlayer
{
/// <summary>
/// 停止下载
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
[OperationContract]
DownloadInfo StopGetNVSVideo(DownloadInfo info);
/// <summary>
/// 开始下载
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
[OperationContract]
DownloadInfo StartGetNVSVideo(DownloadInfo info);
/// <summary>
/// 获取下载进度
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
[OperationContract]
DownloadProgress GetNVSProgress(DownloadInfo info);
}
public class DownloadInfo
{
/// <summary>
/// Dev_Camera的Id,为了获取下面的IP和通道
/// </summary>
public int CId { get; set; }
/// <summary>
/// 录像机IP
/// </summary>
public string Ip { get; set; }
/// <summary>
/// 录像机通道
/// </summary>
public string Channel { get; set; }
/// <summary>
/// 开始时间 2019-07-19 13:07:45 "yyyy-MM-dd HH:mm:ss"
/// </summary>
public string StartTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public string EndTime { get; set; }
/// <summary>
/// 结果
/// </summary>
public bool Result { get; set; }
/// <summary>
/// 消息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 下载Id
/// </summary>
public int DId { get; set; }
/// <summary>
/// hls地址(已经存在该文件的情况下)
/// </summary>
public string Url { get; set; }
public override string ToString()
{
//return string.Format("cid:{0},start:{1},end:{2}", CId, StartTime, EndTime);
string json = JsonConvert.SerializeObject(this);
return json;
}
}
public class DownloadProgress
{
/// <summary>
/// 下载Id
/// </summary>
public int DId { get; set; }
/// <summary>
/// 进度
/// </summary>
public int Progress { get; set; }
/// <summary>
/// 进度内容
/// </summary>
public string ProgressText { get; set; }
/// <summary>
/// 是否完成
/// </summary>
public bool IsFinished { get; set; }
/// <summary>
/// hls地址
/// </summary>
public string Url { get; set; }
public override string ToString()
{
//return string.Format("cid:{0},Progress:{1},ProgressText:{2},IsFinished:{3},Url{4}", CId, Progress,
// ProgressText, IsFinished, Url);
string json = JsonConvert.SerializeObject(this);
return json;
}
}
二.接口实现
public DownloadInfo StopGetNVSVideo(DownloadInfo info)
{
try
{
Log.Info(LogTags.DbGet, "StopGetNVSVideo:" + info);
NVSManage.GetPlayBackFormEx(info.Ip, (f, s) => { f.Stop(); });
return info;
}
catch (Exception e)
{
Log.Error(LogTags.DbGet, "LocationService.StopGetNVSVideo:" + ToString());
return null;
}
}
public DownloadInfo StartGetNVSVideo(DownloadInfo info)
{
try
{
Log.Info(LogTags.DbGet, "StartGetNVSVideo:"+ info);
if (info == null) return null;
//客户端可以把IP(录像机的IP)和Channel传过来(不用查数据库),也可以只传一个摄像机Id(要查数据库)
if (string.IsNullOrEmpty(info.Ip) || string.IsNullOrEmpty(info.Channel))
{
Log.Info(LogTags.DbGet, "GetIpAndChannelFormDb");
if (!GetIpAndChannelFormDb(info))//GetIpAndChannelFormDb里面会填充Ip和Channel信息,但是没有找到摄像头的话就返回了。
{
info.Result = false;
info.Message = "camera == null";
return info;
}
}
Log.Info(LogTags.DbGet, "info:"+ info);
var start = info.StartTime.ToDateTime();
var end = info.EndTime.ToDateTime();
var file = Downloader.GetFileName(start, end);//视频文件的名称(预订)
Log.Info(LogTags.DbGet, string.Format("start:{0},end:{1},file:{2}",start,end,file));
if (Downloader.IsFileExist(file))//文件已经存在
{
string url = Downloader.GetHlsUrl(NVSManage.RTMP_Host, file);
info.Url = url;
info.Result = true;
info.Message = "ExistFile";
Log.Info(LogTags.DbGet, string.Format("ExistFile url:{0}", url));
}
else
{
var waitForLogin = true;
NVSManage.GetPlayBackFormEx(info.Ip, (form1,loginState) =>
{
Log.Info(LogTags.DbGet, string.Format("loginState:{0}", loginState));
if (loginState)
{
var channel = info.Channel.ToInt();
var r = form1.Download(channel, start, end);//有文件正在下载的情况
info.Result = r;
if (r == false)
{
Log.Error(LogTags.DbGet, form1.Message);
info.Message = form1.Message;
}
else
{
info.DId = form1.downloader.m_iDLTimeId;//下载用的Id,查询下载进度用
}
}
else
{
info.Result = false;
info.Message = form1.Message;
}
waitForLogin = false;
});
for (int i = 0; i < 300; i++)//等待登录结果
{
if (waitForLogin == false) break;
Application.DoEvents();
Thread.Sleep(50);//等待登录结果
}
}
Log.Info(LogTags.DbGet, string.Format("Return:{0}", info));
return info;
}
catch (Exception e)
{
Log.Error(LogTags.DbGet, "LocationService.StartGetNVSVideo:"+ToString());
return null;
}
}
//private bool waitForLogin;
private bool GetIpAndChannelFormDb(DownloadInfo info)
{
var camera = db.Dev_CameraInfos.Find(info.CId); //摄像机Id
if (camera == null)
{
Log.Error(LogTags.DbGet, "camera == null");
return false;
}
var rtsp = camera.RtspUrl; //"rtsp://admin:1111@192.168.108.107/13"
Log.Info(LogTags.DbGet, "rtsp:" + rtsp);
string[] parts = rtsp.Split('@');
string[] parts2 = parts[1].Split('/');
string ip = parts2[0];
info.Ip = ip;
string channel = parts2[1];
info.Channel = channel;
return true;
}
public DownloadProgress GetNVSProgress(DownloadInfo info)
{
try
{
Log.Info(LogTags.DbGet, "GetNVSProgress:" + info);
if (info == null) return null;
DownloadProgress progress = new DownloadProgress();
progress.DId = info.DId;
PlayBackForm form1 = NVSManage.Form;
var downloader = form1.downloader;
if (downloader == null)
{
progress.Progress = 0;
progress.ProgressText = "downloader == null";
progress.IsFinished = false;
}
else
{
if (downloader.m_iDLTimeId != info.DId)//当前下载的文件不是客户端申请的,多个客户端下载文件的话。
{
progress.Progress = 0;
progress.ProgressText = "downloader.m_iDLTimeId != info.DId";
progress.IsFinished = false;
}
else
{
progress.Progress = downloader.Progress;
progress.ProgressText = downloader.ProgressText;
progress.IsFinished = downloader.IsFinished;
if (progress.IsFinished)
{
progress.Url = downloader.GetUrl(NVSManage.RTMP_Host);
if (!progress.Url.StartsWith("http"))
{
Log.Error(LogTags.DbGet, progress.Url);
}
else
{
Log.Info(LogTags.DbGet, "GetNVSProgress Finished !!!!!!!!:" + progress);
}
}
}
}
Log.Info(LogTags.DbGet, "GetNVSProgress:" + progress);
return progress;
}
catch (Exception e)
{
Log.Error("LocationService.GetNVSProgress", e.ToString());
return null;
}
}
三.SDKDemo修改
1.增加了一个日志窗口
2.录像机能自动登录
3.录像机能切换登录(录像机上有两个Ip,两个ip上的通道映射到所有的摄像头)
登录成功失败都加了回调函数
4.下载部分提取成了一个类
四:客户端实现(WPF)
private DispatcherTimer timer;
LocationServiceClient client;
private DownloadInfo info;
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
info = new DownloadInfo();
info.CId = TbCid.Text.ToInt();
info.StartTime = TbStart.Text;
info.EndTime = TbEnd.Text;
info.Channel = TbChannel.Text;
info.Ip = TbIp.Text;
var r = client.StartGetNVSVideo(info);
if (r != null)
{
WriteLog(r.Result+"|"+r.Message);
if (r.Result)
{
if (string.IsNullOrEmpty(r.Url))
{
WriteLog("启动计时器");
if (timer == null)
{
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(100);
timer.Tick += Timer_Tick;
}
timer.Start();
}
else
{
MessageBox.Show("已经下载过:" + r.Url);
}
}
else
{
MessageBox.Show("下载错误:" + r.Message);
}
}
else
{
MessageBox.Show("结果为空");
}
}
private string _log = "";
private void WriteLog(string log)
{
string txt = string.Format("[{0}]{1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), log);
_log = txt + "\n" + _log;
TbResult.Text = _log;
}
private void Timer_Tick(object sender, EventArgs e)
{
var progress = client.GetNVSProgress(info);
if (progress != null)
{
WriteLog("p:"+progress.Progress);
ProgressBar1.Value = progress.Progress;
if (progress.IsFinished)
{
timer.Stop();
WriteLog("下载完成:" + progress.Url);
MessageBox.Show("下载完成:"+ progress.Url);
}
}
else
{
timer.Stop();
MessageBox.Show("进度为空");
}
}
五:客户端实现(Unity)
基于ump的demo改造的
using System;
using System.Collections;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using UMP;
using UnityEngine;
using UnityEngine.UI;
using WCFServiceForWPF.LocationServices;
public class NVSPlayerClient : MonoBehaviour
{
public InputField TbCid;
public InputField TbIp;
public InputField TbChannel;
public InputField TbStart;
public InputField TbEnd;
public InputField TbURL;
public UniversalMediaPlayer Player;
public Toggle CbIsFinished;
public Slider Slider1;
public Text restul;
public string ip = "127.0.0.1";
public string port = "8733";
private LocationServiceClient client;
public LocationServiceClient CreateServiceClient()
{
string hostName = ip;
string portNum = port;
Binding wsBinding = new BasicHttpBinding();
//Binding wsBinding = new WSHttpBinding();
string url =
string.Format("http://{0}:{1}/LocationService/",
hostName, portNum);
Debug.Log("Create Service Client:" + url);
LocationServiceClient client = null;
try
{
EndpointAddress endpointAddress = new EndpointAddress(url);
Debug.Log("endpointAddress:" + endpointAddress);
client = new LocationServiceClient(wsBinding, endpointAddress);
Debug.Log("client:" + client);
}
catch
{
Debug.LogError("CreateServiceClient报错了!");
return null;
}
return client;
}
private DownloadInfo info;
public void Play()
{
info = new DownloadInfo();
info.CId = int.Parse(TbCid.text);
info.StartTime = TbStart.text;
info.EndTime = TbEnd.text;
info.Channel = TbChannel.text;
info.Ip = TbIp.text;
var r = client.StartGetNVSVideo(info);//开始下载视频
if (r != null)
{
WriteLog(r.Result + "|" + r.Message+"|"+r.Url);
if (r.Result)
{
if (string.IsNullOrEmpty(r.Url))
{
InvokeRepeating("GetProgress", 0, 1);//开始获取下载进度
}
else
{
Play(r.Url);//已经查询并下载成功了的时间段
}
}
else
{
if (r.Message == "It is downloading!")//有文件在下载
{
//todo:等待,休眠1s,并重新尝试下载。重试次数10次,还是这样则提示,并不重试。
}
else
{
//todo:提示Message内容
}
}
}
else
{
WriteLog("结果为空");
}
}
private string _log;
private void WriteLog(string log)
{
string txt = string.Format("[{0}]{1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), log);
_log = txt + "\n" + _log;
restul.text = _log;
Debug.Log(log);
}
public void GetProgress()
{
var progress = client.GetNVSProgress(info);
if (progress != null)
{
//TbResult.Text = progress.ToString();
//ProgressBar1.Value = progress.Progress;
Slider1.value = progress.Progress;
WriteLog(progress.ProgressText);
if (progress.IsFinished)
{
CancelInvoke("GetProgress");
WriteLog("下载完成:"+progress.Url);
Play(progress.Url);
}
}
else
{
CancelInvoke("GetProgress");
WriteLog("进度为空");
}
}
private void Play(string url)
{
TbURL.text = url;
Player.Path = url;
Player.Play();
}
// Use this for initialization
void Start()
{
Debug.Log("Start" );
client = CreateServiceClient();
LoginInfo loginInfo = new LoginInfo();
loginInfo.UserName = "Admin";
loginInfo.Password = "Admin@123456";
Debug.Log("loginInfo:" + loginInfo);
var r = client.Login(loginInfo);
Debug.Log("r:"+ r);
WriteLog(r.Result + "|" + r.Authority+"|"+r.Session);
}
// Update is called once per frame
void Update () {
}
}
六:遗留问题
1.多客户端访问
2.大时间段下载
3.缓存清理
4.提前下载缓存