diff --git a/src/MMALSharp/Components/EncoderComponents/MMALImageEncoder.cs b/src/MMALSharp/Components/EncoderComponents/MMALImageEncoder.cs index 7ca13513..a31b17e3 100644 --- a/src/MMALSharp/Components/EncoderComponents/MMALImageEncoder.cs +++ b/src/MMALSharp/Components/EncoderComponents/MMALImageEncoder.cs @@ -196,6 +196,7 @@ private void AddExifTag(ExifTag exifTag) } finally { + Marshal.DestroyStructure(ptr, typeof(MMAL_PARAMETER_EXIF_T)); Marshal.FreeHGlobal(ptr); } } diff --git a/src/MMALSharp/Components/EncoderComponents/MMALVideoEncoder.cs b/src/MMALSharp/Components/EncoderComponents/MMALVideoEncoder.cs index 457ed967..eb90e220 100644 --- a/src/MMALSharp/Components/EncoderComponents/MMALVideoEncoder.cs +++ b/src/MMALSharp/Components/EncoderComponents/MMALVideoEncoder.cs @@ -206,6 +206,7 @@ private void ConfigureVideoProfile(int outputPort) } finally { + Marshal.DestroyStructure(ptr, typeof(MMAL_PARAMETER_VIDEO_PROFILE_T)); Marshal.FreeHGlobal(ptr); } } diff --git a/src/MMALSharp/Components/IDownstreamComponent.cs b/src/MMALSharp/Components/IDownstreamComponent.cs index 552388da..456e2e6a 100644 --- a/src/MMALSharp/Components/IDownstreamComponent.cs +++ b/src/MMALSharp/Components/IDownstreamComponent.cs @@ -79,8 +79,9 @@ IDownstreamComponent ConfigureInputPort(IMMALPortConfig config, IInputCap /// The output port number. /// The port configuration object. /// The capture handler to use with this port. + /// Optional port to copy format from. /// This component. - IDownstreamComponent ConfigureOutputPort(int outputPort, IMMALPortConfig config, IOutputCaptureHandler handler) + IDownstreamComponent ConfigureOutputPort(int outputPort, IMMALPortConfig config, IOutputCaptureHandler handler, IInputPort copyFrom = null) where TPort : IOutputPort; } } diff --git a/src/MMALSharp/Components/MMALDownstreamComponent.cs b/src/MMALSharp/Components/MMALDownstreamComponent.cs index e8d667a3..208483d2 100644 --- a/src/MMALSharp/Components/MMALDownstreamComponent.cs +++ b/src/MMALSharp/Components/MMALDownstreamComponent.cs @@ -134,13 +134,14 @@ public virtual IDownstreamComponent ConfigureOutputPort(int outputPort, IMMALPor /// The output port number to configure. /// User provided port configuration object. /// The output port capture handler. + /// Optional port to copy format from. /// This . - public virtual unsafe IDownstreamComponent ConfigureOutputPort(int outputPort, IMMALPortConfig config, IOutputCaptureHandler handler) + public virtual unsafe IDownstreamComponent ConfigureOutputPort(int outputPort, IMMALPortConfig config, IOutputCaptureHandler handler, IInputPort copyFrom = null) where TPort : IOutputPort { this.Outputs[outputPort] = (IOutputPort)Activator.CreateInstance(typeof(TPort), (IntPtr)(&(*this.Ptr->Output[outputPort])), this, Guid.NewGuid()); - return this.ConfigureOutputPort(outputPort, config, handler); + return this.ConfigureOutputPort(outputPort, config, handler, copyFrom); } /// diff --git a/src/MMALSharp/Components/MMALRawcamComponent.cs b/src/MMALSharp/Components/MMALRawcamComponent.cs index 1ee84839..95d39348 100644 --- a/src/MMALSharp/Components/MMALRawcamComponent.cs +++ b/src/MMALSharp/Components/MMALRawcamComponent.cs @@ -86,6 +86,7 @@ private unsafe void ConfigureCameraInterface(MMAL_CAMERA_INTERFACE_T cameraInter } finally { + Marshal.DestroyStructure(ptr, typeof(MMAL_PARAMETER_CAMERA_INTERFACE_T)); Marshal.FreeHGlobal(ptr); } } @@ -106,6 +107,7 @@ private unsafe void ConfigureCameraClockingMode(MMAL_CAMERA_CLOCKING_MODE_T cloc } finally { + Marshal.DestroyStructure(ptr, typeof(MMAL_PARAMETER_CAMERA_CLOCKING_MODE_T)); Marshal.FreeHGlobal(ptr); } } @@ -127,6 +129,7 @@ private unsafe void ConfigureCameraRxConfig(MMALRawcamRxConfig rxConfig) } finally { + Marshal.DestroyStructure(ptr, typeof(MMAL_PARAMETER_CAMERA_RX_CONFIG_T)); Marshal.FreeHGlobal(ptr); } } @@ -149,6 +152,7 @@ private unsafe void ConfigureTimingRegisters(MMALRawcamTimingConfig timingConfig } finally { + Marshal.DestroyStructure(ptr, typeof(MMAL_PARAMETER_CAMERA_RX_TIMING_T)); Marshal.FreeHGlobal(ptr); } } diff --git a/src/MMALSharp/Components/MMALRendererComponent.cs b/src/MMALSharp/Components/MMALRendererComponent.cs index 954d4905..889e4526 100644 --- a/src/MMALSharp/Components/MMALRendererComponent.cs +++ b/src/MMALSharp/Components/MMALRendererComponent.cs @@ -165,6 +165,7 @@ public unsafe void ConfigureRenderer() } finally { + Marshal.DestroyStructure(ptr, typeof(MMAL_DISPLAYREGION_T)); Marshal.FreeHGlobal(ptr); } } diff --git a/src/MMALSharp/IBuffer.cs b/src/MMALSharp/IBuffer.cs index 8f59669c..e32a78da 100644 --- a/src/MMALSharp/IBuffer.cs +++ b/src/MMALSharp/IBuffer.cs @@ -3,6 +3,7 @@ // Licensed under the MIT License. Please see LICENSE.txt for License info. // +using System; using System.Collections.Generic; using MMALSharp.Native; @@ -99,6 +100,14 @@ public interface IBuffer : IMMALObject /// Signal that we've reached the end of the input file. void ReadIntoBuffer(byte[] source, int length, bool eof); + /// + /// Primes the buffer for use via vcsm. + /// + /// The vcsm opaque handle. + /// The buffer size. + /// The memory handle. + void ReadIntoBufferVcsm(IntPtr handle, int bufferSize, IntPtr userData); + /// /// Acquire a buffer header. Acquiring a buffer header increases a reference counter on it and makes /// sure that the buffer header won't be recycled until all the references to it are gone. diff --git a/src/MMALSharp/MMALBufferImpl.cs b/src/MMALSharp/MMALBufferImpl.cs index 9b75d3c1..3e1cb4e7 100644 --- a/src/MMALSharp/MMALBufferImpl.cs +++ b/src/MMALSharp/MMALBufferImpl.cs @@ -22,7 +22,7 @@ public unsafe class MMALBufferImpl : MMALObject, IBuffer /// /// Pointer to the data associated with this buffer header. /// - public byte* Data => this.Ptr->data; + public IntPtr Data => this.Ptr->data; /// /// Defines what the buffer header contains. This is a FourCC with 0 as a special value meaning stream data. @@ -185,7 +185,7 @@ public byte[] GetBufferData() try { - var ps = this.Ptr->data + this.Offset; + var ps = (byte*)this.Ptr->data + this.Offset; var buffer = new byte[(int)this.Ptr->Length]; Marshal.Copy((IntPtr)ps, buffer, 0, buffer.Length); MMALBuffer.mmal_buffer_header_mem_unlock(this.Ptr); @@ -226,6 +226,24 @@ public void ReadIntoBuffer(byte[] source, int length, bool eof) Marshal.Copy(source, 0, (IntPtr)this.Ptr->data, length); } + /// + /// Primes the buffer for use via vcsm. + /// + /// The vcsm opaque handle. + /// The buffer size. + /// The memory handle. + public void ReadIntoBufferVcsm(IntPtr handle, int bufferSize, IntPtr userData) + { + if (MMALCameraConfig.Debug) + { + MMALLog.Logger.LogDebug($"Priming buffer to handle {bufferSize} bytes."); + } + + this.Ptr->data = handle; + this.Ptr->length = (uint)bufferSize; + this.Ptr->userData = userData; + } + /// /// Acquire a buffer header. Acquiring a buffer header increases a reference counter on it and makes /// sure that the buffer header won't be recycled until all the references to it are gone. diff --git a/src/MMALSharp/MMALCameraExtensions.cs b/src/MMALSharp/MMALCameraExtensions.cs index 52447fbd..fd44a58a 100644 --- a/src/MMALSharp/MMALCameraExtensions.cs +++ b/src/MMALSharp/MMALCameraExtensions.cs @@ -337,7 +337,9 @@ internal static void SetAnnotateSettings(this MMALCameraComponent camera) } catch { + Marshal.DestroyStructure(ptrV4, typeof(MMAL_PARAMETER_CAMERA_ANNOTATE_V4_T)); Marshal.FreeHGlobal(ptrV4); + ptrV4 = IntPtr.Zero; MMALLog.Logger.LogWarning("Unable to set V4 annotation structure. Trying V3. Please update firmware to latest version."); @@ -358,6 +360,7 @@ internal static void SetAnnotateSettings(this MMALCameraComponent camera) } finally { + Marshal.DestroyStructure(ptr, typeof(MMAL_PARAMETER_CAMERA_ANNOTATE_V3_T)); Marshal.FreeHGlobal(ptr); } } @@ -394,6 +397,7 @@ internal static void DisableAnnotate(this MMALCameraComponent camera) } finally { + Marshal.DestroyStructure(ptr, typeof(MMAL_PARAMETER_CAMERA_ANNOTATE_V3_T)); Marshal.FreeHGlobal(ptr); } } diff --git a/src/MMALSharp/MMALRawcam.cs b/src/MMALSharp/MMALRawcam.cs index d3d21358..8b60b666 100644 --- a/src/MMALSharp/MMALRawcam.cs +++ b/src/MMALSharp/MMALRawcam.cs @@ -21,8 +21,16 @@ public sealed class MMALRawcam private const int I2C_M_RD = 0x0001; private const int I2C_SLAVE = 0x0703; private const int I2C_SLAVE_FORCE = 0x0706; - private const int I2C_RDWR = 0x0707; - + private const int I2C_RDWR = 0x0707; + + private MMALRawcamComponent _rawcamComponent; + private MMALIspComponent _ispComponent; + private SensorDef _sensorDef; + private ModeDef _modeDef; + private int _mode; + private string _i2cDeviceName; + private int _activeFileDescriptor; + /// /// Gets the singleton instance of the MMAL Raw Camera. Call to initialise the camera for first use. /// @@ -35,30 +43,12 @@ public sealed class MMALRawcam private static readonly Lazy Lazy = new Lazy(() => new MMALRawcam()); - private static List SensorDefs - { - get - { - return new List - { - Ov5647SensorDefs.Ov5647SensorDef, - Imx219SensorDefs.Imx219SensorDef - }; - } - } - - private MMALRawcamComponent RawcamComponent { get; set; } - - private MMALIspComponent IspComponent { get; set; } - - private SensorDef SensorDef { get; set; } - - private ModeDef ModeDef { get; set; } - - private int Mode { get; set; } - - private string I2CDeviceName { get; set; } - + private static List SensorDefs => new List + { + Ov5647SensorDefs.Ov5647SensorDef, + Imx219SensorDefs.Imx219SensorDef + }; + private MMALRawcam() { BcmHost.bcm_host_init(); @@ -75,10 +65,10 @@ private MMALRawcam() /// The I2C device name. public void ConfigureRawcamPipeline(MMALRawcamComponent rawcamComponent, MMALIspComponent ispComponent, int mode, string i2cDeviceName) { - this.RawcamComponent = rawcamComponent; - this.IspComponent = ispComponent; - this.I2CDeviceName = i2cDeviceName; - this.Mode = mode; + _rawcamComponent = rawcamComponent; + _ispComponent = ispComponent; + _i2cDeviceName = i2cDeviceName; + _mode = mode; } /// @@ -93,46 +83,46 @@ public void ConfigureRawcamPipeline(MMALRawcamComponent rawcamComponent, MMALIsp MMALLog.Logger.LogInformation("Attempting to enable rawcam components..."); - var sensor = this.ProbeSensor(this.I2CDeviceName); + var sensor = this.ProbeSensor(_i2cDeviceName); if (sensor == null) { - throw new MMALIOException($"Could not probe sensor {this.I2CDeviceName}"); + throw new MMALIOException($"Could not probe sensor {_i2cDeviceName}"); } + + _ispComponent.EnableComponent(); + _rawcamComponent.EnableComponent(); - this.IspComponent.EnableComponent(); - this.RawcamComponent.EnableComponent(); - - this.IspComponent.Inputs[0].Start(); - this.IspComponent.Outputs[0].Start(); - this.RawcamComponent.Outputs[0].Start(); + _ispComponent.Inputs[0].Start(); + _ispComponent.Outputs[0].Start(); + _rawcamComponent.Outputs[0].Start(); - tasks.Add(this.IspComponent.Outputs[0].Trigger.Task); - tasks.Add(this.RawcamComponent.Outputs[0].Trigger.Task); + tasks.Add(_ispComponent.Outputs[0].Trigger.Task); + tasks.Add(_rawcamComponent.Outputs[0].Trigger.Task); MMALLog.Logger.LogDebug("Attempting to start rawcam streaming..."); - await this.StartCapture(sensor, sensor.Modes[this.Mode], this.I2CDeviceName); + await this.StartCapture(sensor, sensor.Modes[_mode], _i2cDeviceName); if (cancellationToken == CancellationToken.None) { await Task.WhenAll(tasks).ConfigureAwait(false); - await this.StopCapture(sensor, this.I2CDeviceName); + await this.StopCapture(sensor, _i2cDeviceName); } else { await Task.WhenAny(Task.WhenAll(tasks), cancellationToken.AsTask()).ConfigureAwait(false); - this.IspComponent.ForceStopProcessing = true; - this.RawcamComponent.ForceStopProcessing = true; + _ispComponent.ForceStopProcessing = true; + _rawcamComponent.ForceStopProcessing = true; - await this.StopCapture(sensor, this.I2CDeviceName); + await this.StopCapture(sensor, _i2cDeviceName); await Task.WhenAll(tasks).ConfigureAwait(false); } - foreach (var port in this.IspComponent.ProcessingPorts.Values) + foreach (var port in _ispComponent.ProcessingPorts.Values) { if (port.ConnectedReference == null) { @@ -140,10 +130,10 @@ public void ConfigureRawcamPipeline(MMALRawcamComponent rawcamComponent, MMALIsp } } - this.IspComponent.CleanPortPools(); - this.IspComponent.DisableConnections(); + _ispComponent.CleanPortPools(); + _ispComponent.DisableConnections(); - foreach (var port in this.RawcamComponent.ProcessingPorts.Values) + foreach (var port in _rawcamComponent.ProcessingPorts.Values) { if (port.ConnectedReference == null) { @@ -151,8 +141,8 @@ public void ConfigureRawcamPipeline(MMALRawcamComponent rawcamComponent, MMALIsp } } - this.RawcamComponent.CleanPortPools(); - this.RawcamComponent.DisableConnections(); + _rawcamComponent.CleanPortPools(); + _rawcamComponent.DisableConnections(); } /// @@ -173,28 +163,14 @@ private async Task StartCapture(SensorDef sensorDef, ModeDef modeDef, string i2c { try { - var fd = MMALI2C.Open(i2cDeviceName, OPEN_READ_WRITE); - - if (fd == 0) - { - throw new MMALIOException("Couldn't open I2C device."); - } - - var addrArr = new byte[1] { sensorDef.I2CAddr }; - var ptr = Marshal.AllocHGlobal(addrArr.Length); - - Marshal.Copy(addrArr, 0, ptr, addrArr.Length); - - if (MMALI2C.Ioctl(fd, I2C_SLAVE_FORCE, ptr) < 0) + if (MMALI2C.IoctlByte(_activeFileDescriptor, I2C_SLAVE_FORCE, sensorDef.I2CAddr) < 0) { - Marshal.FreeHGlobal(ptr); - throw new MMALIOException("Failed to set I2C address."); + var lastError = Marshal.GetLastWin32Error(); + throw new MMALIOException($"Failed to set I2C address. Win32Err {lastError}. Fd {_activeFileDescriptor}. I2CAddr {sensorDef.I2CAddr}."); } - Marshal.FreeHGlobal(ptr); + await this.SendRegs(_activeFileDescriptor, sensorDef, modeDef.Regs); - await this.SendRegs(fd, sensorDef, modeDef.Regs); - MMALI2C.Close(fd); MMALLog.Logger.LogInformation("Now streaming..."); } catch (Exception e) @@ -207,28 +183,16 @@ private async Task StopCapture(SensorDef sensorDef, string i2cDeviceName) { try { - var fd = MMALI2C.Open(i2cDeviceName, OPEN_READ_WRITE); - - if (fd == 0) + if (MMALI2C.IoctlByte(_activeFileDescriptor, I2C_SLAVE_FORCE, sensorDef.I2CAddr) < 0) { - throw new MMALIOException("Couldn't open I2C device."); + var lastError = Marshal.GetLastWin32Error(); + throw new MMALIOException($"Failed to set I2C address. Win32Err {lastError}. Fd {_activeFileDescriptor}. I2CAddr {sensorDef.I2CAddr}."); } - var addrArr = new byte[1] { sensorDef.I2CAddr }; - var ptr = Marshal.AllocHGlobal(addrArr.Length); + await this.SendRegs(_activeFileDescriptor, sensorDef, sensorDef.StopReg); - Marshal.Copy(addrArr, 0, ptr, addrArr.Length); + MMALI2C.Close(_activeFileDescriptor); - if (MMALI2C.Ioctl(fd, I2C_SLAVE_FORCE, ptr) < 0) - { - Marshal.FreeHGlobal(ptr); - throw new MMALIOException("Failed to set I2C address."); - } - - Marshal.FreeHGlobal(ptr); - - await this.SendRegs(fd, sensorDef, sensorDef.StopReg); - MMALI2C.Close(fd); MMALLog.Logger.LogInformation("Stop streaming..."); } catch (Exception e) @@ -248,6 +212,8 @@ private SensorDef ProbeSensor(string i2cDeviceName) throw new MMALIOException("Couldn't open I2C device."); } + _activeFileDescriptor = fd; + MMALLog.Logger.LogInformation($"I2C Device Name: {i2cDeviceName} | Fd err: {err}"); foreach (var sensorDef in SensorDefs) @@ -361,6 +327,7 @@ private async Task SendRegs(int fd, SensorDef sensorDef, List sensorR if (sensorRegs[i].Reg == 0xFFFF) { var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(sensorRegs[i].Data)); + Marshal.WriteInt32(ptr, sensorRegs[i].Data); if (MMALI2C.Ioctl(fd, I2C_SLAVE_FORCE, ptr) < 0) { @@ -369,6 +336,8 @@ private async Task SendRegs(int fd, SensorDef sensorDef, List sensorR } Marshal.FreeHGlobal(ptr); + + MMALLog.Logger.LogInformation($"Successfully called Ioctl on file descriptor {fd}."); } else if (sensorRegs[i].Reg == 0xFFFE) { @@ -413,6 +382,8 @@ private async Task SendRegs(int fd, SensorDef sensorDef, List sensorR throw new MMALIOException($"Failed to write register index {i} ({sensorRegs[i].Reg} val {sensorRegs[i].Data}). Msg length: {msg.Length}. Written: {write}. Errno: {errno}"); } + + MMALLog.Logger.LogInformation($"Wrote {msg.Length} bytes to file descriptor {fd}."); } else { @@ -452,6 +423,8 @@ private async Task SendRegs(int fd, SensorDef sensorDef, List sensorR throw new MMALIOException($"Failed to write register index {i}. Msg length: {msg.Length}. Written: {write}. Errno: {errno}"); } + + MMALLog.Logger.LogInformation($"Wrote {msg.Length} bytes to file descriptor {fd}."); } } } diff --git a/src/MMALSharp/Native/MMALBuffer.cs b/src/MMALSharp/Native/MMALBuffer.cs index b05299e8..e4f42624 100644 --- a/src/MMALSharp/Native/MMALBuffer.cs +++ b/src/MMALSharp/Native/MMALBuffer.cs @@ -175,11 +175,10 @@ public unsafe struct MMAL_BUFFER_HEADER_T private MMAL_BUFFER_HEADER_T* next; private IntPtr priv; private uint cmd; - public byte* data; + public IntPtr data; public uint allocSize, length, offset, flags; public long pts, dts; - - private IntPtr type, userData; + public IntPtr type, userData; public MMAL_BUFFER_HEADER_T* Next => this.next; @@ -187,7 +186,7 @@ public unsafe struct MMAL_BUFFER_HEADER_T public uint Cmd => this.cmd; - public byte* Data => this.data; + public IntPtr Data => this.data; public uint AllocSize => this.allocSize; @@ -205,7 +204,7 @@ public unsafe struct MMAL_BUFFER_HEADER_T public IntPtr UserData => this.userData; - public MMAL_BUFFER_HEADER_T(MMAL_BUFFER_HEADER_T* next, IntPtr priv, uint cmd, byte* data, uint allocSize, uint length, uint offset, uint flags, long pts, long dts, IntPtr type, IntPtr userData) + public MMAL_BUFFER_HEADER_T(MMAL_BUFFER_HEADER_T* next, IntPtr priv, uint cmd, IntPtr data, uint allocSize, uint length, uint offset, uint flags, long pts, long dts, IntPtr type, IntPtr userData) { this.next = next; this.priv = priv; diff --git a/src/MMALSharp/Native/MMALI2C.cs b/src/MMALSharp/Native/MMALI2C.cs index 870c5139..28ac5821 100644 --- a/src/MMALSharp/Native/MMALI2C.cs +++ b/src/MMALSharp/Native/MMALI2C.cs @@ -13,6 +13,9 @@ public class MMALI2C [DllImport("libc.so.6", EntryPoint = "ioctl", SetLastError = true)] internal static extern int Ioctl(int fd, int request, IntPtr data); + [DllImport("libc.so.6", EntryPoint = "ioctl", SetLastError = true)] + internal static extern int IoctlByte(int fd, int request, byte data); + [DllImport("libc.so.6", EntryPoint = "read", SetLastError = true)] internal static extern int Read(int handle, IntPtr data, int length); diff --git a/src/MMALSharp/Native/MMALUtil.cs b/src/MMALSharp/Native/MMALUtil.cs index 491cbfcc..20452d10 100644 --- a/src/MMALSharp/Native/MMALUtil.cs +++ b/src/MMALSharp/Native/MMALUtil.cs @@ -50,6 +50,29 @@ public enum MMAL_STATUS_T MMAL_STATUS_MAX = 0x7FFFFFFF } + public enum VCSM_CACHE_TYPE_T + { + /// + /// No caching applies. + /// + VCSM_CACHE_TYPE_NONE = 0, + + /// + /// Allocation is cached on host (user space). + /// + VCSM_CACHE_TYPE_HOST, + + /// + /// Allocation is cached on videocore. + /// + VCSM_CACHE_TYPE_VC, + + /// + /// Allocation is cached on both host and videocore. + /// + VCSM_CACHE_TYPE_HOST_AND_VC + } + #pragma warning disable IDE1006 // Naming Styles [DllImport("libmmal.so", EntryPoint = "mmal_port_parameter_set_boolean", CallingConvention = CallingConvention.Cdecl)] public static extern unsafe MMAL_STATUS_T mmal_port_parameter_set_boolean(MMAL_PORT_T* port, uint id, int value); @@ -145,8 +168,23 @@ public enum MMAL_STATUS_T public static extern unsafe MMAL_PORT_T mmal_util_get_port(MMAL_COMPONENT_T* comp, MMALPort.MMAL_PORT_TYPE_T pType, uint index); [DllImport("libmmal.so", EntryPoint = "mmal_4cc_to_string", CallingConvention = CallingConvention.Cdecl)] - public static extern unsafe string mmal_4cc_to_string([MarshalAs(UnmanagedType.LPTStr)] string buffer, ushort len, uint fourcc); - + public static extern string mmal_4cc_to_string([MarshalAs(UnmanagedType.LPStr)] string buffer, ushort len, uint fourcc); + + [DllImport("libvcsm.so", EntryPoint = "vcsm_malloc_cache", CallingConvention = CallingConvention.Cdecl)] + public static extern uint vcsm_malloc_cache(uint size, VCSM_CACHE_TYPE_T cache, [MarshalAs(UnmanagedType.LPStr)] string name); + + [DllImport("libvcsm.so", EntryPoint = "vcsm_vc_hdl_from_hdl", CallingConvention = CallingConvention.Cdecl)] + public static extern uint vcsm_vc_hdl_from_hdl(uint handle); + + [DllImport("libvcsm.so", EntryPoint = "vcsm_lock", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr vcsm_lock(uint handle); + + [DllImport("libvcsm.so", EntryPoint = "vcsm_unlock_hdl", CallingConvention = CallingConvention.Cdecl)] + public static extern uint vcsm_unlock_hdl(uint handle); + + [DllImport("libvcsm.so", EntryPoint = "vcsm_free", CallingConvention = CallingConvention.Cdecl)] + public static extern void vcsm_free(uint handle); + #pragma warning restore IDE1006 // Naming Styles } diff --git a/src/MMALSharp/Ports/Outputs/RawcamOutputPort.cs b/src/MMALSharp/Ports/Outputs/RawcamOutputPort.cs new file mode 100644 index 00000000..9bffa5a1 --- /dev/null +++ b/src/MMALSharp/Ports/Outputs/RawcamOutputPort.cs @@ -0,0 +1,98 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using MMALSharp.Callbacks; +using MMALSharp.Common.Utility; +using MMALSharp.Components; +using MMALSharp.Handlers; +using MMALSharp.Native; +using MMALSharp.Ports.Inputs; + +namespace MMALSharp.Ports.Outputs +{ + public unsafe class RawcamOutputPort : OutputPort + { + /// + public override Resolution Resolution + { + get => new Resolution(this.Width, this.Height); + internal set + { + if (value.Width == 0 || value.Height == 0) + { + this.Width = MMALCameraConfig.VideoResolution.Pad().Width; + this.Height = MMALCameraConfig.VideoResolution.Pad().Height; + } + else + { + this.Width = value.Pad().Width; + this.Height = value.Pad().Height; + } + } + } + + /// + /// Creates a new instance of . + /// + /// The native pointer. + /// The component this port is associated with. + /// Managed unique identifier for this port. + public RawcamOutputPort(IntPtr ptr, IComponent comp, Guid guid) + : base(ptr, comp, guid) + { + } + + /// + /// Creates a new instance of . + /// + /// The port to copy data from. + public RawcamOutputPort(IPort copyFrom) + : base((IntPtr)copyFrom.Ptr, copyFrom.ComponentReference, copyFrom.Guid) + { + } + + /// + public override void Configure(IMMALPortConfig config, IInputPort copyFrom, IOutputCaptureHandler handler) + { + base.Configure(config, copyFrom, handler); + + this.CallbackHandler = new DefaultOutputPortCallbackHandler(this, handler, null); + } + + /// + /// The native callback MMAL passes buffer headers to. + /// + /// The port the buffer is sent to. + /// The buffer header. + internal override void NativeOutputPortCallback(MMAL_PORT_T* port, MMAL_BUFFER_HEADER_T* buffer) + { + if (MMALCameraConfig.Debug) + { + MMALLog.Logger.LogDebug($"In native {nameof(RawcamOutputPort)} output callback"); + } + + var bufferImpl = new MMALBufferImpl(buffer); + + bufferImpl.PrintProperties(); + + var eos = (this.PortConfig.Timeout.HasValue && DateTime.Now.CompareTo(this.PortConfig.Timeout.Value) > 0) || this.ComponentReference.ForceStopProcessing; + + if (bufferImpl.CheckState() && bufferImpl.Length > 0 && !eos && !this.Trigger.Task.IsCompleted) + { + this.CallbackHandler.Callback(bufferImpl); + } + + // Ensure we release the buffer before any signalling or we will cause a memory leak due to there still being a reference count on the buffer. + this.ReleaseBuffer(bufferImpl); + + if (eos) + { + if (!this.Trigger.Task.IsCompleted) + { + MMALLog.Logger.LogDebug($"{this.ComponentReference.Name} {this.Name} Timeout exceeded, triggering signal."); + Task.Run(() => { this.Trigger.SetResult(true); }); + } + } + } + } +} diff --git a/src/MMALSharp/Ports/PortBase.cs b/src/MMALSharp/Ports/PortBase.cs index 1879afc8..353a1bb2 100644 --- a/src/MMALSharp/Ports/PortBase.cs +++ b/src/MMALSharp/Ports/PortBase.cs @@ -384,6 +384,40 @@ public void SendBuffer(IBuffer buffer) } } + /// + /// Send a buffer header to a port manually via vcsm. This method does not use MMAL Core functionality. + /// + /// A managed buffer object. + /// + public void SendBufferManual(IBuffer buffer) + { + if (this.Enabled) + { + if (MMALCameraConfig.Debug) + { + MMALLog.Logger.LogDebug("Sending manual buffer start."); + } + + var vcsmHandle = MMALUtil.vcsm_malloc_cache(buffer.Length, MMALUtil.VCSM_CACHE_TYPE_T.VCSM_CACHE_TYPE_VC, "mmal_vc_port buffer"); + var vcHandle = MMALUtil.vcsm_vc_hdl_from_hdl(vcsmHandle); + var ptr = MMALUtil.vcsm_lock(vcsmHandle); + + if (ptr != IntPtr.Zero) + { + buffer.ReadIntoBufferVcsm(vcHandle, (int)buffer.Length, ptr); + } + else + { + + } + + if (MMALCameraConfig.Debug) + { + MMALLog.Logger.LogDebug("Sending manual buffer complete."); + } + } + } + /// /// Attempts to send all available buffers in the queue to this port. /// @@ -403,6 +437,22 @@ public void SendAllBuffers() } } + public void SendAllBuffersManual() + { + this.InitialiseBufferPool(); + + var length = this.BufferPool.Queue.QueueLength(); + + for (int i = 0; i < length; i++) + { + var buffer = this.BufferPool.Queue.GetBuffer(); + + MMALLog.Logger.LogDebug($"Sending buffer to output port: Length {buffer.Length}"); + + this.SendBuffer(buffer); + } + } + /// /// Attempts to send all available buffers in the specified pool's queue to this port. ///