0

I am accessing the Microsoft Azure Kinect depth camera's video footage and displaying the video on a pictureBox using a C# WinForm application. I am looking for a way to record this video now. Is there a way to record the video playing on my entire pictureBox during the application run time?

I am using .NET framework 4.7.2

Ken White
  • 123,280
  • 14
  • 225
  • 444
Mad
  • 435
  • 2
  • 17
  • Does this answer your question? [How to create video file from Images sequence file?](https://stackoverflow.com/questions/36744334/how-to-create-video-file-from-images-sequence-file) – Andy May 05 '21 at 00:56
  • I went through that question and it seems AForge libs that are suggested in that question are compatible with a .NET framework not exceed version 3.5. I am not sure I could use that to solve this. – Mad May 05 '21 at 01:36
  • 2
    Why would you need to record it from the picturebox instead of directly from the source? – Ken White May 05 '21 at 01:47
  • Is there a VideoFileWriter that supports .NET v.4.7.2 like in this question https://stackoverflow.com/questions/36744334/how-to-create-video-file-from-images-sequence-file – Mad May 05 '21 at 02:35
  • Funny how some requests for external libraries are closed as off topic and other requests that essentially amount to the same thing get answered – Caius Jard May 05 '21 at 04:41
  • Last time I had this problem I handed the entire thing off to ffmpeg to do a screen record - one line of code to process.start it – Caius Jard May 05 '21 at 04:42
  • @CaiusJard could you share how you solved it? – Mad May 13 '21 at 09:29

1 Answers1

0

I wrote this class to wrap around ffmpeg (follow the "how to record your screen" help in the ffmpeg docs) and make some bits easier:

using System;
using System.Diagnostics;
using System.Drawing;
using System.Text;

namespace CJCam
{
    public class FfmpegRecorder : Recorder
    {
        public string OutputPath { get; set; }

        private Process _ffmpeg = null;
        private readonly StringBuilder _ffLog = new StringBuilder();

        //like STDERR: frame=113987 fps= 10 q=-1.0 Lsize=  204000kB time=03:09:58.50 bitrate= 146.6kbits/s    
        private string _streamStats = "";

        ~FfmpegRecorder()
        {
            Dispose();
        }

        public override void Dispose()
        {
        }

        public FfmpegRecorder(string outputPath, Rectangle recordingRect)
        {
            ScreenCaptureRecorderRegistry.SetRecordRegion(recordingRect);

            OutputPath = outputPath;
        }

        public override void StartRecording()
        {
            ProcessStartInfo psi = new ProcessStartInfo
            {
                FileName = Properties.Settings.Default.CommandLineFfmpegPath,
                Arguments = string.Format(
                    Properties.Settings.Default.CommandLineFfmpegArgs,
                    OutputPath
                ),
                WindowStyle = ProcessWindowStyle.Hidden,
                CreateNoWindow = true,
                UseShellExecute = false,
                RedirectStandardInput = true,
                RedirectStandardOutput = true,
                RedirectStandardError = true
            };

            _ffmpeg = System.Diagnostics.Process.Start(psi);
            _ffmpeg.OutputDataReceived += Ffmpeg_OutputDataReceived;
            _ffmpeg.ErrorDataReceived += Ffmpeg_ErrorDataReceived;
            _ffmpeg.BeginOutputReadLine();
            _ffmpeg.BeginErrorReadLine();

            _ffmpeg.PriorityClass = ProcessPriorityClass.High;
        }

        void Ffmpeg_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null && IsInteresting(e.Data))
            {
                _ffLog.Append("STDOUT: ").AppendLine(e.Data);
            }
        }
        void Ffmpeg_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null && IsInteresting(e.Data))
            {
                _ffLog.Append("STDERR: ").AppendLine(e.Data);
            }
        }

        bool IsInteresting(string data)
        {
            if (data.StartsWith("frame="))
            {
                _streamStats = data;
                return false;
            }

            return true;
        }

        public override void PauseRecording()
        {
            throw new NotImplementedException("Cannot pause FFmpeg at this time");
        }

        public override void StopRecording()
        {
            if (_ffmpeg == null)
                return;

            if (_ffmpeg.HasExited)
                return;

            _ffmpeg.StandardInput.WriteLine("q");

            _ffmpeg.WaitForExit(5000);
        }

        public override string GetLogFile()
        {
            return _ffLog.AppendLine().Append("CURRENT FRAME:").AppendLine(_streamStats).ToString();
        }
    }
}

It gets some help from this class:

    class ScreenCaptureRecorderRegistry
    {
        public static void SetRecordRegion(Rectangle region)
        {
            RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\screen-capture-recorder");

            // If the return value is null, the key doesn't exist
            if (key == null)
                key = Registry.CurrentUser.CreateSubKey("Software\\screen-capture-recorder");

            key = Registry.CurrentUser.OpenSubKey("Software\\screen-capture-recorder", true);

            key.SetValue("start_x", region.X, RegistryValueKind.DWord);
            key.SetValue("start_y", region.Y, RegistryValueKind.DWord);
            key.SetValue("capture_width", region.Width, RegistryValueKind.DWord);
            key.SetValue("capture_height", region.Height, RegistryValueKind.DWord);
        }
    }

Then you just put an ffmpeg binary at some place and put the path in settings (CommandLineFfmpegPath) and some suitable args (settings name CommandLineFfmpegArgs) for what you want to record

My args are -rtbufsize 2048M -thread_queue_size 512 -f dshow -i video="screen-capture-recorder" -thread_queue_size 512 -f dshow -i audio="Line 1 (Virtual Audio Cable)" -x264opts keyint=50 -map 0:v -map 1:a -pix_fmt yuv420p -y "{0}" - you'll only have a virtual audio cable if you install it, but you can get ffmpeg to list the sound devices on your system and put one of those instead, or even omit it if you dont want sound.

Screenshot of settings editor:

enter image description here

Then you make an instance of FfmpegRecorder with a rectangle to record - that would be the coordinates of your picturebox, translated to screen coords (watch out for DPI/you have to adjust your values if your Windows doesn't run at 100% "zoom")

If you want to make your life easier/get to the "one line record" I mentioned, just make sure your picturebox is in the same place all the time (maximize the form), set the reg settings one time using regedit, and fire off a Process.Start to launch ffmpeg with some args. Most of the other fluff in this answer is because I wanted to capture FF's logs, or interact with it, or adjust the recording region to different places all the time

Caius Jard
  • 72,509
  • 5
  • 49
  • 80