public override void handleGETRequest(HttpProcessor p) { try { string requestedPage = Uri.UnescapeDataString(p.request_url.AbsolutePath.TrimStart('/')); if (requestedPage == "admin") { p.writeRedirect("admin/main"); return; } if (requestedPage == "login") { LogOutUser(p, null); return; } Session s = sm.GetSession(p.requestCookies.GetValue("cps"), p.requestCookies.GetValue("auth"), p.GetParam("rawauth")); if (s.sid != null && s.sid.Length == 16) { p.responseCookies.Add("cps", s.sid, TimeSpan.FromMinutes(s.sessionLengthMinutes)); } if (requestedPage == "logout") { LogOutUser(p, s); return; } if (requestedPage.StartsWith("admin/")) { string adminPage = requestedPage == "admin" ? "" : requestedPage.Substring("admin/".Length); if (string.IsNullOrWhiteSpace(adminPage)) { adminPage = "main"; } int idxQueryStringStart = adminPage.IndexOf('?'); if (idxQueryStringStart == -1) { idxQueryStringStart = adminPage.Length; } adminPage = adminPage.Substring(0, idxQueryStringStart); Pages.Admin.AdminPage.HandleRequest(adminPage, p, s); return; } else if (requestedPage.StartsWith("image/")) { requestedPage = requestedPage.Substring("image/".Length); #region image/ if (requestedPage.EndsWith(".jpg") || requestedPage.EndsWith(".jpeg") || requestedPage.EndsWith(".png") || requestedPage.EndsWith(".webp")) { int extensionLength = requestedPage[requestedPage.Length - 4] == '.' ? 4 : 5; string format = requestedPage.Substring(requestedPage.Length - (extensionLength - 1)); string cameraId = requestedPage.Substring(0, requestedPage.Length - extensionLength); cameraId = cameraId.ToLower(); int minPermission = cm.GetCameraMinPermission(cameraId); if (minPermission == 101) { p.writeFailure(); return; } if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission)) { LogOutUser(p, s); return; } int wait = p.GetIntParam("wait", 5000); IPCameraBase cam = cm.GetCamera(cameraId); byte[] latestImage = cm.GetLatestImage(cameraId, wait); int patience = p.GetIntParam("patience"); if (patience > 0) { if (patience > 5000) { patience = 5000; } int timeLeft = patience; System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); timer.Start(); while (s.DuplicateImageSendCheck(cameraId, latestImage) && cam != null && timeLeft > 0) { // The latest image was already sent to the user in a previous image request. // Wait for up to 5 seconds as desired by the user to get a "new" image. cam.newFrameWaitHandle.WaitOne(Math.Max(50, timeLeft)); // This EventWaitHandle nonsense isn't perfect, so this should prevent excessively long delays in the event of a timing error. latestImage = cm.GetLatestImage(cameraId); timeLeft = patience - (int)timer.ElapsedMilliseconds; } } if (latestImage.Length == 0) { p.writeFailure("502 Bad Gateway"); return; } ImageFormat imgFormat = ImageFormat.Jpeg; latestImage = ImageConverter.HandleRequestedConversionIfAny(latestImage, p, ref imgFormat, format); p.tcpClient.SendBufferSize = latestImage.Length + 256; p.writeSuccess(Util.GetMime(imgFormat), latestImage.Length); p.outputStream.Flush(); p.rawOutputStream.Write(latestImage, 0, latestImage.Length); } else if (requestedPage.EndsWith(".mjpg")) { string cameraId = requestedPage.Substring(0, requestedPage.Length - 5); cameraId = cameraId.ToLower(); int minPermission = cm.GetCameraMinPermission(cameraId); if (minPermission == 101) { p.writeFailure(); return; } if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission)) { LogOutUser(p, s); return; } if (cm.GetLatestImage(cameraId).Length == 0) { return; } // Increasing the send buffer size here does not help streaming fluidity. p.writeSuccess("multipart/x-mixed-replace;boundary=ipcamera"); byte[] newImage; byte[] lastImage = null; while (!this.stopRequested) { try { newImage = cm.GetLatestImage(cameraId); while (newImage == lastImage) { Thread.Sleep(1); newImage = cm.GetLatestImage(cameraId); if (this.stopRequested) { return; } } lastImage = newImage; ImageFormat imgFormat = ImageFormat.Jpeg; byte[] sendImage = ImageConverter.HandleRequestedConversionIfAny(newImage, p, ref imgFormat); p.outputStream.WriteLine("--ipcamera"); p.outputStream.WriteLine("Content-Type: " + Util.GetMime(imgFormat)); p.outputStream.WriteLine("Content-Length: " + sendImage.Length); p.outputStream.WriteLine(); p.outputStream.Flush(); p.rawOutputStream.Write(sendImage, 0, sendImage.Length); p.rawOutputStream.Flush(); p.outputStream.WriteLine(); } catch (Exception ex) { if (!p.isOrdinaryDisconnectException(ex)) { Logger.Debug(ex); } break; } } } else if (requestedPage.EndsWith(".ogg")) { string cameraId = requestedPage.Substring(0, requestedPage.Length - 4); cameraId = cameraId.ToLower(); int minPermission = cm.GetCameraMinPermission(cameraId); if (minPermission == 101) { p.writeFailure(); return; } if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission)) { LogOutUser(p, s); return; } IPCameraBase _cam = cm.GetCamera(cameraId); if (_cam is Html5VideoCamera) { Html5VideoCamera cam = (Html5VideoCamera)_cam; ConcurrentQueue <byte[]> myDataListener = new ConcurrentQueue <byte[]>(); try { cam.RegisterStreamListener(myDataListener); p.writeSuccess("application/octet-stream"); p.outputStream.Flush(); byte[] outputBuffer; int chunkCount = 0; while (!this.stopRequested) { try { chunkCount = myDataListener.Count; if (chunkCount > 100) { return; // This connection is falling too far behind. End it. } else if (chunkCount > 0) { Console.Write(chunkCount + " "); if (myDataListener.TryDequeue(out outputBuffer)) { p.rawOutputStream.Write(outputBuffer, 0, outputBuffer.Length); p.rawOutputStream.Flush(); } } else { Thread.Sleep(1); } } catch (Exception ex) { if (!p.isOrdinaryDisconnectException(ex)) { Logger.Debug(ex); } break; } } } finally { cam.UnregisterStreamListener(myDataListener); } } else { p.writeFailure("501 Not Implemented"); } } else if (requestedPage.EndsWith(".cam")) { string cameraId = requestedPage.Substring(0, requestedPage.Length - 4); cameraId = cameraId.ToLower(); int minPermission = cm.GetCameraMinPermission(cameraId); if (minPermission == 101) { p.writeFailure(); return; } if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission)) { LogOutUser(p, s); return; } IPCameraBase cam = cm.GetCamera(cameraId); if (cam != null && cam.cameraSpec.ptzType == MJpegCameraProxy.Configuration.PtzType.Dahua || cam.cameraSpec.ptzType == MJpegCameraProxy.Configuration.PtzType.Hikvision) { p.writeRedirect("../Camera.html?cam=" + cameraId); return; } string userAgent = p.GetHeaderValue("User-Agent", ""); bool isMobile = userAgent.Contains("iPad") || userAgent.Contains("iPhone") || userAgent.Contains("Android") || userAgent.Contains("BlackBerry"); bool isLanConnection = p == null ? false : p.IsLanConnection; int defaultRefresh = isLanConnection && !isMobile ? -1 : 250; string html = CamPage.GetHtml(cameraId, !isMobile, p.GetIntParam("refresh", defaultRefresh), p.GetBoolParam("override") ? -1 : 600000, p); if (string.IsNullOrEmpty(html) || html == "NO") { p.writeFailure(); return; } p.writeSuccess("text/html"); p.outputStream.Write(html); } else if (requestedPage == "PTZPRESETIMG") { string cameraId = p.GetParam("id"); cameraId = cameraId.ToLower(); IPCameraBase cam = cm.GetCamera(cameraId); if (cam != null) { int index = p.GetIntParam("index", -1); if (index > -1) { if (cam.cameraSpec.ptz_proxy) { string auth = (!string.IsNullOrEmpty(cam.cameraSpec.ptz_username) && !string.IsNullOrEmpty(cam.cameraSpec.ptz_password)) ? "rawauth=" + HttpUtility.UrlEncode(cam.cameraSpec.ptz_username) + ":" + HttpUtility.UrlEncode(cam.cameraSpec.ptz_password) + "&" : ""; byte[] data = SimpleProxy.GetData("http://" + cam.cameraSpec.ptz_hostName + "/PTZPRESETIMG?" + auth + "id=" + HttpUtility.UrlEncode(cam.cameraSpec.ptz_proxy_cameraId) + "&index=" + index); if (data.Length > 0) { p.writeSuccess("image/jpg", data.Length); p.outputStream.Flush(); p.rawOutputStream.Write(data, 0, data.Length); return; } } else { string fileName = Globals.ThumbsDirectoryBase + cameraId + index + ".jpg"; int minPermission = cm.GetCameraMinPermission(cameraId); if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission) || minPermission == 101) { } else { if (File.Exists(fileName)) { byte[] bytes = File.ReadAllBytes(fileName); p.writeSuccess("image/jpg", bytes.Length); p.outputStream.Flush(); p.rawOutputStream.Write(bytes, 0, bytes.Length); return; } } } } } { // Failed to get image thumbnail byte[] bytes = File.ReadAllBytes(Globals.WWWPublicDirectoryBase + "Images/qmark.png"); p.writeSuccess("image/png", bytes.Length); p.outputStream.Flush(); p.rawOutputStream.Write(bytes, 0, bytes.Length); return; } } else if (requestedPage.EndsWith(".wanscamstream")) { string cameraId = requestedPage.Substring(0, requestedPage.Length - ".wanscamstream".Length); IPCameraBase cam = cm.GetCamera(cameraId); if (cam == null) { return; } if (!cam.cameraSpec.wanscamCompatibilityMode) { return; } if (p.RemoteIPAddress != "127.0.0.1") { return; } Uri url = new Uri(cam.cameraSpec.imageryUrl); string host = url.Host; int port = url.Port; string path = url.PathAndQuery; //string path = "/livestream.cgi?user=admin&pwd=nooilwell&streamid=0&audio=0&filename="; //string path = "/videostream.cgi?user=admin&pwd=nooilwell&resolution=8"; int total = 0; try { //Console.WriteLine("opening"); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Connect(host, port); byte[] buffer = new byte[4096]; socket.Send(UTF8Encoding.UTF8.GetBytes("GET " + path + " HTTP/1.1\r\nHost: " + host + ":" + port + "\r\nConnection: close\r\n\r\n")); //Console.WriteLine("open"); int read = socket.Receive(buffer); p.writeSuccess("video/raw"); p.outputStream.Flush(); while (read > 0 && socket.Connected && p.tcpClient.Connected) { p.rawOutputStream.Write(buffer, 0, read); total += read; //Console.WriteLine(read); read = socket.Receive(buffer); } //Console.WriteLine("close"); } catch (Exception ex) { if (!p.isOrdinaryDisconnectException(ex)) { Logger.Debug(ex); } } } #endregion } else if (requestedPage.StartsWith("control/")) { requestedPage = requestedPage.Substring("control/".Length); #region control/ if (requestedPage == "keepalive") { string cameraId = p.GetParam("id"); cameraId = cameraId.ToLower(); int minPermission = cm.GetCameraMinPermission(cameraId); if (minPermission == 101) { p.writeFailure(); return; } if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission)) { p.writeFailure("403 Forbidden"); return; } cm.GetRTSPUrl(cameraId, p); p.writeSuccess("text/plain"); p.outputStream.Write("1"); } else if (requestedPage == "PTZ") { string cameraId = p.GetParam("id"); cameraId = cameraId.ToLower(); int minPermission = cm.GetCameraMinPermission(cameraId); if (minPermission == 101) { p.writeFailure(); return; } if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission)) { LogOutUser(p, s); return; } PTZ.RunCommand(cameraId, p.GetParam("cmd")); p.writeSuccess("text/plain"); } #endregion } else { #region www int permissionRequired; if (!Util.TryGetValue(requestedPage.ToLower(), MJpegWrapper.cfg.GetWwwFilesList(), out permissionRequired)) { permissionRequired = -1; } string wwwDirectory = permissionRequired == -1 ? Globals.WWWPublicDirectoryBase : Globals.WWWDirectoryBase; if (permissionRequired < 0) { permissionRequired = 0; } else if (permissionRequired > 100) { permissionRequired = 100; } if (permissionRequired > s.permission) { LogOutUser(p, s); return; } DirectoryInfo WWWDirectory = new DirectoryInfo(wwwDirectory); string wwwDirectoryBase = WWWDirectory.FullName.Replace('\\', '/').TrimEnd('/') + '/'; FileInfo fi = new FileInfo(wwwDirectoryBase + requestedPage); string targetFilePath = fi.FullName.Replace('\\', '/'); if (!targetFilePath.StartsWith(wwwDirectoryBase) || targetFilePath.Contains("../")) { p.writeFailure("400 Bad Request"); return; } if (!fi.Exists) { p.writeFailure(); return; } // && (fi.Extension == ".html" || fi.Extension == ".htm") if (fi.Name.ToLower() == "camera.html" && fi.Length < 256000) { p.writeSuccess(Mime.GetMimeType(fi.Extension)); string html = File.ReadAllText(fi.FullName); CamPage2 cp = new CamPage2(html, p); html = cp.Html; html = html.Replace("%ALLCAMS%", string.Join(",", MJpegServer.cm.GenerateAllCameraIdList())); html = html.Replace("%ALLCAMS_IDS_NAMES_JS_ARRAY%", MJpegServer.cm.GenerateAllCameraIdNameList(s == null ? 0 : s.permission)); try { html = html.Replace("%REMOTEIP%", p.RemoteIPAddress); } catch (Exception ex) { Logger.Debug(ex); } p.outputStream.Write(html); p.outputStream.Flush(); } else if ((fi.Extension == ".html" || fi.Extension == ".htm") && fi.Length < 256000) { p.writeSuccess(Mime.GetMimeType(fi.Extension)); string html = File.ReadAllText(fi.FullName); html = html.Replace("%ALLCAMS%", string.Join(",", MJpegServer.cm.GenerateAllCameraIdList())); html = html.Replace("%ALLCAMS_IDS_NAMES_JS_ARRAY%", MJpegServer.cm.GenerateAllCameraIdNameList(s == null ? 0 : s.permission)); try { html = html.Replace("%REMOTEIP%", p.RemoteIPAddress); } catch (Exception ex) { Logger.Debug(ex); } p.outputStream.Write(html); p.outputStream.Flush(); } else { List <KeyValuePair <string, string> > additionalHeaders = new List <KeyValuePair <string, string> >(); additionalHeaders.Add(new KeyValuePair <string, string>("Cache-Control", "max-age=3600, public")); p.writeSuccess(Mime.GetMimeType(fi.Extension), additionalHeaders: additionalHeaders); p.outputStream.Flush(); using (FileStream fs = fi.OpenRead()) { fs.CopyTo(p.rawOutputStream); } p.rawOutputStream.Flush(); } #endregion } } catch (Exception ex) { if (!p.isOrdinaryDisconnectException(ex)) { Logger.Debug(ex); } } }
public static void RunCommand(IPCameraBase ipCam, string cam, string command) { try { int preset_number = 0; if (ipCam == null) { return; } if (!string.IsNullOrEmpty(cam)) { cam = cam.ToLower(); } string cmdEnd = ""; int duration = 500; CommandType commandType = CommandType.decoder_control; #region Commands if (command.StartsWith("q")) { commandType = CommandType.camera_control; if (command == "q1") { command = "param=0&value=2"; } else if (command == "q1.5") { command = "param=0&value=4"; } else if (command == "q2") { command = "param=0&value=8"; } else if (command == "q2.5") { command = "param=0&value=16"; } else if (command == "q3") { command = "param=0&value=32"; } else { return; } } else if (command == "2") // UP { cmdEnd = "1"; duration = 200; } else if (command == "0") // DOWN { cmdEnd = "3"; duration = 200; } else if (command == "6") // LEFT { cmdEnd = "5"; duration = 500; } else if (command == "4") // RIGHT { cmdEnd = "7"; duration = 500; } else if (command == "18") // ZOOM OUT { commandType = CommandType.camera_control; command = "param=18&value=1"; cmdEnd = "param=18&value=0"; duration = 1500; } else if (command == "17") // ZOOM IN { commandType = CommandType.camera_control; command = "param=17&value=1"; cmdEnd = "param=17&value=0"; duration = 1500; } else if (command == "116") // ZOOM OUT FINE { commandType = CommandType.camera_control; command = "param=18&value=1"; cmdEnd = "param=18&value=0"; duration = 500; } else if (command == "118") // ZOOM IN FINE { commandType = CommandType.camera_control; command = "param=17&value=1"; cmdEnd = "param=17&value=0"; duration = 500; } else if (command == "1116") // ZOOM OUT FINER { commandType = CommandType.camera_control; command = "param=18&value=1"; cmdEnd = "param=18&value=0"; duration = 100; } else if (command == "1118") // ZOOM IN FINER { commandType = CommandType.camera_control; command = "param=17&value=1"; cmdEnd = "param=17&value=0"; duration = 100; } else if (command == "30" || command == "31" || // Preset 1 - Mark / Recall command == "32" || command == "33" || // 2 command == "34" || command == "35" || // 3 command == "36" || command == "37" || // 4 command == "38" || command == "39" || // 5 command == "40" || command == "41" || // 6 command == "42" || command == "43" || // 7 command == "44" || command == "45" // 8 ) { int cmdNum = int.Parse(command); if (cmdNum % 2 == 0) { preset_number = (cmdNum - 28) / 2; } } else { return; // Invalid Command } #endregion string auth = "&loginuse=" + ipCam.cameraSpec.ptz_username + "&loginpas="******"http://" + ipCam.cameraSpec.ptz_hostName + "/decoder_control.cgi?command=" + command + auth); if (cmdEnd != "") { Thread.Sleep(duration); SimpleProxy.GetData("http://" + ipCam.cameraSpec.ptz_hostName + "/decoder_control.cgi?command=" + cmdEnd + auth); } } else if (commandType == CommandType.camera_control) { SimpleProxy.GetData("http://" + ipCam.cameraSpec.ptz_hostName + "/camera_control.cgi?" + command + auth); if (cmdEnd != "") { Thread.Sleep(duration); SimpleProxy.GetData("http://" + ipCam.cameraSpec.ptz_hostName + "/camera_control.cgi?" + cmdEnd + auth); } } if (preset_number > 0) { try { byte[] image = MJpegServer.cm.GetLatestImage(cam); if (image.Length > 0) { Util.WriteImageThumbnailToFile(image, CameraProxyGlobals.ThumbsDirectoryBase + cam.ToLower() + preset_number + ".jpg"); } } catch (Exception ex) { Logger.Debug(ex); } } } catch (Exception ex) { Logger.Debug(ex); } }
public static void RunCommand(string cameraId, string cmd) { IPCameraBase cam = MJpegServer.cm.GetCamera(cameraId); if (cam == null) { return; } if (cam.cameraSpec.ptz_proxy) { string auth = (!string.IsNullOrEmpty(cam.cameraSpec.ptz_username) && !string.IsNullOrEmpty(cam.cameraSpec.ptz_password)) ? "rawauth=" + HttpUtility.UrlEncode(cam.cameraSpec.ptz_username) + ":" + HttpUtility.UrlEncode(cam.cameraSpec.ptz_password) + "&" : ""; SimpleProxy.GetData("http://" + cam.cameraSpec.ptz_hostName + "/control/PTZ?" + auth + "id=" + HttpUtility.UrlEncode(cam.cameraSpec.ptz_proxy_cameraId) + "&cmd=" + HttpUtility.UrlEncode(cmd)); } else if (cam.cameraSpec.ptzType == PtzType.LoftekCheap) { LoftekCheapPTZ.RunCommand(cam, cameraId, cmd); } //else if (cam.cameraSpec.ptzType == PtzType.Dahua) // DahuaPTZ.RunCommand(cam, cmd); else if (cam.cameraSpec.ptzType == PtzType.WanscamCheap) { WanscamCheapPTZ.RunCommand(cam, cameraId, cmd); } //else if (cam.cameraSpec.ptzType == PtzType.TrendnetIP672) // IP672SeriesPTZ.RunCommand(cam, cameraId, cmd); else if (cam.cameraSpec.ptzType == PtzType.IPS_EYE01) { PanTiltZoom.PTZDirection dir = PanTiltZoom.PTZDirection.Up; if (cmd.StartsWith("d")) { dir = PanTiltZoom.PTZDirection.Down; } else if (cmd.StartsWith("l")) { dir = PanTiltZoom.PTZDirection.Left; } else if (cmd.StartsWith("r")) { dir = PanTiltZoom.PTZDirection.Right; } new PanTiltZoom.IPS_EYE01.IPS_EYE01_PTZ(cam.cameraSpec).MoveSimple(dir); } else if (cam.cameraSpec.ptzType == PtzType.TrendnetTVIP400) { PanTiltZoom.PTZDirection dir = PanTiltZoom.PTZDirection.Up; if (cmd.StartsWith("d")) { dir = PanTiltZoom.PTZDirection.Down; } else if (cmd.StartsWith("l")) { dir = PanTiltZoom.PTZDirection.Left; } else if (cmd.StartsWith("r")) { dir = PanTiltZoom.PTZDirection.Right; } new PanTiltZoom.TrendNet.TV_IP400(cam.cameraSpec).MoveSimple(dir); } else if (cam.cameraSpec.ptzType == PtzType.CustomPTZProfile || cam.cameraSpec.ptzType == PtzType.Huisun) { object ptzProfile = null; if (cam.cameraSpec.ptzType == PtzType.CustomPTZProfile) { ptzProfile = new PanTiltZoom.Custom.CustomPTZProfile(cam.cameraSpec, cam); } else if (cam.cameraSpec.ptzType == PtzType.Huisun) { ptzProfile = new PanTiltZoom.HuisunBullet(cam.cameraSpec, cam); } int presetNum; if (cmd == "u") { ((IPTZSimple)ptzProfile).MoveSimple(PanTiltZoom.PTZDirection.Up); } else if (cmd == "d") { ((IPTZSimple)ptzProfile).MoveSimple(PanTiltZoom.PTZDirection.Down); } else if (cmd == "l") { ((IPTZSimple)ptzProfile).MoveSimple(PanTiltZoom.PTZDirection.Left); } else if (cmd == "r") { ((IPTZSimple)ptzProfile).MoveSimple(PanTiltZoom.PTZDirection.Right); } else if (cmd == "ul") { ((IPTZSimple)ptzProfile).MoveSimple(PanTiltZoom.PTZDirection.UpLeft); } else if (cmd == "ur") { ((IPTZSimple)ptzProfile).MoveSimple(PanTiltZoom.PTZDirection.UpRight); } else if (cmd == "dl") { ((IPTZSimple)ptzProfile).MoveSimple(PanTiltZoom.PTZDirection.DownLeft); } else if (cmd == "dr") { ((IPTZSimple)ptzProfile).MoveSimple(PanTiltZoom.PTZDirection.DownRight); } else if (cmd == "i3") { ((IPTZSimple)ptzProfile).Zoom(PanTiltZoom.ZoomDirection.In, PanTiltZoom.ZoomAmount.Long); } else if (cmd == "o3") { ((IPTZSimple)ptzProfile).Zoom(PanTiltZoom.ZoomDirection.Out, PanTiltZoom.ZoomAmount.Long); } else if (cmd == "i2") { ((IPTZSimple)ptzProfile).Zoom(PanTiltZoom.ZoomDirection.In, PanTiltZoom.ZoomAmount.Medium); } else if (cmd == "o2") { ((IPTZSimple)ptzProfile).Zoom(PanTiltZoom.ZoomDirection.Out, PanTiltZoom.ZoomAmount.Medium); } else if (cmd == "i1") { ((IPTZSimple)ptzProfile).Zoom(PanTiltZoom.ZoomDirection.In, PanTiltZoom.ZoomAmount.Short); } else if (cmd == "o1") { ((IPTZSimple)ptzProfile).Zoom(PanTiltZoom.ZoomDirection.Out, PanTiltZoom.ZoomAmount.Short); } else if (cmd.Length == 3 && cmd.StartsWith("pl") && int.TryParse(cmd.Substring(2), out presetNum)) { ((IPTZPresets)ptzProfile).LoadPreset(presetNum); } else if (cmd.Length == 3 && cmd.StartsWith("ps") && int.TryParse(cmd.Substring(2), out presetNum)) { ((IPTZPresets)ptzProfile).SavePreset(presetNum); } } }