首页 > 解决方案 > 使用 OpenCvSharp 从网络摄像头录制视频 - 使文件播放速度加快

问题描述

我正在尝试使用 OpenCvSharp 从网络摄像头录制视频

我已经可以使用下面的代码录制视频,但是生成的 .mp4 文件播放速度很快(例如,我录制了 5 秒,结果甚至不到一秒)。

我已经玩过延迟AddCameraFrameToRecordingThread但无济于事

可能是什么问题?或者我可以使用哪些其他库来从网络摄像头录制视频?

namespace BlackBears.Recording
{
    using System;
    using System.Drawing;
    using System.Threading;

    using OpenCvSharp;
    using OpenCvSharp.Extensions;

    using Size = OpenCvSharp.Size;

    public class Recorder : IDisposable
    {
        private readonly VideoCaptureAPIs _videoCaptureApi = VideoCaptureAPIs.DSHOW;
        private readonly ManualResetEventSlim _writerReset = new(false);
        private readonly VideoCapture _videoCapture;
        private VideoWriter _videoWriter;
        private Thread _writerThread;

        private bool IsVideoCaptureValid => _videoCapture is not null && _videoCapture.IsOpened();

        public Recorder(int deviceIndex, int frameWidth, int frameHeight, double fps)
        {
            _videoCapture = VideoCapture.FromCamera(deviceIndex, _videoCaptureApi);
            _videoCapture.Open(deviceIndex, _videoCaptureApi);

            _videoCapture.FrameWidth = frameWidth;
            _videoCapture.FrameHeight = frameHeight;
            _videoCapture.Fps = fps;
        }

        /// <inheritdoc />
        public void Dispose()
        {
            GC.SuppressFinalize(this);
            Dispose(true);
        }

        ~Recorder()
        {
            Dispose(false);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                StopRecording();

                _videoCapture?.Release();
                _videoCapture?.Dispose();
            }
        }

        public void StartRecording(string path)
        {
            if (_writerThread is not null)
                return;

            if (!IsVideoCaptureValid)
                ThrowHelper.ThrowVideoCaptureNotReadyException();

            _videoWriter = new VideoWriter(path, FourCC.XVID, _videoCapture.Fps, new Size(_videoCapture.FrameWidth, _videoCapture.FrameHeight));

            _writerReset.Reset();
            _writerThread = new Thread(AddCameraFrameToRecordingThread);
            _writerThread.Start();
        }

        public void StopRecording()
        {
            if (_writerThread is not null)
            {
                _writerReset.Set();
                _writerThread.Join();
                _writerThread = null;
                _writerReset.Reset();
            }

            _videoWriter?.Release();
            _videoWriter?.Dispose();
            _videoWriter = null;
        }

        private void AddCameraFrameToRecordingThread()
        {
            var waitTimeBetweenFrames = (int)(1_000 / _videoCapture.Fps);
            using var frame = new Mat();
            while (!_writerReset.Wait(waitTimeBetweenFrames))
            {
                if (!_videoCapture.Read(frame))
                    return;
                _videoWriter.Write(frame);
            }
        }
    }
}

标签: c#.netopencv

解决方案


在玩了很多之后,我自己找到了解决方案。

捕获帧并写入它们的单个线程是不够的。我现在创建了两个线程,一个从相机捕获帧,一个写入它们。为了在生成的文件中获得正确的时序,必须考虑写入创建的延迟。

我最终得到了以下两个在不同线程中运行的函数:

private void CaptureFrameLoop()
{
    while (!_threadStopEvent.Wait(0))
    {
        _videoCapture.Read(_capturedFrame);
    }
}

private void AddCameraFrameToRecordingThread()
{
    var waitTimeBetweenFrames = 1_000 / _videoCapture.Fps;
    var lastWrite = DateTime.Now;

    while (!_threadStopEvent.Wait(0))
    {
        if (DateTime.Now.Subtract(lastWrite).TotalMilliseconds < waitTimeBetweenFrames)
            continue;
        lastWrite = DateTime.Now;
        _videoWriter.Write(_capturedFrame);
    }
}

推荐阅读