techyian / MMALSharp

C# wrapper to Broadcom's MMAL with an API to the Raspberry Pi camera.
MIT License
195 stars 33 forks source link

see MMALSharp issue #163 #164

Closed MV10 closed 3 years ago

MV10 commented 3 years ago

Fixes #163

Adds OnDemandImageCaptureHandler and IStreamWriter to the Processing project.

Modifies FastImageOutputCallbackHandler in the main project to recognize an IStreamWriter handler.

MV10 commented 3 years ago

Modifications to the wiki motion detection example to capture three images at the start of recording, one per second. Motion detection comments removed so that on-demand comments will stand out:

public async Task DetectMotionWithImages()
{
    MMALCamera cam = MMALCamera.Instance;
    MMALCameraConfig.InlineHeaders = true;

    // Stores the most current full frame; will write it on demand in response to a call to NewFile()
    using (var imgCaptureHandler = new OnDemandImageCaptureHandler(ramdiskPath, "jpg"))

    // Setting continuousCapture puts the handler into fast image processing mode
    using (var imgEncoder = new MMALImageEncoder(continuousCapture: true))

    using (var vidCaptureHandler = new CircularBufferCaptureHandler(4000000, "/home/pi/videos/detections", "h264"))
    using (var motionCircularBufferCaptureHandler = new CircularBufferCaptureHandler(4000000))
    using (var splitter = new MMALSplitterComponent())
    using (var resizer = new MMALIspComponent())
    using (var vidEncoder = new MMALVideoEncoder())
    using (var renderer = new MMALVideoRenderer())
    {
        cam.ConfigureCameraSettings();

        splitter.ConfigureInputPort(new MMALPortConfig(MMALEncoding.OPAQUE, MMALEncoding.I420), cam.Camera.VideoPort, null);
        resizer.ConfigureOutputPort<VideoPort>(0, new MMALPortConfig(MMALEncoding.RGB24, MMALEncoding.RGB24, width: 640, height: 480), motionCaptureHandler);
        vidEncoder.ConfigureOutputPort(new MMALPortConfig(MMALEncoding.H264, MMALEncoding.I420, 0, MMALVideoEncoder.MaxBitrateLevel4, null), vidCaptureHandler);

        // Note the image quality argument for still image capture
        imgEncoder.ConfigureOutputPort(new MMALPortConfig(MMALEncoding.JPEG, MMALEncoding.I420, quality: 90), imgCaptureHandler);

        cam.Camera.VideoPort.ConnectTo(splitter);
        cam.Camera.PreviewPort.ConnectTo(renderer);

        splitter.Outputs[0].ConnectTo(resizer);
        splitter.Outputs[1].ConnectTo(vidEncoder);
        splitter.Outputs[2].ConnectTo(imgEncoder); // Still-image input/output connection

        await Task.Delay(2000);

        var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60));

        var motionConfig = new MotionConfig(threshold: 130, testFrameInterval: TimeSpan.FromSeconds(3));

        await cam.WithMotionDetection(motionCircularBufferCaptureHandler, motionConfig,
        async () =>
        {
            // This callback will be invoked when motion has been detected.

            motionCircularBufferCaptureHandler.DisableMotionDetection();

            var ctsStopRecording = new CancellationTokenSource();
            ctsStopRecording.Token.Register(() =>
            {
                motionCircularBufferCaptureHandler.EnableMotionDetection();
                vidCaptureHandler.StopRecording();
                vidCaptureHandler.Split();
            });

            // These will time out and record a still image one and two seconds after the recording starts
            var ctsImageOneSecond = new CancellationTokenSource();
            var ctsImageTwoSecond = new CancellationTokenSource();

            // Save image on demand by calling NewFile() when the token timeout expires
            ctsImageOneSecond.Token.Register(imgCaptureHandler.NewFile)
            ctsImageTwoSecond.Token.Register(imgCaptureHandler.NewFile)

            // Record an image right now, then set the still-image timers, and the overall recording timer
            imgCaptureHandler.NewFile();
            ctsImageOneSecond.CancelAfter(1000);
            ctsImageTwoSecond.CancelAfter(2000);
            ctsStopRecording.CancelAfter(10 * 1000);

            await Task.WhenAny(
                vidCaptureHandler.StartRecording(vidEncoder.RequestIFrame, ctsStopRecording.Token),
                cts.Token.AsTask()
            );

            if (!recordingCTS.IsCancellationRequested) 
            {
                ctsImageOneSecond.Cancel();
                ctsImageTwoSecond.Cancel();
                recordingCTS.Cancel();
            }
        })
        .ProcessAsync(cam.Camera.VideoPort, cts.Token);
    }

    cam.Cleanup();
}
MV10 commented 3 years ago

Closing in favor of modifications to existing handlers.