Skip to content
This repository has been archived by the owner on Feb 22, 2024. It is now read-only.

Commit

Permalink
#184 - Allow splitter component to process still images.
Browse files Browse the repository at this point in the history
  • Loading branch information
techyian committed Oct 15, 2020
1 parent 3f02ee7 commit 127f984
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 65 deletions.
8 changes: 4 additions & 4 deletions src/MMALSharp/Components/MMALSplitterComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
namespace MMALSharp.Components
{
/// <summary>
/// The Splitter Component is intended on being connected to the camera video output port. In turn, it
/// provides an additional 4 output ports which can be used to produce multiple image/video outputs
/// from the single camera video port.
/// The Splitter Component can be connected to either the camera's still or video port and in turn, it
/// provides an additional 4 output ports which can be used to produce multiple image/video outputs.
/// </summary>
public class MMALSplitterComponent : MMALDownstreamHandlerComponent
{
/// <summary>
/// Creates a new instance of <see cref="MMALSplitterComponent"/>.
/// Creates a new instance of <see cref="MMALSplitterComponent"/>. Defaults to video port behaviour; configure the output port
/// using <see cref="SplitterStillPort"/> as a generic constraint if this splitter is intended to be connected to the camera's still port.
/// </summary>
public unsafe MMALSplitterComponent()
: base(MMALParameters.MMAL_COMPONENT_DEFAULT_VIDEO_SPLITTER)
Expand Down
57 changes: 57 additions & 0 deletions src/MMALSharp/Ports/Outputs/SplitterOutputPort.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// <copyright file="SplitterOutputPort.cs" company="Techyian">
// Copyright (c) Ian Auty and contributors. All rights reserved.
// Licensed under the MIT License. Please see LICENSE.txt for License info.
// </copyright>

using System;
using MMALSharp.Common.Utility;
using MMALSharp.Components;

namespace MMALSharp.Ports.Outputs
{
/// <summary>
/// Represents a splitter component output port.
/// </summary>
public unsafe abstract class SplitterOutputPort : OutputPort
{
private Resolution _resolution;

/// <inheritdoc />
public override Resolution Resolution
{
get
{
return _resolution;
}

internal set
{
// The splitter component doesn't support resolution changes. This override has been
// added in case the user wants to use the splitter component with the Image Processing
// library and will use this property to inform what resolution it's running at. Applied
// via port config.
_resolution = new Resolution(value.Width, value.Height);
}
}

/// <summary>
/// Creates a new instance of <see cref="SplitterOutputPort"/>.
/// </summary>
/// <param name="ptr">The native pointer.</param>
/// <param name="comp">The component this port is associated with.</param>
/// <param name="guid">Managed unique identifier for this port.</param>
protected SplitterOutputPort(IntPtr ptr, IComponent comp, Guid guid)
: base(ptr, comp, guid)
{
}

/// <summary>
/// Creates a new instance of <see cref="SplitterOutputPort"/>.
/// </summary>
/// <param name="copyFrom">The port to copy data from.</param>
protected SplitterOutputPort(IPort copyFrom)
: base((IntPtr)copyFrom.Ptr, copyFrom.ComponentReference, copyFrom.Guid)
{
}
}
}
49 changes: 49 additions & 0 deletions src/MMALSharp/Ports/Outputs/SplitterStillPort.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// <copyright file="SplitterStillPort.cs" company="Techyian">
// Copyright (c) Ian Auty and contributors. All rights reserved.
// Licensed under the MIT License. Please see LICENSE.txt for License info.
// </copyright>

using System;
using Microsoft.Extensions.Logging;
using MMALSharp.Common.Utility;
using MMALSharp.Components;
using MMALSharp.Native;

namespace MMALSharp.Ports.Outputs
{
/// <summary>
/// Represents a splitter component still output port.
/// </summary>
public unsafe class SplitterStillPort : SplitterOutputPort
{
/// <summary>
/// Creates a new instance of <see cref="SplitterStillPort"/>.
/// </summary>
/// <param name="ptr">The native pointer.</param>
/// <param name="comp">The component this port is associated with.</param>
/// <param name="guid">Managed unique identifier for this port.</param>
public SplitterStillPort(IntPtr ptr, IComponent comp, Guid guid)
: base(ptr, comp, guid)
{
}

/// <summary>
/// Creates a new instance of <see cref="SplitterStillPort"/>.
/// </summary>
/// <param name="copyFrom">The port to copy data from.</param>
public SplitterStillPort(IPort copyFrom)
: base((IntPtr)copyFrom.Ptr, copyFrom.ComponentReference, copyFrom.Guid)
{
}

internal override void NativeOutputPortCallback(MMAL_PORT_T* port, MMAL_BUFFER_HEADER_T* buffer)
{
if (MMALCameraConfig.Debug)
{
MMALLog.Logger.LogDebug($"{this.Name}: In native {nameof(SplitterStillPort)} output callback");
}

base.NativeOutputPortCallback(port, buffer);
}
}
}
66 changes: 5 additions & 61 deletions src/MMALSharp/Ports/Outputs/SplitterVideoPort.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// <copyright file="ISplitterVideoPort.cs" company="Techyian">
// <copyright file="SplitterVideoPort.cs" company="Techyian">
// Copyright (c) Ian Auty and contributors. All rights reserved.
// Licensed under the MIT License. Please see LICENSE.txt for License info.
// </copyright>

using System;
using Microsoft.Extensions.Logging;
using MMALSharp.Callbacks;
using MMALSharp.Common;
using MMALSharp.Common.Utility;
using MMALSharp.Components;
using MMALSharp.Handlers;
Expand All @@ -16,9 +15,9 @@
namespace MMALSharp.Ports.Outputs
{
/// <summary>
/// Represents a splitter component output port.
/// Represents a splitter component video output port.
/// </summary>
public unsafe class SplitterVideoPort : VideoPort
public unsafe class SplitterVideoPort : SplitterOutputPort, IVideoPort
{
/// <summary>
/// Creates a new instance of <see cref="SplitterVideoPort"/>.
Expand All @@ -43,64 +42,9 @@ public SplitterVideoPort(IPort copyFrom)
/// <inheritdoc />
public override void Configure(IMMALPortConfig config, IInputPort copyFrom, IOutputCaptureHandler handler)
{
// The splitter component should not have its resolution set on the output port so override method accordingly.
if (config != null)
{
this.PortConfig = config;

copyFrom?.ShallowCopy(this);

if (config.EncodingType != null)
{
this.NativeEncodingType = config.EncodingType.EncodingVal;
}

if (config.PixelFormat != null)
{
this.NativeEncodingSubformat = config.PixelFormat.EncodingVal;
}

MMAL_VIDEO_FORMAT_T tempVid = this.Ptr->Format->Es->Video;

try
{
this.Commit();
}
catch
{
// If commit fails using new settings, attempt to reset using old temp MMAL_VIDEO_FORMAT_T.
MMALLog.Logger.LogWarning($"{this.Name}: Commit of output port failed. Attempting to reset values.");
this.Ptr->Format->Es->Video = tempVid;
this.Commit();
}
base.Configure(config, copyFrom, handler);

if (config.ZeroCopy)
{
this.ZeroCopy = true;
this.SetParameter(MMALParametersCommon.MMAL_PARAMETER_ZERO_COPY, true);
}

if (MMALCameraConfig.VideoColorSpace != null &&
MMALCameraConfig.VideoColorSpace.EncType == MMALEncoding.EncodingType.ColorSpace)
{
this.VideoColorSpace = MMALCameraConfig.VideoColorSpace;
}

if (config.Bitrate > 0)
{
this.Bitrate = config.Bitrate;
}

this.EncodingType = config.EncodingType;
this.PixelFormat = config.PixelFormat;

this.BufferNum = Math.Max(this.BufferNumMin, config.BufferNum > 0 ? config.BufferNum : this.BufferNumRecommended);
this.BufferSize = Math.Max(this.BufferSizeMin, config.BufferSize > 0 ? config.BufferSize : this.BufferSizeRecommended);

this.Commit();
}

this.CallbackHandler = new VideoOutputCallbackHandler(this, (IVideoCaptureHandler)handler, null);
this.CallbackHandler = new VideoOutputCallbackHandler(this, (IVideoCaptureHandler)handler, config.Split);
}

internal override void NativeOutputPortCallback(MMAL_PORT_T* port, MMAL_BUFFER_HEADER_T* buffer)
Expand Down
80 changes: 80 additions & 0 deletions tests/MMALSharp.Tests/ImageEncoderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using MMALSharp.Processors;
using MMALSharp.Tests.Data;
using Xunit;
using MMALSharp.Ports.Outputs;

namespace MMALSharp.Tests
{
Expand Down Expand Up @@ -603,5 +604,84 @@ public async Task TakePictureWithCustomConnectionCallbackHandler()
Fixture.CheckAndAssertFilepath(imgCaptureHandler.GetFilepath());
}
}

[Fact]
public async Task TakeMultiplePicturesFromSplitterComponent()
{
// This test relies on an ISP component being connected between a splitter component output port
// and an image encoder input port. If the ISP component is not used as a go-between, the splitter
// component appears to only accept 1 of its output ports being connected to an image encoder. I believe
// this may be a firmware restriction.
TestHelper.BeginTest("TakeMultiplePicturesFromSplitterComponent");
TestHelper.SetConfigurationDefaults();

using (var imgCaptureHandler = new ImageStreamCaptureHandler("/home/pi/images/tests", "jpg"))
using (var imgCaptureHandler2 = new ImageStreamCaptureHandler("/home/pi/images/tests", "jpg"))
using (var imgCaptureHandler3 = new ImageStreamCaptureHandler("/home/pi/images/tests", "jpg"))
using (var imgCaptureHandler4 = new ImageStreamCaptureHandler("/home/pi/images/tests", "jpg"))
using (var imgEncoder = new MMALImageEncoder())
using (var imgEncoder2 = new MMALImageEncoder())
using (var imgEncoder3 = new MMALImageEncoder())
using (var imgEncoder4 = new MMALImageEncoder())
using (var splitter = new MMALSplitterComponent())
using (var isp1 = new MMALIspComponent())
using (var isp2 = new MMALIspComponent())
using (var isp3 = new MMALIspComponent())
using (var isp4 = new MMALIspComponent())
using (var nullSink = new MMALNullSinkComponent())
{
Fixture.MMALCamera.ConfigureCameraSettings();

var portConfig = new MMALPortConfig(MMALEncoding.JPEG, MMALEncoding.I420, quality: 90);
var portConfig2 = new MMALPortConfig(MMALEncoding.JPEG, MMALEncoding.I420, quality: 90);
var portConfig3 = new MMALPortConfig(MMALEncoding.JPEG, MMALEncoding.I420, quality: 90);
var portConfig4 = new MMALPortConfig(MMALEncoding.JPEG, MMALEncoding.I420, quality: 90);

var splitterConfig = new MMALPortConfig(MMALEncoding.I420, MMALEncoding.I420);

var resizeConfig = new MMALPortConfig(MMALEncoding.I420, MMALEncoding.I420, width: 1280, height: 720);
var resizeConfig2 = new MMALPortConfig(MMALEncoding.I420, MMALEncoding.I420, width: 1024, height: 720);
var resizeConfig3 = new MMALPortConfig(MMALEncoding.I420, MMALEncoding.I420, width: 640, height: 480);
var resizeConfig4 = new MMALPortConfig(MMALEncoding.I420, MMALEncoding.I420, width: 620, height: 310);

// Create our component pipeline.
splitter.ConfigureOutputPort<SplitterStillPort>(0, splitterConfig, null);
splitter.ConfigureOutputPort<SplitterStillPort>(1, splitterConfig, null);
splitter.ConfigureOutputPort<SplitterStillPort>(2, splitterConfig, null);
splitter.ConfigureOutputPort<SplitterStillPort>(3, splitterConfig, null);

isp1.ConfigureOutputPort(resizeConfig, null);
isp2.ConfigureOutputPort(resizeConfig2, null);
isp3.ConfigureOutputPort(resizeConfig3, null);
isp4.ConfigureOutputPort(resizeConfig4, null);

imgEncoder.ConfigureOutputPort(portConfig, imgCaptureHandler);
imgEncoder2.ConfigureOutputPort(portConfig2, imgCaptureHandler2);
imgEncoder3.ConfigureOutputPort(portConfig3, imgCaptureHandler3);
imgEncoder4.ConfigureOutputPort(portConfig4, imgCaptureHandler4);

Fixture.MMALCamera.Camera.StillPort.ConnectTo(splitter);
Fixture.MMALCamera.Camera.PreviewPort.ConnectTo(nullSink);

splitter.Outputs[0].ConnectTo(isp1);
splitter.Outputs[1].ConnectTo(isp2);
splitter.Outputs[2].ConnectTo(isp3);
splitter.Outputs[3].ConnectTo(isp4);

isp1.Outputs[0].ConnectTo(imgEncoder);
isp2.Outputs[0].ConnectTo(imgEncoder2);
isp3.Outputs[0].ConnectTo(imgEncoder3);
isp4.Outputs[0].ConnectTo(imgEncoder4);

// Camera warm up time
await Task.Delay(2000);
await Fixture.MMALCamera.ProcessAsync(Fixture.MMALCamera.Camera.StillPort);

Fixture.CheckAndAssertFilepath(imgCaptureHandler.GetFilepath());
Fixture.CheckAndAssertFilepath(imgCaptureHandler2.GetFilepath());
Fixture.CheckAndAssertFilepath(imgCaptureHandler3.GetFilepath());
Fixture.CheckAndAssertFilepath(imgCaptureHandler4.GetFilepath());
}
}
}
}
37 changes: 37 additions & 0 deletions tests/MMALSharp.Tests/RawCaptureTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,42 @@ public async Task RecordVideoDirectlyFromResizerWithSplitterComponent()
Fixture.CheckAndAssertFilepath(vidCaptureHandler.GetFilepath());
}
}

[Fact]
public async Task TakePicturesDirectlyFromSplitterComponent()
{
TestHelper.BeginTest("TakePicturesDirectlyFromSplitterComponent");
TestHelper.SetConfigurationDefaults();

using (var imgCaptureHandler = new ImageStreamCaptureHandler("/home/pi/images/tests", "raw"))
using (var imgCaptureHandler2 = new ImageStreamCaptureHandler("/home/pi/images/tests", "raw"))
using (var imgCaptureHandler3 = new ImageStreamCaptureHandler("/home/pi/images/tests", "raw"))
using (var imgCaptureHandler4 = new ImageStreamCaptureHandler("/home/pi/images/tests", "raw"))
using (var splitter = new MMALSplitterComponent())
using (var nullSink = new MMALNullSinkComponent())
{
Fixture.MMALCamera.ConfigureCameraSettings();

var splitterConfig = new MMALPortConfig(MMALEncoding.I420, MMALEncoding.I420);

// Create our component pipeline.
splitter.ConfigureOutputPort<SplitterStillPort>(0, splitterConfig, imgCaptureHandler);
splitter.ConfigureOutputPort<SplitterStillPort>(1, splitterConfig, imgCaptureHandler2);
splitter.ConfigureOutputPort<SplitterStillPort>(2, splitterConfig, imgCaptureHandler3);
splitter.ConfigureOutputPort<SplitterStillPort>(3, splitterConfig, imgCaptureHandler4);

Fixture.MMALCamera.Camera.StillPort.ConnectTo(splitter);
Fixture.MMALCamera.Camera.PreviewPort.ConnectTo(nullSink);

// Camera warm up time
await Task.Delay(2000);
await Fixture.MMALCamera.ProcessAsync(Fixture.MMALCamera.Camera.StillPort);

Fixture.CheckAndAssertFilepath(imgCaptureHandler.GetFilepath());
Fixture.CheckAndAssertFilepath(imgCaptureHandler2.GetFilepath());
Fixture.CheckAndAssertFilepath(imgCaptureHandler3.GetFilepath());
Fixture.CheckAndAssertFilepath(imgCaptureHandler4.GetFilepath());
}
}
}
}

0 comments on commit 127f984

Please sign in to comment.