/// <summary> /// Call to configure changes on an Image Encoder input port. Used when providing an image file directly /// to the component. /// </summary> /// <param name="encodingType">The encoding type the input port will expect data in</param> /// <param name="width">The width of the incoming frame</param> /// <param name="height">The height of the incoming frame</param> public virtual unsafe void ConfigureInputPort(MMALEncoding encodingType, int width, int height) { this.InitialiseInputPort(0); if (encodingType != null) { this.Inputs[0].Ptr->Format->encoding = encodingType.EncodingVal; } this.Inputs[0].Ptr->Format->es->video.height = MMALUtil.VCOS_ALIGN_UP(height, 32); this.Inputs[0].Ptr->Format->es->video.width = MMALUtil.VCOS_ALIGN_UP(width, 32); this.Inputs[0].Ptr->Format->es->video.crop = new MMAL_RECT_T(0, 0, width, height); this.Inputs[0].Commit(); if (this.Outputs[0].Ptr->Format->type == MMALFormat.MMAL_ES_TYPE_T.MMAL_ES_TYPE_UNKNOWN) { throw new PiCameraError("Unable to determine settings for output port."); } this.Inputs[0].Ptr->BufferNum = Math.Max(this.Inputs[0].Ptr->BufferNumRecommended, this.Inputs[0].Ptr->BufferNumMin); this.Inputs[0].Ptr->BufferSize = Math.Max(this.Inputs[0].Ptr->BufferSizeRecommended, this.Inputs[0].Ptr->BufferSizeMin); this.Inputs[0].EncodingType = encodingType; }
/// <summary> /// Creates a new instance of <see cref="MMALPoolImpl"/> based on a port. /// </summary> /// <param name="port">The port.</param> public MMALPoolImpl(PortBase port) { MMALLog.Logger.Debug($"Creating buffer pool with {port.BufferNum} buffers of size {port.BufferSize}"); this.Ptr = MMALUtil.mmal_port_pool_create(port.Ptr, port.BufferNum, port.BufferSize); this.Queue = new MMALQueueImpl((*this.Ptr).Queue); }
/// <summary> /// Initialises the camera's video port using the width, height and encoding as specified by the user /// </summary> internal void InitialiseVideo() { if (MMALCameraConfig.VideoResolution.Width == 0 || MMALCameraConfig.VideoResolution.Width > this.CameraInfo.MaxWidth) { MMALCameraConfig.VideoResolution.Width = this.CameraInfo.MaxWidth; } if (MMALCameraConfig.VideoResolution.Height == 0 || MMALCameraConfig.VideoResolution.Height > this.CameraInfo.MaxHeight) { MMALCameraConfig.VideoResolution.Height = this.CameraInfo.MaxHeight; } var vFormat = new MMAL_VIDEO_FORMAT_T( MMALUtil.VCOS_ALIGN_UP(MMALCameraConfig.VideoResolution.Width, 32), MMALUtil.VCOS_ALIGN_UP(MMALCameraConfig.VideoResolution.Height, 16), new MMAL_RECT_T(0, 0, MMALCameraConfig.VideoResolution.Width, MMALCameraConfig.VideoResolution.Height), MMALCameraConfig.VideoFramerate, this.VideoPort.Ptr->Format->es->video.par, this.VideoPort.Ptr->Format->es->video.colorSpace ); this.VideoPort.Ptr->Format->encoding = MMALCameraConfig.VideoEncoding.EncodingVal; this.VideoPort.Ptr->Format->encodingVariant = MMALCameraConfig.VideoSubformat.EncodingVal; this.VideoPort.Ptr->Format->es->video = vFormat; MMALLog.Logger.Debug("Commit video"); this.VideoPort.Commit(); this.VideoPort.Ptr->BufferNum = Math.Max(this.VideoPort.BufferNumRecommended, this.VideoPort.BufferNumMin); this.VideoPort.Ptr->BufferSize = Math.Max(this.VideoPort.BufferSizeRecommended, this.VideoPort.BufferSizeMin); }
/// <summary> /// Initialises the camera's preview component using the user defined width/height for the video port /// </summary> internal void InitialisePreview() { var vFormat = new MMAL_VIDEO_FORMAT_T( MMALUtil.VCOS_ALIGN_UP(MMALCameraConfig.VideoResolution.Width, 32), MMALUtil.VCOS_ALIGN_UP(MMALCameraConfig.VideoResolution.Height, 16), new MMAL_RECT_T(0, 0, MMALCameraConfig.VideoResolution.Width, MMALCameraConfig.VideoResolution.Height), new MMAL_RATIONAL_T(0, 1), this.PreviewPort.Ptr->Format->es->video.par, this.PreviewPort.Ptr->Format->es->video.colorSpace ); this.PreviewPort.Ptr->Format->encoding = MMALCameraConfig.PreviewEncoding.EncodingVal; this.PreviewPort.Ptr->Format->encodingVariant = MMALCameraConfig.PreviewSubformat.EncodingVal; this.PreviewPort.Ptr->Format->es->video = vFormat; MMALLog.Logger.Debug("Commit preview"); this.PreviewPort.Commit(); this.PreviewPort.Ptr->BufferNum = Math.Max(this.PreviewPort.BufferNumRecommended, this.PreviewPort.BufferNumMin); this.PreviewPort.Ptr->BufferSize = Math.Max(this.PreviewPort.BufferSizeRecommended, this.PreviewPort.BufferSizeMin); }
/// <summary> /// Provides a facility to get data from the port using the native helper functions. /// </summary> /// <param name="port">The port to get the parameter from.</param> /// <param name="key">The unique key for the parameter.</param> /// <returns>Dynamic parameter based on key parameter.</returns> public static unsafe dynamic GetParameter(this IPort port, int key) { var t = MMALParameterHelpers.ParameterHelper.Where(c => c.ParamValue == key).FirstOrDefault(); if (t == null) { throw new PiCameraError($"Could not find parameter {key}"); } MMALLog.Logger.LogDebug($"Getting parameter {t.ParamName}"); try { switch (t.ParamType.Name) { case "MMAL_PARAMETER_BOOLEAN_T": int boolVal = 0; MMALCheck(MMALUtil.mmal_port_parameter_get_boolean(port.Ptr, (uint)key, ref boolVal), "Unable to get boolean value"); return(boolVal == 1); case "MMAL_PARAMETER_UINT64_T": ulong ulongVal = 0UL; MMALCheck(MMALUtil.mmal_port_parameter_get_uint64(port.Ptr, (uint)key, ref ulongVal), "Unable to get ulong value"); return(ulongVal); case "MMAL_PARAMETER_INT64_T": long longVal = 0U; MMALCheck(MMALUtil.mmal_port_parameter_get_int64(port.Ptr, (uint)key, ref longVal), "Unable to get long value"); return(longVal); case "MMAL_PARAMETER_UINT32_T": uint uintVal = 0U; MMALCheck(MMALUtil.mmal_port_parameter_get_uint32(port.Ptr, (uint)key, ref uintVal), "Unable to get uint value"); return(uintVal); case "MMAL_PARAMETER_INT32_T": int intVal = 0; MMALCheck(MMALUtil.mmal_port_parameter_get_int32(port.Ptr, (uint)key, ref intVal), "Unable to get int value"); return(intVal); case "MMAL_PARAMETER_RATIONAL_T": MMAL_RATIONAL_T ratVal = default(MMAL_RATIONAL_T); MMALCheck(MMALUtil.mmal_port_parameter_get_rational(port.Ptr, (uint)key, ref ratVal), "Unable to get rational value"); return((double)ratVal.Num / ratVal.Den); default: throw new NotSupportedException(); } } catch { MMALLog.Logger.LogWarning($"Unable to get port parameter {t.ParamName}"); throw; } }
/// <summary> /// Provides a facility to set data on the port using the native helper functions /// </summary> /// <param name="port">The port we want to set the parameter on</param> /// <param name="key">The unique key of the parameter</param> /// <param name="value">The value of the parameter</param> public static unsafe void SetParameter(this MMALPortBase port, int key, dynamic value) { var t = MMALParameterHelpers.ParameterHelper.Where(c => c.ParamValue == key).FirstOrDefault(); if (t == null) { throw new PiCameraError($"Could not find parameter {key}"); } MMALLog.Logger.Debug($"Setting parameter {t.ParamName}"); try { switch (t.ParamType.Name) { case "MMAL_PARAMETER_BOOLEAN_T": int i = (bool)value ? 1 : 0; MMALCheck(MMALUtil.mmal_port_parameter_set_boolean(port.Ptr, (uint)key, i), "Unable to set boolean value"); break; case "MMAL_PARAMETER_UINT64_T": MMALCheck(MMALUtil.mmal_port_parameter_set_uint64(port.Ptr, (uint)key, (ulong)value), "Unable to set ulong value"); break; case "MMAL_PARAMETER_INT64_T": MMALCheck(MMALUtil.mmal_port_parameter_set_int64(port.Ptr, (uint)key, (long)value), "Unable to set long value"); break; case "MMAL_PARAMETER_UINT32_T": MMALCheck(MMALUtil.mmal_port_parameter_set_uint32(port.Ptr, (uint)key, (uint)value), "Unable to set uint value"); break; case "MMAL_PARAMETER_INT32_T": MMALCheck(MMALUtil.mmal_port_parameter_set_int32(port.Ptr, (uint)key, (int)value), "Unable to set int value"); break; case "MMAL_PARAMETER_RATIONAL_T": MMALCheck(MMALUtil.mmal_port_parameter_set_rational(port.Ptr, (uint)key, (MMAL_RATIONAL_T)value), "Unable to set rational value"); break; case "MMAL_PARAMETER_STRING_T": MMALCheck(MMALUtil.mmal_port_parameter_set_string(port.Ptr, (uint)key, (string)value), "Unable to set rational value"); break; default: throw new NotSupportedException(); } } catch { MMALLog.Logger.Warn($"Unable to set port parameter {t.ParamName}"); throw; } }
/// <summary> /// Destroy a pool of MMAL_BUFFER_HEADER_T associated with a specific port. This will also deallocate all of the memory /// which was allocated when creating or resizing the pool. /// </summary> internal void DestroyPortPool() { if (this.BufferPool != null) { if (this.Enabled) { this.DisablePort(); } MMALUtil.mmal_port_pool_destroy(this.Ptr, this.BufferPool.Ptr); } }
/// <summary> /// Call to configure changes on an Downstream component output port. /// </summary> /// <param name="outputPort">The output port we are configuring</param> /// <param name="encodingType">The encoding type this output port will send data in</param> /// <param name="pixelFormat">The pixel format this output port will send data in</param> /// <param name="quality">The quality of our outputted data</param> /// <param name="bitrate">The bitrate we are sending data at</param> public virtual unsafe void ConfigureOutputPort(int outputPort, MMALEncoding encodingType, MMALEncoding pixelFormat, int quality, int bitrate = 0) { this.InitialiseOutputPort(outputPort); this.ProcessingPorts.Add(outputPort); this.Inputs[0].ShallowCopy(this.Outputs[outputPort]); if (encodingType != null) { this.Outputs[outputPort].Ptr->Format->encoding = encodingType.EncodingVal; } if (pixelFormat != null) { this.Outputs[outputPort].Ptr->Format->encodingVariant = pixelFormat.EncodingVal; } MMAL_VIDEO_FORMAT_T tempVid = this.Outputs[outputPort].Ptr->Format->es->video; //this.OutputPort.Ptr->Format->es->video.frameRate = new MMAL_RATIONAL_T(0, 1); //this.OutputPort.Ptr->Format->bitrate = bitrate; try { this.Outputs[outputPort].Commit(); } catch { //If commit fails using new settings, attempt to reset using old temp MMAL_VIDEO_FORMAT_T. MMALLog.Logger.Warn("Commit of output port failed. Attempting to reset values."); this.Outputs[outputPort].Ptr->Format->es->video = tempVid; this.Outputs[outputPort].Commit(); } if (encodingType == MMALEncoding.JPEG) { this.Outputs[outputPort].SetParameter(MMALParametersCamera.MMAL_PARAMETER_JPEG_Q_FACTOR, quality); } this.Outputs[outputPort].EncodingType = encodingType; this.Outputs[outputPort].PixelFormat = pixelFormat; this.Outputs[outputPort].Ptr->Format->es->video.height = MMALUtil.VCOS_ALIGN_UP(this.Height, 32); this.Outputs[outputPort].Ptr->Format->es->video.width = MMALUtil.VCOS_ALIGN_UP(this.Width, 32); this.Outputs[outputPort].Ptr->Format->es->video.crop = new MMAL_RECT_T(0, 0, this.Width, this.Height); this.Outputs[outputPort].Ptr->BufferNum = Math.Max(this.Outputs[outputPort].Ptr->BufferNumRecommended, this.Outputs[outputPort].Ptr->BufferNumMin); this.Outputs[outputPort].Ptr->BufferSize = Math.Max(this.Outputs[outputPort].Ptr->BufferSizeRecommended, this.Outputs[outputPort].Ptr->BufferSizeMin); //It is important to re-commit changes to width and height. this.Outputs[outputPort].Commit(); }
/// <inheritdoc /> public virtual void Callback(IBuffer buffer) { if (MMALCameraConfig.Debug) { MMALLog.Logger.LogDebug($"In managed {this.WorkingPort.PortType.GetPortType()} callback"); } long?pts = null; var data = buffer.GetBufferData(); var eos = buffer.AssertProperty(MMALBufferProperties.MMAL_BUFFER_HEADER_FLAG_FRAME_END) || buffer.AssertProperty(MMALBufferProperties.MMAL_BUFFER_HEADER_FLAG_EOS); var containsIFrame = buffer.AssertProperty(MMALBufferProperties.MMAL_BUFFER_HEADER_FLAG_CONFIG); if (this is IVideoOutputCallbackHandler && !buffer.AssertProperty(MMALBufferProperties.MMAL_BUFFER_HEADER_FLAG_CONFIG) && buffer.Pts != MMALUtil.MMAL_TIME_UNKNOWN && (buffer.Pts != _ptsLastTime || !_ptsLastTime.HasValue)) { if (!_ptsStartTime.HasValue) { _ptsStartTime = buffer.Pts; } _ptsLastTime = buffer.Pts; pts = buffer.Pts - _ptsStartTime.Value; } if (MMALCameraConfig.Debug) { MMALLog.Logger.LogDebug("Attempting to process data."); } this.CaptureHandler?.Process(new ImageContext { Data = data, Eos = eos, IFrame = containsIFrame, Resolution = this.WorkingPort.Resolution, Encoding = this.WorkingPort.EncodingType, PixelFormat = this.WorkingPort.PixelFormat, Raw = this.WorkingPort.EncodingType.EncType == MMALEncoding.EncodingType.PixelFormat, Pts = pts, Stride = MMALUtil.mmal_encoding_width_to_stride(WorkingPort.PixelFormat?.EncodingVal ?? this.WorkingPort.EncodingType.EncodingVal, this.WorkingPort.Resolution.Width) }); if (eos) { // Once we have a full frame, perform any post processing as required. this.CaptureHandler?.PostProcess(); } }
internal static bool RgbOrderFixed(this MMALPortImpl port) { int newFirmware = 0; var encodings = port.GetSupportedEncodings(); foreach (int enc in encodings) { if (enc == MMALUtil.MMAL_FOURCC("BGR3")) { break; } if (enc == MMALUtil.MMAL_FOURCC("RGB3")) { newFirmware = 1; } } return(newFirmware == 1); }
/// <summary> /// Pads a <see cref="Resolution"/> object to the desired width/height. /// </summary> /// <param name="width">The width to be padded to.</param> /// <param name="height">The height to be padded to.</param> /// <returns>A new <see cref="Resolution"/> struct, padded to the required width/height.</returns> public Resolution Pad(int width = 32, int height = 16) { return(new Resolution(MMALUtil.VCOS_ALIGN_UP(this.Width, width), MMALUtil.VCOS_ALIGN_UP(this.Height, height))); }
/// <summary> /// Initialises the camera's still port using the width, height and encoding as specified by the user /// </summary> internal void InitialiseStill() { if (MMALCameraConfig.StillResolution.Width == 0 || MMALCameraConfig.StillResolution.Width > this.CameraInfo.MaxWidth) { MMALCameraConfig.StillResolution.Width = this.CameraInfo.MaxWidth; } if (MMALCameraConfig.StillResolution.Height == 0 || MMALCameraConfig.StillResolution.Height > this.CameraInfo.MaxHeight) { MMALCameraConfig.StillResolution.Height = this.CameraInfo.MaxHeight; } var vFormat = new MMAL_VIDEO_FORMAT_T(); if (MMALCameraConfig.StillEncoding == MMALEncoding.RGB32 || MMALCameraConfig.StillEncoding == MMALEncoding.RGB24 || MMALCameraConfig.StillEncoding == MMALEncoding.RGB16) { MMALLog.Logger.Warn("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_RATIONAL_T(0, 1), this.StillPort.Ptr->Format->es->video.par, this.StillPort.Ptr->Format->es->video.colorSpace ); try { if (!this.StillPort.RgbOrderFixed()) { MMALLog.Logger.Warn("Using old firmware. Setting encoding to BGR24"); this.StillPort.Ptr->Format->encoding = MMALEncoding.BGR24.EncodingVal; } } catch { MMALLog.Logger.Warn("Using old firmware. Setting encoding to BGR24"); this.StillPort.Ptr->Format->encoding = MMALEncoding.BGR24.EncodingVal; } this.StillPort.Ptr->Format->encodingVariant = 0; } else { vFormat = new MMAL_VIDEO_FORMAT_T( MMALUtil.VCOS_ALIGN_UP(MMALCameraConfig.StillResolution.Width, 32), MMALUtil.VCOS_ALIGN_UP(MMALCameraConfig.StillResolution.Height, 16), new MMAL_RECT_T(0, 0, MMALCameraConfig.StillResolution.Width, MMALCameraConfig.StillResolution.Height), MMALCameraConfig.StillFramerate, this.StillPort.Ptr->Format->es->video.par, this.StillPort.Ptr->Format->es->video.colorSpace ); this.StillPort.Ptr->Format->encoding = MMALCameraConfig.StillEncoding.EncodingVal; this.StillPort.Ptr->Format->encodingVariant = MMALCameraConfig.StillSubFormat.EncodingVal; } this.StillPort.Ptr->Format->es->video = vFormat; MMALLog.Logger.Debug("Commit still"); this.StillPort.Commit(); this.StillPort.Ptr->BufferNum = Math.Max(this.StillPort.BufferNumRecommended, this.StillPort.BufferNumMin); this.StillPort.Ptr->BufferSize = Math.Max(this.StillPort.BufferSizeRecommended, this.StillPort.BufferSizeMin); }