diff --git a/MMALSharp.Common/Handlers/ICaptureHandler.cs b/MMALSharp.Common/Handlers/ICaptureHandler.cs
new file mode 100644
index 00000000..609f487d
--- /dev/null
+++ b/MMALSharp.Common/Handlers/ICaptureHandler.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MMALSharp.Handlers
+{
+ public interface ICaptureHandler : IDisposable
+ {
+ ///
+ /// Used to process the byte array containing our image data
+ ///
+ /// A byte array containing image data
+ void Process(byte[] data);
+ ///
+ /// Used for any further processing once we have completed capture
+ ///
+ void PostProcess();
+ }
+}
diff --git a/MMALSharp.Common/Handlers/ImageStreamCaptureHandler.cs b/MMALSharp.Common/Handlers/ImageStreamCaptureHandler.cs
new file mode 100644
index 00000000..b1fc9518
--- /dev/null
+++ b/MMALSharp.Common/Handlers/ImageStreamCaptureHandler.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MMALSharp.Handlers
+{
+ public class ImageStreamCaptureHandler : StreamCaptureHandler
+ {
+ public ImageStreamCaptureHandler(string directory, string extension) : base(directory, extension) { }
+ }
+}
diff --git a/MMALSharp/Handlers/ProcessResult.cs b/MMALSharp.Common/Handlers/ProcessResult.cs
similarity index 100%
rename from MMALSharp/Handlers/ProcessResult.cs
rename to MMALSharp.Common/Handlers/ProcessResult.cs
diff --git a/MMALSharp.Common/Handlers/StreamCaptureHandler.cs b/MMALSharp.Common/Handlers/StreamCaptureHandler.cs
new file mode 100644
index 00000000..a6211224
--- /dev/null
+++ b/MMALSharp.Common/Handlers/StreamCaptureHandler.cs
@@ -0,0 +1,86 @@
+using MMALSharp.Utility;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MMALSharp.Handlers
+{
+ ///
+ /// Processes the image data to a stream.
+ ///
+ public abstract class StreamCaptureHandler : ICaptureHandler
+ {
+ protected Stream CurrentStream { get; set; }
+ public List ProcessedStreams { get; set; } = new List();
+ protected int Processed { get; set; }
+ protected string Directory { get; set; }
+ protected string Extension { get; set; }
+
+ public StreamCaptureHandler(string directory, string extension)
+ {
+ this.Directory = directory.TrimEnd('/');
+ this.Extension = extension.TrimStart('.');
+ }
+
+ public void NewFile()
+ {
+ if (this.CurrentStream != null)
+ this.CurrentStream.Dispose();
+ this.CurrentStream = File.Create(this.Directory + "/" + DateTime.Now.ToString("dd-MMM-yy HH-mm-ss") + "." + this.Extension);
+ }
+
+ public void Process(byte[] data)
+ {
+ this.Processed += data.Length;
+
+ if (this.CurrentStream.CanWrite)
+ this.CurrentStream.Write(data, 0, data.Length);
+ else
+ throw new IOException("Stream not writable.");
+
+ }
+
+ public void PostProcess()
+ {
+ this.ProcessedStreams.Add(this.GetDirectory() + "/" + this.GetFilename() + "." + this.GetExtension());
+ Console.WriteLine(string.Format("Successfully processed {0}", Helpers.ConvertBytesToMegabytes(this.Processed)));
+ }
+
+ public string GetDirectory()
+ {
+ if(this.CurrentStream.GetType() == typeof(FileStream))
+ {
+ return Path.GetDirectoryName(((FileStream)this.CurrentStream).Name);
+ }
+ return null;
+ }
+
+ private string GetExtension()
+ {
+ if (this.CurrentStream.GetType() == typeof(FileStream))
+ {
+ return Path.GetExtension(((FileStream)this.CurrentStream).Name);
+ }
+ return null;
+ }
+
+ private string GetFilename()
+ {
+ if (this.CurrentStream.GetType() == typeof(FileStream))
+ {
+ return Path.GetFileNameWithoutExtension(((FileStream)this.CurrentStream).Name);
+ }
+ return null;
+ }
+
+ public void Dispose()
+ {
+ if (this.CurrentStream != null)
+ this.CurrentStream.Dispose();
+ }
+
+ }
+}
diff --git a/MMALSharp.Common/Handlers/VideoStreamCaptureHandler.cs b/MMALSharp.Common/Handlers/VideoStreamCaptureHandler.cs
new file mode 100644
index 00000000..6d18cf13
--- /dev/null
+++ b/MMALSharp.Common/Handlers/VideoStreamCaptureHandler.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MMALSharp.Handlers
+{
+ public class VideoStreamCaptureHandler : StreamCaptureHandler
+ {
+ public VideoStreamCaptureHandler(string directory, string extension) : base(directory, extension) { }
+
+ public bool CanSplit()
+ {
+ if (this.CurrentStream.GetType() == typeof(FileStream))
+ return true;
+ return false;
+ }
+
+ public void Split()
+ {
+ if (this.CanSplit())
+ {
+ this.NewFile();
+ }
+ }
+ }
+}
diff --git a/MMALSharp.Common/MMALSharp.Common.csproj b/MMALSharp.Common/MMALSharp.Common.csproj
new file mode 100644
index 00000000..57f88089
--- /dev/null
+++ b/MMALSharp.Common/MMALSharp.Common.csproj
@@ -0,0 +1,59 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {D8CA0BC9-CA3B-4EC1-9898-542A6F5346F8}
+ Library
+ Properties
+ MMALSharp.Common
+ MMALSharp.Common
+ v4.5.2
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MMALSharp.Common/Properties/AssemblyInfo.cs b/MMALSharp.Common/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..abd49325
--- /dev/null
+++ b/MMALSharp.Common/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MMALSharp-Common")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MMALSharp-Common")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("d8ca0bc9-ca3b-4ec1-9898-542a6f5346f8")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/MMALSharp/Utility/Helpers.cs b/MMALSharp.Common/Utility/Helpers.cs
similarity index 100%
rename from MMALSharp/Utility/Helpers.cs
rename to MMALSharp.Common/Utility/Helpers.cs
diff --git a/MMALSharp/Handlers/FFmpegCaptureHandler.cs b/MMALSharp.FFmpeg/Handlers/FFmpegCaptureHandler.cs
similarity index 92%
rename from MMALSharp/Handlers/FFmpegCaptureHandler.cs
rename to MMALSharp.FFmpeg/Handlers/FFmpegCaptureHandler.cs
index 7da86851..69035012 100644
--- a/MMALSharp/Handlers/FFmpegCaptureHandler.cs
+++ b/MMALSharp.FFmpeg/Handlers/FFmpegCaptureHandler.cs
@@ -8,6 +8,9 @@
namespace MMALSharp.Handlers
{
+ ///
+ /// Currently experimental. Not working fully.
+ ///
public class FFmpegCaptureHandler : ICaptureHandler
{
public Process MyProcess { get; set; }
@@ -35,7 +38,7 @@ public FFmpegCaptureHandler(string streamName, string streamUrl)
public bool CanSplit()
{
- throw new NotImplementedException();
+ return false;
}
public string GetDirectory()
@@ -67,9 +70,10 @@ public void Split()
throw new NotImplementedException();
}
- ~FFmpegCaptureHandler()
+ public void Dispose()
{
this.MyProcess.Close();
}
+
}
}
diff --git a/MMALSharp.FFmpeg/MMALSharp.FFmpeg.csproj b/MMALSharp.FFmpeg/MMALSharp.FFmpeg.csproj
new file mode 100644
index 00000000..6ee7799a
--- /dev/null
+++ b/MMALSharp.FFmpeg/MMALSharp.FFmpeg.csproj
@@ -0,0 +1,61 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {8F2E7EDB-4533-4FC5-A8AA-17F11302CC84}
+ Library
+ Properties
+ MMALSharp.FFmpeg
+ MMALSharp.FFmpeg
+ v4.5.2
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {d8ca0bc9-ca3b-4ec1-9898-542a6f5346f8}
+ MMALSharp.Common
+
+
+
+
+
\ No newline at end of file
diff --git a/MMALSharp.FFmpeg/Properties/AssemblyInfo.cs b/MMALSharp.FFmpeg/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..e1ffff0a
--- /dev/null
+++ b/MMALSharp.FFmpeg/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MMALSharp-FFmpeg")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MMALSharp-FFmpeg")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("8f2e7edb-4533-4fc5-a8aa-17f11302cc84")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/MMALSharp.FFmpeg/VideoUtilities.cs b/MMALSharp.FFmpeg/VideoUtilities.cs
new file mode 100644
index 00000000..a6ddbe01
--- /dev/null
+++ b/MMALSharp.FFmpeg/VideoUtilities.cs
@@ -0,0 +1,40 @@
+using MMALSharp.Handlers;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MMALSharp.FFmpeg
+{
+ public static class VideoUtilities
+ {
+ ///
+ /// Useful for Timelapse captures. Enables you to convert a list of images associated with an ImageStreamCaptureHandler to a video
+ ///
+ ///
+ ///
+ public static void ImagesToVideo(this ImageStreamCaptureHandler result, string targetDirectory, int fps)
+ {
+ var process = new Process();
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.CreateNoWindow = true;
+ process.StartInfo.FileName = "ffmpeg";
+
+ StringBuilder sb = new StringBuilder();
+ result.ProcessedStreams.ForEach(c => sb.Append(string.Format("-i {0}", c)));
+ targetDirectory.TrimEnd('/');
+
+ if(fps == 0)
+ {
+ //Default to 25fps - FFmpeg defaults to this value if nothing is specified
+ fps = 25;
+ }
+
+ process.StartInfo.Arguments = string.Format("-framerate {0} {1} {2}/out.avi", fps, sb.ToString(), targetDirectory);
+ process.Start();
+ process.WaitForExit();
+ }
+ }
+}
diff --git a/MMALSharp.sln b/MMALSharp.sln
index 51e8bacc..1034a261 100644
--- a/MMALSharp.sln
+++ b/MMALSharp.sln
@@ -4,8 +4,22 @@ Microsoft Visual Studio Solution File, Format Version 12.00
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MMALSharp", "MMALSharp\MMALSharp.csproj", "{5B03E4B4-13AA-4EC8-8FD3-9DAA176EAA70}"
+ ProjectSection(ProjectDependencies) = postProject
+ {D8CA0BC9-CA3B-4EC1-9898-542A6F5346F8} = {D8CA0BC9-CA3B-4EC1-9898-542A6F5346F8}
+ {8F2E7EDB-4533-4FC5-A8AA-17F11302CC84} = {8F2E7EDB-4533-4FC5-A8AA-17F11302CC84}
+ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MMALSharpExample", "MMALSharpExample\MMALSharpExample.csproj", "{665C0F20-844A-44A8-9EF4-F359A607CAE6}"
+ ProjectSection(ProjectDependencies) = postProject
+ {D8CA0BC9-CA3B-4EC1-9898-542A6F5346F8} = {D8CA0BC9-CA3B-4EC1-9898-542A6F5346F8}
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MMALSharp.FFmpeg", "MMALSharp.FFmpeg\MMALSharp.FFmpeg.csproj", "{8F2E7EDB-4533-4FC5-A8AA-17F11302CC84}"
+ ProjectSection(ProjectDependencies) = postProject
+ {D8CA0BC9-CA3B-4EC1-9898-542A6F5346F8} = {D8CA0BC9-CA3B-4EC1-9898-542A6F5346F8}
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MMALSharp.Common", "MMALSharp.Common\MMALSharp.Common.csproj", "{D8CA0BC9-CA3B-4EC1-9898-542A6F5346F8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -21,6 +35,14 @@ Global
{665C0F20-844A-44A8-9EF4-F359A607CAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{665C0F20-844A-44A8-9EF4-F359A607CAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{665C0F20-844A-44A8-9EF4-F359A607CAE6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8F2E7EDB-4533-4FC5-A8AA-17F11302CC84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8F2E7EDB-4533-4FC5-A8AA-17F11302CC84}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8F2E7EDB-4533-4FC5-A8AA-17F11302CC84}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8F2E7EDB-4533-4FC5-A8AA-17F11302CC84}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D8CA0BC9-CA3B-4EC1-9898-542A6F5346F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D8CA0BC9-CA3B-4EC1-9898-542A6F5346F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D8CA0BC9-CA3B-4EC1-9898-542A6F5346F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D8CA0BC9-CA3B-4EC1-9898-542A6F5346F8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/MMALSharp/Components/MMALDownstreamComponent.cs b/MMALSharp/Components/MMALDownstreamComponent.cs
index 5f2f9d55..0b67a6ba 100644
--- a/MMALSharp/Components/MMALDownstreamComponent.cs
+++ b/MMALSharp/Components/MMALDownstreamComponent.cs
@@ -26,8 +26,9 @@ public abstract unsafe class MMALDownstreamComponent : MMALComponentBase
///
public ICaptureHandler Handler { get; set; }
- public MMALDownstreamComponent(string name) : base(name)
- {
+ public MMALDownstreamComponent(string name, ICaptureHandler handler) : base(name)
+ {
+ this.Handler = handler;
}
///
@@ -49,24 +50,51 @@ public void CreateConnection(MMALPortBase output)
public virtual void ManagedCallback(MMALBufferImpl buffer, MMALPortBase port)
{
var data = buffer.GetBufferData();
- this.Handler.Process(data);
+
+ if(this.Handler != null)
+ {
+ this.Handler.Process(data);
+ }
}
+ ///
+ /// Enable the port with the specified port number.
+ ///
+ /// The output port number
+ /// The managed method to callback to from the native callback
public void Start(int outputPortNumber, Action managedCallback)
{
+ if(this.Handler != null && this.Handler.GetType() == typeof(StreamCaptureHandler))
+ {
+ ((StreamCaptureHandler)this.Handler).NewFile();
+ }
+
this.Outputs.ElementAt(outputPortNumber).EnablePort(managedCallback);
}
+ ///
+ /// Enable the port specified.
+ ///
+ /// The output port
+ /// The managed method to callback to from the native callback
public void Start(MMALPortBase port, Action managedCallback)
{
port.EnablePort(managedCallback);
}
+ ///
+ /// Disable the port with the specified port number
+ ///
+ /// The output port number
public void Stop(int outputPortNumber)
{
this.Outputs.ElementAt(outputPortNumber).DisablePort();
}
+ ///
+ /// Disable the specified port
+ ///
+ /// The output port
public void Stop(MMALPortBase port)
{
port.DisablePort();
diff --git a/MMALSharp/Components/MMALEncoderComponent.cs b/MMALSharp/Components/MMALEncoderComponent.cs
index e8125baa..6c035d3d 100644
--- a/MMALSharp/Components/MMALEncoderComponent.cs
+++ b/MMALSharp/Components/MMALEncoderComponent.cs
@@ -18,7 +18,7 @@ namespace MMALSharp.Components
///
public abstract unsafe class MMALEncoderBase : MMALDownstreamComponent
{
- protected MMALEncoderBase(string encoderName) : base(encoderName) { }
+ protected MMALEncoderBase(string encoderName, ICaptureHandler handler) : base(encoderName, handler) { }
///
/// Initializes the encoder component to allow processing to commence. Creates the same format between input/output port.
@@ -159,7 +159,19 @@ internal void CleanEncoderPorts()
}
public override void Dispose()
- {
+ {
+ if (MMALCameraConfig.Debug)
+ {
+ Console.WriteLine("Removing encoder");
+ }
+
+ this.Connection.Destroy();
+
+ MMALCamera.Instance.Encoders.Remove(this);
+
+ //Remove any unmanaged resources held by the capture handler.
+ this.Handler.Dispose();
+
base.Dispose();
}
}
@@ -210,7 +222,7 @@ public unsafe class MMALVideoEncoder : MMALEncoderBase
///
public bool PrepareSplit { get; set; }
- public MMALVideoEncoder(MMALEncoding encodingType, int bitrate, int framerate, int quality) : base(MMALParameters.MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER)
+ public MMALVideoEncoder(ICaptureHandler handler, MMALEncoding encodingType, int bitrate, int framerate, int quality) : base(MMALParameters.MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, handler)
{
if (encodingType.EncodingVal > 0)
{
@@ -230,7 +242,7 @@ public MMALVideoEncoder(MMALEncoding encodingType, int bitrate, int framerate, i
this.Initialize();
}
- public MMALVideoEncoder(int quality) : base(MMALParameters.MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER)
+ public MMALVideoEncoder(ICaptureHandler handler, int quality) : base(MMALParameters.MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER, handler)
{
this.Initialize();
}
@@ -332,12 +344,10 @@ public override void Initialize()
/// The buffer header we're currently processing
/// The port we're currently processing on
public override void ManagedCallback(MMALBufferImpl buffer, MMALPortBase port)
- {
- var data = buffer.GetBufferData();
-
+ {
if (this.PrepareSplit && buffer.Properties.Any(c => c == MMALBufferProperties.MMAL_BUFFER_HEADER_FLAG_CONFIG))
{
- this.Handler.Split();
+ ((VideoStreamCaptureHandler)this.Handler).Split();
this.LastSplit = DateTime.Now;
this.PrepareSplit = false;
}
@@ -357,7 +367,7 @@ public override void ManagedCallback(MMALBufferImpl buffer, MMALPortBase port)
}
}
- this.Handler.Process(data);
+ base.ManagedCallback(buffer, port);
}
internal void ConfigureRateControl()
@@ -478,7 +488,7 @@ private DateTime CalculateSplit()
///
public unsafe class MMALVideoDecoder : MMALEncoderBase
{
- public MMALVideoDecoder() : base(MMALParameters.MMAL_COMPONENT_DEFAULT_VIDEO_DECODER)
+ public MMALVideoDecoder(ICaptureHandler handler) : base(MMALParameters.MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, handler)
{
this.Initialize();
}
@@ -507,7 +517,7 @@ public unsafe class MMALImageEncoder : MMALEncoderBase
///
public int Quality { get; set; } = 90;
- public MMALImageEncoder(MMALEncoding encodingType, int quality) : base(MMALParameters.MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER)
+ public MMALImageEncoder(ICaptureHandler handler, MMALEncoding encodingType, int quality) : base(MMALParameters.MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, handler)
{
if (encodingType.EncodingVal > 0)
{
@@ -522,7 +532,7 @@ public MMALImageEncoder(MMALEncoding encodingType, int quality) : base(MMALParam
this.Initialize();
}
- public MMALImageEncoder() : base(MMALParameters.MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER)
+ public MMALImageEncoder(ICaptureHandler handler) : base(MMALParameters.MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, handler)
{
this.Initialize();
}
@@ -628,7 +638,7 @@ internal unsafe void AddExifTag(ExifTag exifTag)
///
public unsafe class MMALImageDecoder : MMALEncoderBase
{
- public MMALImageDecoder() : base(MMALParameters.MMAL_COMPONENT_DEFAULT_IMAGE_DECODER)
+ public MMALImageDecoder(ICaptureHandler handler) : base(MMALParameters.MMAL_COMPONENT_DEFAULT_IMAGE_DECODER, handler)
{
this.Initialize();
}
diff --git a/MMALSharp/Components/MMALRendererComponent.cs b/MMALSharp/Components/MMALRendererComponent.cs
index 53d83717..dc0cec68 100644
--- a/MMALSharp/Components/MMALRendererComponent.cs
+++ b/MMALSharp/Components/MMALRendererComponent.cs
@@ -12,7 +12,7 @@ namespace MMALSharp.Components
///
public unsafe abstract class MMALRendererBase : MMALDownstreamComponent
{
- public MMALRendererBase(string name) : base(name)
+ public MMALRendererBase(string name) : base(name, null)
{
if (this.Ptr->InputNum > 0)
{
diff --git a/MMALSharp/Components/MMALSplitterComponent.cs b/MMALSharp/Components/MMALSplitterComponent.cs
index 73316e84..5edb5d4f 100644
--- a/MMALSharp/Components/MMALSplitterComponent.cs
+++ b/MMALSharp/Components/MMALSplitterComponent.cs
@@ -9,7 +9,7 @@ namespace MMALSharp.Components
{
public class MMALSplitterComponent : MMALDownstreamComponent
{
- public MMALSplitterComponent() : base(MMALParameters.MMAL_COMPONENT_DEFAULT_VIDEO_SPLITTER)
+ public MMALSplitterComponent() : base(MMALParameters.MMAL_COMPONENT_DEFAULT_VIDEO_SPLITTER, null)
{
}
diff --git a/MMALSharp/Handlers/ICaptureHandler.cs b/MMALSharp/Handlers/ICaptureHandler.cs
deleted file mode 100644
index c549ede4..00000000
--- a/MMALSharp/Handlers/ICaptureHandler.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace MMALSharp.Handlers
-{
- public interface ICaptureHandler
- {
- ///
- /// Used to process the byte array containing our image data
- ///
- /// A byte array containing image data
- void Process(byte[] data);
- ///
- /// Indicates whether segmented video recording is possible with this handler
- ///
- ///
- bool CanSplit();
- ///
- /// Used to handle segmented video recording
- ///
- void Split();
- ///
- /// Gets the directory of a stream based handler
- ///
- ///
- string GetDirectory();
- ///
- /// Used for any further processing once we have completed capture
- ///
- void PostProcess();
- }
-}
diff --git a/MMALSharp/Handlers/StreamCaptureResult.cs b/MMALSharp/Handlers/StreamCaptureResult.cs
deleted file mode 100644
index 7601d082..00000000
--- a/MMALSharp/Handlers/StreamCaptureResult.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-using MMALSharp.Utility;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace MMALSharp.Handlers
-{
- ///
- /// Processes the image data to a stream.
- ///
- public class StreamCaptureResult : ICaptureHandler
- {
- private Stream _stream;
- private int _processed;
- private string _origFilename;
-
- public StreamCaptureResult(Stream stream)
- {
- this._stream = stream;
- this._origFilename = this.GetFilename();
- }
-
- public void Process(byte[] data)
- {
- this._processed += data.Length;
-
- if (this._stream.CanWrite)
- this._stream.Write(data, 0, data.Length);
- else
- throw new PiCameraError("Stream not writable.");
-
- if (MMALCameraConfig.Debug)
- Console.WriteLine("Currently processed: " + Helpers.ConvertBytesToMegabytes(this._processed));
- }
-
- public void PostProcess()
- {
- Console.WriteLine(string.Format("Successfully processed {0}", Helpers.ConvertBytesToMegabytes(this._processed)));
- }
-
- public bool CanSplit()
- {
- if(this._stream.GetType() == typeof(FileStream))
- return true;
- return false;
- }
-
- public void Split()
- {
- if(this.CanSplit())
- {
- this._stream.Dispose();
- this._stream = File.Create(this.GetDirectory() + "/" + this._origFilename + " " + DateTime.Now.ToString("dd-MMM-yy HH-mm-ss") + this.GetExtension());
- }
- }
-
- public string GetDirectory()
- {
- if(this._stream.GetType() == typeof(FileStream))
- {
- return Path.GetDirectoryName(((FileStream)this._stream).Name);
- }
- return null;
- }
-
- private string GetExtension()
- {
- if (this._stream.GetType() == typeof(FileStream))
- {
- return Path.GetExtension(((FileStream)this._stream).Name);
- }
- return null;
- }
-
- private string GetFilename()
- {
- if (this._stream.GetType() == typeof(FileStream))
- {
- return Path.GetFileNameWithoutExtension(((FileStream)this._stream).Name);
- }
- return null;
- }
-
- ~StreamCaptureResult()
- {
- if(this._stream != null)
- this._stream.Dispose();
- }
-
- }
-}
diff --git a/MMALSharp/MMALCamera.cs b/MMALSharp/MMALCamera.cs
index f8fe3478..90876885 100644
--- a/MMALSharp/MMALCamera.cs
+++ b/MMALSharp/MMALCamera.cs
@@ -1,14 +1,9 @@
using MMALSharp.Components;
-using MMALSharp.Handlers;
using MMALSharp.Native;
using MMALSharp.Utility;
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading;
using System.Threading.Tasks;
namespace MMALSharp
@@ -16,7 +11,7 @@ namespace MMALSharp
///
/// This class provides an interface to the Raspberry Pi camera module.
///
- public sealed class MMALCamera : IDisposable
+ public sealed class MMALCamera
{
///
/// Reference to the camera component
@@ -38,14 +33,10 @@ public sealed class MMALCamera : IDisposable
///
public MMALSplitterComponent Splitter { get; set; }
- private static readonly MMALCamera instance = new MMALCamera();
-
- // Explicit static constructor to tell C# compiler
- // not to mark type as beforefieldinit
- static MMALCamera()
- {
- }
+ private static readonly Lazy lazy = new Lazy(() => new MMALCamera());
+ public static MMALCamera Instance { get { return lazy.Value; } }
+
private MMALCamera()
{
BcmHost.bcm_host_init();
@@ -54,18 +45,10 @@ private MMALCamera()
this.Encoders = new List();
}
- public static MMALCamera Instance
- {
- get
- {
- return instance;
- }
- }
-
///
/// Begin capture on one of the camera's output ports.
///
- ///
+ /// An output port of the camera component
public void StartCapture(MMALPortImpl port)
{
if (port == this.Camera.StillPort || this.Encoders.Any(c => c.Enabled))
@@ -75,7 +58,7 @@ public void StartCapture(MMALPortImpl port)
///
/// Stop capture on one of the camera's output ports
///
- ///
+ /// An output port of the camera component
public void StopCapture(MMALPortImpl port)
{
if (port == this.Camera.StillPort || this.Encoders.Any(c => c.Enabled))
@@ -99,13 +82,8 @@ public void ForceStop(MMALPortImpl port)
/// A timeout to stop the video capture
/// Used for Segmented video mode
///
- public async Task TakeVideo(MMALPortImpl connPort, ICaptureHandler handler, DateTime? timeout = null, Split split = null)
- {
- if (handler == null)
- {
- throw new PiCameraError("Handler cannot be null");
- }
-
+ public async Task TakeVideo(MMALPortImpl connPort, DateTime? timeout = null, Split split = null)
+ {
var encoder = this.Encoders.Where(c => c.Connection != null && c.Connection.OutputPort == connPort).FirstOrDefault();
if (encoder == null || encoder.GetType() != typeof(MMALVideoEncoder))
@@ -113,8 +91,6 @@ public async Task TakeVideo(MMALPortImpl connPort, ICaptureHandler handler, Date
throw new PiCameraError("No video encoder currently attached to output port specified");
}
- encoder.Handler = handler;
-
if (!encoder.Connection.Enabled)
{
encoder.Connection.Enable();
@@ -139,124 +115,36 @@ public async Task TakeVideo(MMALPortImpl connPort, ICaptureHandler handler, Date
var outputPort = 0;
- //Enable the video encoder output port.
- encoder.Start(outputPort, encoder.ManagedCallback);
-
- encoder.Outputs.ElementAt(outputPort).Trigger = new Nito.AsyncEx.AsyncCountdownEvent(1);
-
- ((MMALVideoPort)encoder.Outputs.ElementAt(outputPort)).Timeout = timeout;
- ((MMALVideoEncoder)encoder).Split = split;
+ try
+ {
+ //Enable the video encoder output port.
+ encoder.Start(outputPort, encoder.ManagedCallback);
- this.StartCapture(this.Camera.VideoPort);
-
- await encoder.Outputs.ElementAt(outputPort).Trigger.WaitAsync();
-
- //Wait until the process is complete.
- this.StopCapture(this.Camera.VideoPort);
+ encoder.Outputs.ElementAt(outputPort).Trigger = new Nito.AsyncEx.AsyncCountdownEvent(1);
- //Disable the image encoder output port.
- encoder.Stop(outputPort);
+ ((MMALVideoPort)encoder.Outputs.ElementAt(outputPort)).Timeout = timeout;
+ ((MMALVideoEncoder)encoder).Split = split;
- //Close open connections.
- encoder.Connection.Disable();
- encoder.CleanEncoderPorts();
+ this.StartCapture(this.Camera.VideoPort);
- encoder.Handler.PostProcess();
- }
+ await encoder.Outputs.ElementAt(outputPort).Trigger.WaitAsync();
- ///
- /// Captures a single image from the camera's still port.
- /// Initializes a standalone MMALImageEncoder using the provided encodingType and quality.
- ///
- /// The capture handler for this capture method
- /// The image encoding type
- /// Quality of the image (valid only for JPEG)
- /// Include raw bayer metadeta in the capture
- /// Specify whether to include EXIF tags in the capture
- /// Custom EXIF tags to use in the capture
- ///
- public async Task TakeSinglePicture(ICaptureHandler handler, MMALEncoding encodingType, int quality = 0, bool raw = false, bool useExif = true, params ExifTag[] exifTags)
- {
- var camPreviewPort = this.Camera.PreviewPort;
- var camVideoPort = this.Camera.VideoPort;
- var camStillPort = this.Camera.StillPort;
+ //Wait until the process is complete.
+ this.StopCapture(this.Camera.VideoPort);
- if (handler == null)
- {
- throw new PiCameraError("Handler cannot be null");
- }
+ //Disable the image encoder output port.
+ encoder.Stop(outputPort);
- if (this.Encoders.Any(c => c.Connection != null && c.Connection.OutputPort == this.Camera.StillPort && c.GetType() == typeof(MMALImageEncoder)))
- {
- //Reuse if an Image encoder is already connected to the Still camera port
- await TakePicture(this.Camera.StillPort, this.Camera.StillPort, handler, useExif, raw, exifTags);
+ //Close open connections.
+ encoder.Connection.Disable();
+ encoder.CleanEncoderPorts();
}
- else
+ finally
{
- if(encodingType == null)
- {
- throw new PiCameraError("Encoding type cannot be null");
- }
-
- Console.WriteLine("Preparing to take picture");
- using (var encoder = new MMALImageEncoder(encodingType, quality))
- {
- if (useExif)
- {
- ((MMALImageEncoder)encoder).AddExifTags(exifTags);
- }
-
- //Create connections
- if (this.Preview == null)
- {
- Helpers.PrintWarning("Preview port does not have a Render component configured. Resulting image will be affected.");
- }
- else
- {
- if (this.Preview.Connection == null)
- {
- this.Preview.CreateConnection(camPreviewPort);
- }
- }
-
- encoder.Handler = handler;
-
- encoder.CreateConnection(camStillPort);
-
- if (raw)
- {
- camStillPort.SetRawCapture(true);
- }
-
- if (MMALCameraConfig.EnableAnnotate)
- {
- encoder.AnnotateImage();
- }
-
- int outputPort = 0;
-
- //Enable the image encoder output port.
- encoder.Start(outputPort, encoder.ManagedCallback);
-
- this.StartCapture(camStillPort);
-
- //Wait until the process is complete.
- encoder.Outputs.ElementAt(outputPort).Trigger = new Nito.AsyncEx.AsyncCountdownEvent(1);
- await encoder.Outputs.ElementAt(outputPort).Trigger.WaitAsync();
-
- this.StopCapture(camStillPort);
-
- //Disable the image encoder output port.
- encoder.Stop(outputPort);
-
- //Close open connections.
- encoder.Connection.Destroy();
-
- encoder.Handler.PostProcess();
- }
- }
+ encoder.Handler.PostProcess();
+ }
}
-
+
///
/// Captures a single image from the output port specified. Expects an MMALImageEncoder to be attached.
///
@@ -267,23 +155,18 @@ public async Task TakeSinglePicture(ICaptureHandler handler, MMALEncoding encodi
/// Specify whether to include EXIF tags in the capture
/// Custom EXIF tags to use in the capture
///
- public async Task TakePicture(MMALPortImpl cameraPort, MMALPortImpl connPort, ICaptureHandler handler, bool raw = false, bool useExif = true, params ExifTag[] exifTags)
+ public async Task TakePicture(MMALPortImpl cameraPort, MMALPortImpl connPort, bool raw = false, bool useExif = true, params ExifTag[] exifTags)
{
Console.WriteLine("Preparing to take picture");
//Find the encoder/decoder which is connected to the output port specified.
var encoder = this.Encoders.Where(c => c.Connection != null && c.Connection.OutputPort == connPort).FirstOrDefault();
-
+
if (encoder == null || encoder.GetType() != typeof(MMALImageEncoder))
- throw new PiCameraError("No image encoder currently attached to output port specified");
-
- if (handler == null)
{
- throw new PiCameraError("Handler cannot be null");
+ throw new PiCameraError("No image encoder currently attached to output port specified");
}
-
- encoder.Handler = handler;
-
+
if (!encoder.Connection.Enabled)
{
encoder.Connection.Enable();
@@ -317,105 +200,68 @@ public async Task TakePicture(MMALPortImpl cameraPort, MMALPortImpl connPort, IC
var outputPort = 0;
- //Enable the image encoder output port.
- encoder.Start(outputPort, encoder.ManagedCallback);
-
- this.StartCapture(cameraPort);
-
- //Wait until the process is complete.
- encoder.Outputs.ElementAt(outputPort).Trigger = new Nito.AsyncEx.AsyncCountdownEvent(1);
- await encoder.Outputs.ElementAt(outputPort).Trigger.WaitAsync();
+ //Enable the image encoder output port.
+ try
+ {
+ encoder.Start(outputPort, encoder.ManagedCallback);
- this.StopCapture(cameraPort);
+ this.StartCapture(cameraPort);
- //Disable the image encoder output port.
- encoder.Stop(outputPort);
+ //Wait until the process is complete.
+ encoder.Outputs.ElementAt(outputPort).Trigger = new Nito.AsyncEx.AsyncCountdownEvent(1);
+ await encoder.Outputs.ElementAt(outputPort).Trigger.WaitAsync();
- //Close open connections.
- encoder.Connection.Disable();
- encoder.CleanEncoderPorts();
+ this.StopCapture(cameraPort);
- encoder.Handler.PostProcess();
- }
+ //Disable the image encoder output port.
+ encoder.Stop(outputPort);
- ///
- /// Takes a number of images as specified by the iterations parameter.
- ///
- /// The directory to save our image
- /// The file extension of our image
- /// Number of pictures to take
- /// The encoding type to use
- /// The quality of the image (Valid for JPEG)
- /// Include raw bayer metadeta in the capture
- /// Specify whether to include EXIF tags in the capture
- /// Custom EXIF tags to use in the capture
- public async Task TakePictureIterative(string directory, string extension, int iterations, MMALEncoding encodingType, int quality = 0, bool raw = false, bool useExif = true, params ExifTag[] exifTags)
- {
- if(encodingType == null)
- {
- throw new PiCameraError("Encoding type cannot be null");
+ //Close open connections.
+ encoder.Connection.Disable();
+ encoder.CleanEncoderPorts();
}
-
- for (int i = 0; i < iterations; i++)
+ finally
{
- var filename = (directory.EndsWith("/") ? directory : directory + "/") + DateTime.Now.ToString("dd-MMM-yy HH-mm-ss") + (extension.StartsWith(".") ? extension : "." + extension);
-
- await TakeSinglePicture(new StreamCaptureResult(File.Create(filename)), encodingType, quality, raw, useExif, exifTags);
- }
+ encoder.Handler.PostProcess();
+ }
}
-
+
///
/// Takes images until the moment specified in the timeout parameter has been met.
///
- /// The directory to save our image
- /// The file extension of our image
- /// Take images until this timeout is hit
- /// The encoding type to use
- /// The quality of the image (Valid for JPEG)
+ /// The port that is currently capturing images (Still or Video)
+ /// The port our encoder is attached to
+ /// Take images until this timeout is hit
/// Include raw bayer metadeta in the capture
/// Specify whether to include EXIF tags in the capture
/// Custom EXIF tags to use in the capture
- public async Task TakePictureTimeout(string directory, string extension, DateTime timeout, MMALEncoding encodingType, int quality = 0, bool raw = false, bool useExif = true, params ExifTag[] exifTags)
- {
- if (encodingType == null)
- {
- throw new PiCameraError("Encoding type cannot be null");
- }
-
+ public async Task TakePictureTimeout(MMALPortImpl cameraPort, MMALPortImpl connPort, DateTime timeout, bool raw = false, bool useExif = true, params ExifTag[] exifTags)
+ {
while (DateTime.Now.CompareTo(timeout) < 0)
- {
- var filename = (directory.EndsWith("/") ? directory : directory + "/") + DateTime.Now.ToString("dd-MMM-yy HH-mm-ss") + (extension.StartsWith(".") ? extension : "." + extension);
-
- await TakeSinglePicture(new StreamCaptureResult(File.Create(filename)), encodingType, quality, raw, useExif, exifTags);
+ {
+ await TakePicture(cameraPort, connPort, raw, useExif, exifTags);
}
}
///
/// Takes a timelapse image. You can specify the interval between each image taken and also when the operation should finish.
///
- /// The directory to save our image
- /// The file extension of our image
- /// Specifies settings for the Timelapse
- /// The encoding type to use
- /// The quality of the image (Valid for JPEG)
+ /// The port that is currently capturing images (Still or Video)
+ /// The port our encoder is attached to
+ /// Specifies settings for the Timelapse
/// Include raw bayer metadeta in the capture
/// Specify whether to include EXIF tags in the capture
/// Custom EXIF tags to use in the capture
///
- public async Task TakePictureTimelapse(string directory, string extension, Timelapse tl, MMALEncoding encodingType, int quality = 0, bool raw = false, bool useExif = true, params ExifTag[] exifTags)
- {
- if (encodingType == null)
- {
- throw new PiCameraError("Encoding type cannot be null");
- }
-
+ public async Task TakePictureTimelapse(MMALPortImpl cameraPort, MMALPortImpl connPort, Timelapse tl, bool raw = false, bool useExif = true, params ExifTag[] exifTags)
+ {
int interval = 0;
if(tl == null)
{
throw new PiCameraError("Timelapse object null. This must be initialized for Timelapse mode");
}
-
+
while (DateTime.Now.CompareTo(tl.Timeout) < 0)
{
switch (tl.Mode)
@@ -432,10 +278,8 @@ public async Task TakePictureTimelapse(string directory, string extension, Timel
}
await Task.Delay(interval);
-
- var filename = (directory.EndsWith("/") ? directory : directory + "/") + DateTime.Now.ToString("dd-MMM-yy HH-mm-ss") + (extension.StartsWith(".") ? extension : "." + extension);
-
- await TakeSinglePicture(new StreamCaptureResult(File.Create(filename)), encodingType, quality, raw, useExif, exifTags);
+
+ await TakePicture(cameraPort, connPort, raw, useExif, exifTags);
}
}
@@ -474,12 +318,12 @@ public MMALCamera CreateSplitterComponent()
/// The output port to attach to
///
public MMALCamera AddEncoder(MMALEncoderBase encoder, MMALPortImpl outputPort)
- {
+ {
if (MMALCameraConfig.Debug)
{
Console.WriteLine("Adding encoder");
}
-
+
this.RemoveEncoder(outputPort);
encoder.CreateConnection(outputPort);
@@ -569,31 +413,32 @@ public MMALCamera ConfigureCamera()
return this;
}
-
- public void Dispose()
+
+ ///
+ /// Cleans up any unmanaged resources. It is intended for this method to be run when no more activity is to be done on the camera.
+ ///
+ public void Cleanup()
{
if (MMALCameraConfig.Debug)
{
Console.WriteLine("Destroying final components");
}
-
+
this.Encoders.ForEach(c => c.Dispose());
if (this.Preview != null)
{
this.Preview.Dispose();
}
-
+
if (this.Camera != null)
{
this.Camera.Dispose();
}
-
+
BcmHost.bcm_host_deinit();
}
+
}
-
-
-
-
+
}
diff --git a/MMALSharp/MMALSharp.csproj b/MMALSharp/MMALSharp.csproj
index 1b594ba5..67fb0b3a 100644
--- a/MMALSharp/MMALSharp.csproj
+++ b/MMALSharp/MMALSharp.csproj
@@ -54,15 +54,11 @@
-
-
-
-
-
+
@@ -87,13 +83,22 @@
-
Designer
+
+
+ {d8ca0bc9-ca3b-4ec1-9898-542a6f5346f8}
+ MMALSharp.Common
+
+
+ {8f2e7edb-4533-4fc5-a8aa-17f11302cc84}
+ MMALSharp.FFmpeg
+
+