private void Record() { _stopWrite = false; MainForm.RecordingThreads++; string previewImage = ""; DateTime recordingStart = DateTime.MinValue; if (!String.IsNullOrEmpty(Camobject.recorder.trigger) && TopLevelControl != null) { string[] tid = Camobject.recorder.trigger.Split(','); switch (tid[0]) { case "1": VolumeLevel vl = ((MainForm)TopLevelControl).GetVolumeLevel(Convert.ToInt32(tid[1])); if (vl != null) vl.RecordSwitch(true); break; case "2": CameraWindow cw = ((MainForm)TopLevelControl).GetCameraWindow(Convert.ToInt32(tid[1])); if (cw != null) cw.RecordSwitch(true); break; } } try { if (_writerBuffer != null) _writerBuffer.Clear(); _writerBuffer = new QueueWithEvents<FrameAction>(); _writerBuffer.Changed += WriterBufferChanged; DateTime date = DateTime.Now; string filename = String.Format("{0}-{1}-{2}_{3}-{4}-{5}", date.Year, Helper.ZeroPad(date.Month), Helper.ZeroPad(date.Day), Helper.ZeroPad(date.Hour), Helper.ZeroPad(date.Minute), Helper.ZeroPad(date.Second)); var vc = VolumeControl; if (vc != null && vc.Micobject.settings.active) { vc.ForcedRecording = ForcedRecording; vc.StartSaving(); } VideoFileName = Camobject.id + "_" + filename; string folder = MainForm.Conf.MediaDirectory + "video\\" + Camobject.directory + "\\"; string avifilename = folder + VideoFileName + CodecExtension; bool error = false; double maxAlarm = 0; try { int w, h; GetVideoSize(out w, out h); Program.WriterMutex.WaitOne(); try { Writer = new VideoFileWriter(); if (vc == null || vc.AudioSource==null) Writer.Open(avifilename, w, h, Camobject.recorder.crf, Codec, CalcBitRate(Camobject.recorder.quality), CodecFramerate); else { Writer.Open(avifilename, w, h, Camobject.recorder.crf, Codec, CalcBitRate(Camobject.recorder.quality), CodecAudio, CodecFramerate, vc.AudioSource.RecordingFormat.BitsPerSample * vc.AudioSource.RecordingFormat.SampleRate * vc.AudioSource.RecordingFormat.Channels, vc.AudioSource.RecordingFormat.SampleRate, vc.AudioSource.RecordingFormat.Channels); } } catch { ForcedRecording = false; if (vc != null) { vc.ForcedRecording = false; vc.StopSaving(); } throw; } finally { Program.WriterMutex.ReleaseMutex(); } FrameAction? peakFrame = null; foreach(FrameAction fa in _videoBuffer.OrderBy(p=>p.Timestamp)) { try { using (var ms = new MemoryStream(fa.Frame)) { using (var bmp = (Bitmap)Image.FromStream(ms)) { if (recordingStart == DateTime.MinValue) { recordingStart = fa.Timestamp; } Writer.WriteVideoFrame(ResizeBitmap(bmp), fa.Timestamp - recordingStart); } if (fa.MotionLevel > maxAlarm || peakFrame == null) { maxAlarm = fa.MotionLevel; peakFrame = fa; } _motionData.Append(String.Format(CultureInfo.InvariantCulture, "{0:0.000}", Math.Min(fa.MotionLevel*1000, 100))); _motionData.Append(","); ms.Close(); } } catch (Exception ex) { Log.Error("",ex);//MainForm.LogExceptionToFile(ex); } } _videoBuffer.Clear(); if (vc != null && vc.AudioBuffer != null) { foreach (VolumeLevel.AudioAction aa in vc.AudioBuffer.OrderBy(p=>p.TimeStamp)) { unsafe { fixed (byte* p = aa.Decoded) { if ((aa.TimeStamp - recordingStart).TotalMilliseconds>=0) Writer.WriteAudio(p, aa.Decoded.Length); } } } vc.AudioBuffer.Clear(); } if (recordingStart == DateTime.MinValue) recordingStart = DateTime.Now; while (!_stopWrite) { while (_writerBuffer.Count > 0) { var fa = _writerBuffer.Dequeue(); try { using (var ms = new MemoryStream(fa.Frame)) { var bmp = (Bitmap) Image.FromStream(ms); Writer.WriteVideoFrame(ResizeBitmap(bmp), fa.Timestamp - recordingStart); bmp.Dispose(); bmp = null; if (fa.MotionLevel > maxAlarm || peakFrame == null) { maxAlarm = fa.MotionLevel; peakFrame = fa; } _motionData.Append(String.Format(CultureInfo.InvariantCulture, "{0:0.000}", Math.Min(fa.MotionLevel*1000, 100))); _motionData.Append(","); ms.Close(); } } catch (Exception ex) { Log.Error("",ex);//MainForm.LogExceptionToFile(ex); } if (vc != null && vc.WriterBuffer != null) { try { while (vc.WriterBuffer.Count > 0) { var b = vc.WriterBuffer.Dequeue(); unsafe { fixed (byte* p = b.Decoded) { Writer.WriteAudio(p, b.Decoded.Length); } } } } catch { //can fail if the control is switched off/removed whilst recording } } } _newRecordingFrame.WaitOne(200); } if (!Directory.Exists(folder + @"thumbs\")) Directory.CreateDirectory(folder + @"thumbs\"); if (peakFrame != null) { using (var ms = new MemoryStream(peakFrame.Value.Frame)) { using (var bmp = (Bitmap)Image.FromStream(ms)) { bmp.Save(folder + @"thumbs\" + VideoFileName + "_large.jpg", MainForm.Encoder, MainForm.EncoderParams); Image.GetThumbnailImageAbort myCallback = ThumbnailCallback; using (var myThumbnail = bmp.GetThumbnailImage(96, 72, myCallback, IntPtr.Zero)) { myThumbnail.Save(folder + @"thumbs\" + VideoFileName + ".jpg", MainForm.Encoder, MainForm.EncoderParams); } } previewImage = folder + @"thumbs\" + VideoFileName + ".jpg"; ms.Close(); } } } catch (Exception ex) { error = true; Log.Error("Camera " + Camobject.id, ex); } finally { _stopWrite = false; if (Writer != null) { Program.WriterMutex.WaitOne(); try { Writer.Close(); Writer.Dispose(); } catch (Exception ex) { Log.Error("",ex);//MainForm.LogExceptionToFile(ex); } finally { Program.WriterMutex.ReleaseMutex(); } Writer = null; } try { _writerBuffer.Clear(); } catch { } _writerBuffer = null; _recordingTime = 0; if (vc != null && vc.Micobject.settings.active) VolumeControl.StopSaving(); } if (error) { try { FileOperations.Delete(filename + CodecExtension); } catch { } MainForm.RecordingThreads--; return; } string path = MainForm.Conf.MediaDirectory + "video\\" + Camobject.directory + "\\" + VideoFileName; bool yt = Camobject.settings.youtube.autoupload && MainForm.Conf.Subscribed; string[] fnpath = (path + CodecExtension).Split('\\'); string fn = fnpath[fnpath.Length - 1]; var fpath = MainForm.Conf.MediaDirectory + "video\\" + Camobject.directory + "\\thumbs\\"; var fi = new FileInfo(path + CodecExtension); var dSeconds = Convert.ToInt32((DateTime.Now - recordingStart).TotalSeconds); FilesFile ff = FileList.FirstOrDefault(p => p.Filename.EndsWith(fn)); bool newfile = false; if (ff == null) { ff = new FilesFile(); newfile = true; } ff.CreatedDateTicks = DateTime.Now.Ticks; ff.Filename = fn; ff.MaxAlarm = Math.Min(maxAlarm * 1000, 100); ff.SizeBytes = fi.Length; ff.DurationSeconds = dSeconds; ff.IsTimelapse = false; ff.AlertData = Helper.GetMotionDataPoints(_motionData); _motionData.Clear(); ff.TriggerLevel = (100-Camobject.detector.minsensitivity); //adjusted if (newfile) { FileList.Insert(0, ff); if (!MainForm.MasterFileList.Any(p => p.Filename.EndsWith(fn))) { MainForm.MasterFileList.Add(new FilePreview(fn, dSeconds, Camobject.name, DateTime.Now.Ticks, 2, Camobject.id, ff.MaxAlarm)); if (TopLevelControl != null) { string thumb = fpath + fn.Replace(CodecExtension, ".jpg"); ((MainForm)TopLevelControl).AddPreviewControl(thumb, path + CodecExtension, dSeconds, DateTime.Now, true); } } if (yt) { if (CodecExtension!=".mp4") Log.Info("Skipped youtube upload (only upload mp4 files)."); else { try { YouTubeUploader.AddUpload(Camobject.id, fn, Camobject.settings.youtube.@public, "", ""); } catch (Exception ex) { Log.Error("",ex);//MainForm.LogExceptionToFile(ex); } } } } } catch (Exception ex) { Log.Error("",ex);//MainForm.LogExceptionToFile(ex); } MainForm.RecordingThreads--; Camobject.newrecordingcount++; if (!String.IsNullOrEmpty(Camobject.recorder.trigger) && TopLevelControl != null) { string[] tid = Camobject.recorder.trigger.Split(','); switch (tid[0]) { case "1": VolumeLevel vl = ((MainForm)TopLevelControl).GetVolumeLevel(Convert.ToInt32(tid[1])); if (vl != null) vl.RecordSwitch(false); break; case "2": CameraWindow cw = ((MainForm)TopLevelControl).GetCameraWindow(Convert.ToInt32(tid[1])); if (cw != null) cw.RecordSwitch(false); break; } } if (Notification != null) Notification(this, new NotificationType("NewRecording", Camobject.name, previewImage)); }