diff --git a/src/MMALSharp/Components/MMALCameraComponent.cs b/src/MMALSharp/Components/MMALCameraComponent.cs index a1c31d6d..9ef41327 100644 --- a/src/MMALSharp/Components/MMALCameraComponent.cs +++ b/src/MMALSharp/Components/MMALCameraComponent.cs @@ -136,7 +136,7 @@ internal void Initialise() this.InitialiseStill(); } - private void InitialisePreview() + internal void InitialisePreview() { var vFormat = new MMAL_VIDEO_FORMAT_T( MMALUtil.VCOS_ALIGN_UP(MMALCameraConfig.VideoResolution.Width, 32), @@ -157,7 +157,7 @@ private void InitialisePreview() this.PreviewPort.Commit(); } - private void InitialiseVideo() + internal void InitialiseVideo() { if (MMALCameraConfig.VideoResolution.Width == 0 || MMALCameraConfig.VideoResolution.Width > this.CameraInfo.MaxWidth) { @@ -194,7 +194,7 @@ private void InitialiseVideo() this.VideoPort.BufferSizeMin); } - private void InitialiseStill() + internal void InitialiseStill() { //If user hasn't specified Width/Height, or one which is too high, use highest resolution supported by sensor. if (MMALCameraConfig.StillResolution == null) @@ -218,13 +218,12 @@ private void InitialiseStill() MMALCameraConfig.StillEncoding == MMALEncoding.MMAL_ENCODING_RGB24 || MMALCameraConfig.StillEncoding == MMALEncoding.MMAL_ENCODING_RGB16) { - Helpers.PrintWarning("Encoding set to RGB. Increasing width padding to multiple of 16."); + Helpers.PrintWarning("Encoding set to RGB. Setting width padding to multiple of 16."); vFormat = new MMAL_VIDEO_FORMAT_T( MMALUtil.VCOS_ALIGN_UP(MMALCameraConfig.StillResolution.Width, 16), MMALUtil.VCOS_ALIGN_UP(MMALCameraConfig.StillResolution.Height, 16), - new MMAL_RECT_T(0, 0, MMALCameraConfig.StillResolution.Width, - MMALCameraConfig.StillResolution.Height), + new MMAL_RECT_T(0, 0, MMALCameraConfig.StillResolution.Width, MMALCameraConfig.StillResolution.Height), new MMAL_RATIONAL_T(0, 1), this.StillPort.Ptr->Format->es->video.Par, this.StillPort.Ptr->Format->es->video.ColorSpace @@ -242,14 +241,14 @@ private void InitialiseStill() this.StillPort.Ptr->Format->es->video.ColorSpace ); } - + this.StillPort.Ptr->Format->es->video = vFormat; this.StillPort.Ptr->Format->encoding = MMALCameraConfig.StillEncoding.EncodingVal; this.StillPort.Ptr->Format->encodingVariant = MMALCameraConfig.StillSubFormat.EncodingVal; if (MMALCameraConfig.Debug) Console.WriteLine("Commit still"); - + this.StillPort.Commit(); this.StillPort.Ptr->BufferNum = Math.Max(this.StillPort.BufferNumRecommended, diff --git a/src/MMALSharp/Components/MMALComponentBase.cs b/src/MMALSharp/Components/MMALComponentBase.cs index 70c2c393..71ee788a 100644 --- a/src/MMALSharp/Components/MMALComponentBase.cs +++ b/src/MMALSharp/Components/MMALComponentBase.cs @@ -220,6 +220,9 @@ internal void CleanPortPools() if (MMALCameraConfig.Debug) Console.WriteLine("Destroying port pool"); + if (port.Enabled) + port.DisablePort(); + port.DestroyPortPool(); port.BufferPool = null; } @@ -232,6 +235,9 @@ internal void CleanPortPools() if (MMALCameraConfig.Debug) Console.WriteLine("Destroying port pool"); + if (port.Enabled) + port.DisablePort(); + port.DestroyPortPool(); port.BufferPool = null; } diff --git a/src/MMALSharp/MMALCamera.cs b/src/MMALSharp/MMALCamera.cs index 839f02e4..3dfb7495 100644 --- a/src/MMALSharp/MMALCamera.cs +++ b/src/MMALSharp/MMALCamera.cs @@ -112,7 +112,8 @@ public async Task TakeVideo(MMALPortImpl connPort, DateTime? timeout = null, Spl try { - Console.WriteLine($"Preparing to take video. Resolution: {MMALCameraConfig.VideoResolution.Width} x {MMALCameraConfig.VideoResolution.Height}. Encoder: {encoder.EncodingType.EncodingName}. Pixel Format: {encoder.PixelFormat.EncodingName}."); + Console.WriteLine($"Preparing to take video. Resolution: {MMALCameraConfig.VideoResolution.Width} x {MMALCameraConfig.VideoResolution.Height}. " + + $"Encoder: {encoder.EncodingType.EncodingName}. Pixel Format: {encoder.PixelFormat.EncodingName}."); ((MMALVideoPort)encoder.Outputs.ElementAt(0)).Timeout = timeout; ((MMALVideoEncoder)encoder).Split = split; @@ -138,19 +139,16 @@ public async Task TakeRawPicture(ICaptureHandler handler) { throw new PiCameraError("A connection was found to the Camera still port. No encoder should be connected to the Camera's still port for raw capture."); } - if (handler == null) - { - throw new PiCameraError("No handler specified"); - } - this.Camera.Handler = handler; + this.Camera.Handler = handler ?? throw new PiCameraError("No handler specified"); this.CheckPreviewComponentStatus(); //Enable the image encoder output port. try { - Console.WriteLine($"Preparing to take picture - Resolution: {MMALCameraConfig.StillResolution.Width} x {MMALCameraConfig.StillResolution.Height}"); + Console.WriteLine($"Preparing to take raw picture - Resolution: {MMALCameraConfig.StillResolution.Width} x {MMALCameraConfig.StillResolution.Height}. " + + $"Encoder: {MMALCameraConfig.StillEncoding.EncodingName}. Pixel Format: {MMALCameraConfig.StillSubFormat.EncodingName}."); await BeginProcessing(this.Camera, null, this.Camera.StillPort, MMALCameraComponent.MMALCameraStillPort); } @@ -209,7 +207,8 @@ public async Task TakePicture(MMALPortImpl connPort, bool rawBayer = false, bool //Enable the image encoder output port. try { - Console.WriteLine($"Preparing to take picture. Resolution: {MMALCameraConfig.StillResolution.Width} x {MMALCameraConfig.StillResolution.Height}. Encoder: {encoder.EncodingType.EncodingName}. Pixel Format: {encoder.PixelFormat.EncodingName}."); + Console.WriteLine($"Preparing to take picture. Resolution: {MMALCameraConfig.StillResolution.Width} x {MMALCameraConfig.StillResolution.Height}. " + + $"Encoder: {encoder.EncodingType.EncodingName}. Pixel Format: {encoder.PixelFormat.EncodingName}."); await BeginProcessing(encoder, encoder.Connection, this.Camera.StillPort, 0); } @@ -228,8 +227,13 @@ public async Task TakePicture(MMALPortImpl connPort, bool rawBayer = false, bool /// Specify whether to include EXIF tags in the capture /// Custom EXIF tags to use in the capture /// The awaitable Task - public async Task TakePictureTimeout(MMALPortImpl connPort, DateTime timeout, bool rawBayer = false, bool useExif = true, params ExifTag[] exifTags) - { + public async Task TakePictureTimeout(MMALPortImpl connPort, DateTime timeout, bool rawBayer = false, bool useExif = true, bool burstMode = false, params ExifTag[] exifTags) + { + if(burstMode) + { + this.Camera.StillPort.SetParameter(MMALParametersCamera.MMAL_PARAMETER_CAMERA_BURST_CAPTURE, true); + } + while (DateTime.Now.CompareTo(timeout) < 0) { await TakePicture(connPort, rawBayer, useExif, exifTags); @@ -305,19 +309,16 @@ private async Task BeginProcessing(MMALComponentBase component, MMALConnectionIm component.CleanPortPools(); } + + /// /// Helper method to create a new preview component /// /// The renderer type - /// The static Camera instance + /// The camera instance public MMALCamera CreatePreviewComponent(MMALRendererBase renderer) { - if (this.Preview != null) - { - this.Preview?.Connection.Disable(); - this.Preview?.Connection.Destroy(); - this.Preview.Dispose(); - } + this.DestroyPreviewComponent(); this.Preview = renderer; this.Preview.CreateConnection(this.Camera.PreviewPort); @@ -327,7 +328,7 @@ public MMALCamera CreatePreviewComponent(MMALRendererBase renderer) /// /// Helper method to create a splitter component /// - /// The static Camera instance + /// The camera instance public MMALCamera CreateSplitterComponent() { this.Splitter = new MMALSplitterComponent(); @@ -339,7 +340,7 @@ public MMALCamera CreateSplitterComponent() /// /// The encoder component to attach to the output port /// The output port to attach to - /// The static Camera instance + /// The camera instance public MMALCamera AddEncoder(MMALEncoderBase encoder, MMALPortImpl outputPort) { if (MMALCameraConfig.Debug) @@ -359,7 +360,7 @@ public MMALCamera AddEncoder(MMALEncoderBase encoder, MMALPortImpl outputPort) /// Remove an encoder component from an output port /// /// The output port we are removing an encoder component from - /// The static Camera instance + /// The camera instance public MMALCamera RemoveEncoder(MMALPortImpl outputPort) { var enc = this.Encoders.Where(c => c.Connection != null && c.Connection.OutputPort == outputPort).FirstOrDefault(); @@ -399,26 +400,76 @@ public void EnableCamera() } /// - /// Configures the camera component. This method applies configuration settings and initialises the components required - /// for capturing images. + /// Reconfigures the Camera's still port. /// - /// The static Camera instance - public MMALCamera ConfigureCamera() + /// The camera instance + public MMALCamera ConfigureStill() { - if (MMALCameraConfig.Debug) - { - Console.WriteLine("Configuring camera parameters."); - } - this.DisableCamera(); - this.Camera.SetCameraParameters(); + this.Encoders.Where(c => c.Connection != null && c.Connection.OutputPort == this.Camera.StillPort).ToList().ForEach(c => c.Connection.Disable()); + + this.Camera.InitialiseStill(); + + this.Encoders.Where(c => c.Connection != null && c.Connection.OutputPort == this.Camera.StillPort).ToList().ForEach(c => c.Connection.Enable()); this.EnableCamera(); - + + return this; + } + + /// + /// Reconfigures the Camera's video port. + /// + /// The camera instance + public MMALCamera ConfigureVideo() + { + this.DisableCamera(); + + this.Encoders.Where(c => c.Connection != null && c.Connection.OutputPort == this.Camera.VideoPort).ToList().ForEach(c => c.Connection.Disable()); + + this.Camera.InitialiseVideo(); + + this.Encoders.Where(c => c.Connection != null && c.Connection.OutputPort == this.Camera.VideoPort).ToList().ForEach(c => c.Connection.Enable()); + + this.EnableCamera(); + + return this; + } + + /// + /// Reconfigures the Camera's preview port. + /// + /// The camera instance + public MMALCamera ConfigurePreview() + { + this.DisableCamera(); + + this.Preview?.Connection?.Disable(); + this.Camera.InitialisePreview(); + this.Preview?.Connection?.Enable(); + + this.EnableCamera(); + return this; } - + + + private void DestroyPreviewComponent() + { + if (this.Preview != null) + { + this.Preview?.Connection.Disable(); + this.Preview?.Connection.Destroy(); + this.Preview.Dispose(); + } + } + + private void DestroyEncoders() + { + this.Encoders.ForEach(c => c.Dispose()); + } + /// /// Helper method to check the Renderer component status. If a Renderer has not been initialized, a warning will /// be shown to the user. If a Renderer has been created but a connection has not been initialized, this will be diff --git a/src/MMALSharp/MMALCameraConfig.cs b/src/MMALSharp/MMALCameraConfig.cs index a0b9c3b3..c801fb6c 100644 --- a/src/MMALSharp/MMALCameraConfig.cs +++ b/src/MMALSharp/MMALCameraConfig.cs @@ -184,7 +184,7 @@ public static class MMALCameraConfig /// public static void Reload() { - MMALCamera.Instance.ConfigureCamera(); + MMALCamera.Instance.Camera.SetCameraParameters(); } } diff --git a/src/MMALSharp/MMALPortBase.cs b/src/MMALSharp/MMALPortBase.cs index c2d1ba29..d9ae6e0d 100644 --- a/src/MMALSharp/MMALPortBase.cs +++ b/src/MMALSharp/MMALPortBase.cs @@ -218,6 +218,9 @@ internal void DestroyPortPool() { if (this.BufferPool != null) { + if (this.Enabled) + this.DisablePort(); + MMALUtil.mmal_port_pool_destroy(this.Ptr, this.BufferPool.Ptr); } } diff --git a/src/MMALSharp/Native/MMALFormat.cs b/src/MMALSharp/Native/MMALFormat.cs index 1f3ff8a9..cac3d1a9 100644 --- a/src/MMALSharp/Native/MMALFormat.cs +++ b/src/MMALSharp/Native/MMALFormat.cs @@ -112,11 +112,15 @@ public MMAL_SUBPICTURE_FORMAT_T(uint xOffset, uint yOffset) } } - [StructLayout(LayoutKind.Sequential)] + //Union type. + [StructLayout(LayoutKind.Explicit)] public struct MMAL_ES_SPECIFIC_FORMAT_T { + [FieldOffset(0)] public MMAL_AUDIO_FORMAT_T audio; + [FieldOffset(0)] public MMAL_VIDEO_FORMAT_T video; + [FieldOffset(0)] public MMAL_SUBPICTURE_FORMAT_T subpicture; public MMAL_ES_SPECIFIC_FORMAT_T(MMAL_AUDIO_FORMAT_T audio, MMAL_VIDEO_FORMAT_T video, MMAL_SUBPICTURE_FORMAT_T subpicture) diff --git a/tests/MMALSharp.Tests/ImageEncoderTests.cs b/tests/MMALSharp.Tests/ImageEncoderTests.cs index 44efd8a2..a59f7ab1 100644 --- a/tests/MMALSharp.Tests/ImageEncoderTests.cs +++ b/tests/MMALSharp.Tests/ImageEncoderTests.cs @@ -348,9 +348,8 @@ public void TakePicture(string extension, MMALEncoding encodingType, MMALEncodin //Create our component pipeline. fixture.MMALCamera .AddEncoder(imgEncoder, fixture.MMALCamera.Camera.StillPort) - .CreatePreviewComponent(new MMALNullSinkComponent()) - .ConfigureCamera(); - + .CreatePreviewComponent(new MMALNullSinkComponent()); + await fixture.MMALCamera.TakePicture(fixture.MMALCamera.Camera.StillPort); } @@ -381,8 +380,7 @@ public void TakePictureRawBayer(string extension, MMALEncoding encodingType, MMA //Create our component pipeline. fixture.MMALCamera .AddEncoder(imgEncoder, fixture.MMALCamera.Camera.StillPort) - .CreatePreviewComponent(new MMALNullSinkComponent()) - .ConfigureCamera(); + .CreatePreviewComponent(new MMALNullSinkComponent()); await fixture.MMALCamera.TakePicture(fixture.MMALCamera.Camera.StillPort, true); } @@ -414,8 +412,7 @@ public void TakePictureTimelapse(string extension, MMALEncoding encodingType, MM //Create our component pipeline. fixture.MMALCamera .AddEncoder(imgEncoder, fixture.MMALCamera.Camera.StillPort) - .CreatePreviewComponent(new MMALNullSinkComponent()) - .ConfigureCamera(); + .CreatePreviewComponent(new MMALNullSinkComponent()); await fixture.MMALCamera.TakePictureTimelapse(fixture.MMALCamera.Camera.StillPort, new Timelapse { Mode = TimelapseMode.Second, Value = 5, Timeout = DateTime.Now.AddSeconds(20) }); @@ -438,8 +435,7 @@ public void TakePictureTimeout(string extension, MMALEncoding encodingType, MMAL //Create our component pipeline. fixture.MMALCamera .AddEncoder(imgEncoder, fixture.MMALCamera.Camera.StillPort) - .CreatePreviewComponent(new MMALNullSinkComponent()) - .ConfigureCamera(); + .CreatePreviewComponent(new MMALNullSinkComponent()); await fixture.MMALCamera.TakePictureTimeout(fixture.MMALCamera.Camera.StillPort, DateTime.Now.AddSeconds(20)); @@ -462,8 +458,7 @@ public void ChangeEncodingType() //Create our component pipeline. fixture.MMALCamera .AddEncoder(imgEncoder, fixture.MMALCamera.Camera.StillPort) - .CreatePreviewComponent(new MMALNullSinkComponent()) - .ConfigureCamera(); + .CreatePreviewComponent(new MMALNullSinkComponent()); await fixture.MMALCamera.TakePicture(fixture.MMALCamera.Camera.StillPort); } @@ -485,8 +480,7 @@ public void ChangeEncodingType() //Create our component pipeline. fixture.MMALCamera .AddEncoder(imgEncoder, fixture.MMALCamera.Camera.StillPort) - .CreatePreviewComponent(new MMALNullSinkComponent()) - .ConfigureCamera(); + .CreatePreviewComponent(new MMALNullSinkComponent()); await fixture.MMALCamera.TakePicture(fixture.MMALCamera.Camera.StillPort); } diff --git a/tests/MMALSharp.Tests/VideoEncoderTests.cs b/tests/MMALSharp.Tests/VideoEncoderTests.cs index 146a0a70..3d9c8f63 100644 --- a/tests/MMALSharp.Tests/VideoEncoderTests.cs +++ b/tests/MMALSharp.Tests/VideoEncoderTests.cs @@ -83,8 +83,7 @@ public void TakeVideo(string extension, MMALEncoding encodingType, MMALEncoding //Create our component pipeline. fixture.MMALCamera .AddEncoder(vidEncoder, fixture.MMALCamera.Camera.VideoPort) - .CreatePreviewComponent(new MMALVideoRenderer()) - .ConfigureCamera(); + .CreatePreviewComponent(new MMALVideoRenderer()); //Record video for 20 seconds await fixture.MMALCamera.TakeVideo(fixture.MMALCamera.Camera.VideoPort, DateTime.Now.AddSeconds(20)); @@ -120,8 +119,7 @@ public void TakeVideoSplit(string extension, MMALEncoding encodingType, MMALEnco //Create our component pipeline. fixture.MMALCamera .AddEncoder(vidEncoder, fixture.MMALCamera.Camera.VideoPort) - .CreatePreviewComponent(new MMALVideoRenderer()) - .ConfigureCamera(); + .CreatePreviewComponent(new MMALVideoRenderer()); //2 files should be created from this test. await fixture.MMALCamera.TakeVideo(fixture.MMALCamera.Camera.VideoPort, DateTime.Now.AddSeconds(30), @@ -149,8 +147,7 @@ public void ChangeEncoderType() //Create our component pipeline. fixture.MMALCamera .AddEncoder(vidEncoder, fixture.MMALCamera.Camera.VideoPort) - .CreatePreviewComponent(new MMALVideoRenderer()) - .ConfigureCamera(); + .CreatePreviewComponent(new MMALVideoRenderer()); //Record video for 20 seconds await fixture.MMALCamera.TakeVideo(fixture.MMALCamera.Camera.VideoPort, DateTime.Now.AddSeconds(20)); @@ -174,8 +171,7 @@ public void ChangeEncoderType() //Create our component pipeline. fixture.MMALCamera .AddEncoder(vidEncoder, fixture.MMALCamera.Camera.VideoPort) - .CreatePreviewComponent(new MMALVideoRenderer()) - .ConfigureCamera(); + .CreatePreviewComponent(new MMALVideoRenderer()); //Record video for 20 seconds await fixture.MMALCamera.TakeVideo(fixture.MMALCamera.Camera.VideoPort, DateTime.Now.AddSeconds(20));