diff --git a/Samples/Video/Player/MainForm.cs b/Samples/Video/Player/MainForm.cs index 1d6c69d4..70ba1beb 100644 --- a/Samples/Video/Player/MainForm.cs +++ b/Samples/Video/Player/MainForm.cs @@ -128,7 +128,7 @@ private void OpenVideoSource( IVideoSource source ) CloseCurrentVideoSource( ); // start new video source - videoSourcePlayer.VideoSource = source; + videoSourcePlayer.VideoSource = new AsyncVideoSource( source ); videoSourcePlayer.Start( ); // reset stop watch diff --git a/Sources/Genetic/Chromosomes/ChromosomeBase.cs b/Sources/Genetic/Chromosomes/ChromosomeBase.cs index 97fe1fb6..d989a50e 100644 --- a/Sources/Genetic/Chromosomes/ChromosomeBase.cs +++ b/Sources/Genetic/Chromosomes/ChromosomeBase.cs @@ -20,12 +20,12 @@ namespace AForge.Genetic public abstract class ChromosomeBase : IChromosome { /// - /// Chromosome's fintess value. + /// Chromosome's fitness value. /// protected double fitness = 0; /// - /// Chromosome's fintess value. + /// Chromosome's fitness value. /// /// /// Fitness value (usefulness) of the chromosome calculate by calling diff --git a/Sources/Genetic/Chromosomes/DoubleArrayChromosome.cs b/Sources/Genetic/Chromosomes/DoubleArrayChromosome.cs index 660ed483..67341438 100644 --- a/Sources/Genetic/Chromosomes/DoubleArrayChromosome.cs +++ b/Sources/Genetic/Chromosomes/DoubleArrayChromosome.cs @@ -112,7 +112,7 @@ public double[] Value /// /// /// The property controls type of mutation, which is used more - /// frequently. A radnom number is generated each time before doing mutation - + /// frequently. A random number is generated each time before doing mutation - /// if the random number is smaller than the specified balance value, then one /// mutation type is used, otherwse another. See method /// for more information. @@ -131,7 +131,7 @@ public double MutationBalancer /// /// /// The property controls type of crossover, which is used more - /// frequently. A radnom number is generated each time before doing crossover - + /// frequently. A random number is generated each time before doing crossover - /// if the random number is smaller than the specified balance value, then one /// crossover type is used, otherwse another. See method /// for more information. diff --git a/Sources/Genetic/Chromosomes/IChromosome.cs b/Sources/Genetic/Chromosomes/IChromosome.cs index 0ea89492..cf56aab2 100644 --- a/Sources/Genetic/Chromosomes/IChromosome.cs +++ b/Sources/Genetic/Chromosomes/IChromosome.cs @@ -20,11 +20,11 @@ namespace AForge.Genetic public interface IChromosome : IComparable { /// - /// Chromosome's fintess value. + /// Chromosome's fitness value. /// /// /// The fitness value represents chromosome's usefulness - the greater the - /// value, the more useful it. + /// value, the more useful it is. /// double Fitness { get; } diff --git a/Sources/Imaging/BlobCounterBase.cs b/Sources/Imaging/BlobCounterBase.cs index 7e9bfdc4..00b27292 100644 --- a/Sources/Imaging/BlobCounterBase.cs +++ b/Sources/Imaging/BlobCounterBase.cs @@ -672,7 +672,6 @@ public Blob[] GetObjects( UnmanagedImage image, bool extractInOriginalSize ) ( image.PixelFormat != PixelFormat.Format8bppIndexed ) && ( image.PixelFormat != PixelFormat.Format32bppRgb ) && ( image.PixelFormat != PixelFormat.Format32bppArgb ) && - ( image.PixelFormat != PixelFormat.Format32bppRgb ) && ( image.PixelFormat != PixelFormat.Format32bppPArgb ) ) throw new UnsupportedImageFormatException( "Unsupported pixel format of the provided image." ); diff --git a/Sources/Video.DirectShow/Internals/CustomMarshalers.cs b/Sources/Video.DirectShow/Internals/CustomMarshalers.cs new file mode 100644 index 00000000..6a270b6a --- /dev/null +++ b/Sources/Video.DirectShow/Internals/CustomMarshalers.cs @@ -0,0 +1,127 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2020 +// andrew.kirillov@gmail.com +// +// directshow.net library +// directshownet.sourceforge.net +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + // Custom marshaller used to interop different Direct Show APIs + abstract internal class DSMarshaler : ICustomMarshaler + { + protected string cookie; + protected object obj; + + public DSMarshaler( string cookie ) + { + this.cookie = cookie; + } + + // Called just before invoking the COM method. The returned IntPtr is what goes on the stack + // for the COM call. The input arg is the parameter that was passed to the method. + virtual public IntPtr MarshalManagedToNative( object managedObj ) + { + this.obj = managedObj; + + // create an appropriately sized buffer, blank it, and send it to the marshaler to make the COM call + int size = GetNativeDataSize( ) + 3; + IntPtr ptr = Marshal.AllocCoTaskMem( size ); + + for ( int x = 0; x < size / 4; x++ ) + { + Marshal.WriteInt32( ptr, x * 4, 0 ); + } + + return ptr; + } + + // Called just after invoking the COM method. The IntPtr is the same one that just got returned + // from MarshalManagedToNative. The return value is unused. + virtual public object MarshalNativeToManaged( IntPtr ptrNativeData ) + { + return this.obj; + } + + // Release the buffer + virtual public void CleanUpNativeData( IntPtr ptrNativeData ) + { + if (ptrNativeData != IntPtr.Zero ) + { + Marshal.FreeCoTaskMem( ptrNativeData ); + } + } + + // Release the managed object + virtual public void CleanUpManagedData( object managedObj ) + { + this.obj = null; + } + + // This routine is (apparently) never called by the marshaler. However it can be useful. + abstract public int GetNativeDataSize( ); + + // GetInstance is called by the marshaler in preparation to doing custom marshaling. The (optional) + // cookie is the value specified in MarshalCookie="asdf", or "" is none is specified. + + // It is commented out in this abstract class, but MUST be implemented in derived classes + // public static ICustomMarshaler GetInstance(string cookie) + } + + // Custom marshaller used for IEnumMediaTypes.Nex() as C# does not correctly marshal arrays of pointers + internal class EMTMarshaler : DSMarshaler + { + public EMTMarshaler( string cookie ) : base( cookie ) + { + } + + // Called just after invoking the COM method. The IntPtr is the same one that just got returned + // from MarshalManagedToNative. The return value is unused. + override public object MarshalNativeToManaged( IntPtr ptrNativeData ) + { + AMMediaType[] emt = this.obj as AMMediaType[]; + + for ( int x = 0; x < emt.Length; x++ ) + { + // copy in the value, and advance the pointer + IntPtr ptr = Marshal.ReadIntPtr( ptrNativeData, x * IntPtr.Size ); + if ( ptr != IntPtr.Zero) + { + emt[x] = (AMMediaType) Marshal.PtrToStructure( ptr, typeof( AMMediaType ) ); + } + else + { + emt[x] = null; + } + } + + return null; + } + + // The number of bytes to marshal out + override public int GetNativeDataSize( ) + { + // get the array size + int len = ( (Array) this.obj ).Length; + + // multiply that times the size of a pointer + int size = len * IntPtr.Size; + + return size; + } + + // This method is called by interop to create the custom marshaler. The (optional) + // cookie is the value specified in MarshalCookie="asdf", or "" is none is specified. + public static ICustomMarshaler GetInstance( string cookie ) + { + return new EMTMarshaler( cookie ); + } + } + +} \ No newline at end of file diff --git a/Sources/Video.DirectShow/Internals/IEnumMediaTypes.cs b/Sources/Video.DirectShow/Internals/IEnumMediaTypes.cs new file mode 100644 index 00000000..67d52e2a --- /dev/null +++ b/Sources/Video.DirectShow/Internals/IEnumMediaTypes.cs @@ -0,0 +1,68 @@ +// AForge Direct Show Library +// AForge.NET framework +// +// Copyright © Andrew Kirillov, 2020 +// andrew.kirillov@gmail.com +// + +namespace AForge.Video.DirectShow.Internals +{ + using System; + using System.Runtime.InteropServices; + + /// + /// This interface enumerates a pin's preferred media types. + /// + /// + [ComImport, + Guid( "89C31040-846B-11CE-97D3-00AA0055595A" ), + InterfaceType( ComInterfaceType.InterfaceIsIUnknown )] + internal interface IEnumMediaTypes + { + /// + /// Retrieves a specified number of media types. + /// + /// + /// The number of media types to retrieve. + /// Array of media types to fill in. + /// Receives the number of media types returned. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Next( [In] int cMediaTypes, + [In, Out, MarshalAs( UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof( EMTMarshaler ), SizeParamIndex = 0 )] AMMediaType[] mediaTypes, + [Out] out int typesFetched ); + + /// + /// Skips over a specified number of media types. + /// + /// + /// Number of media types to skip. + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Skip( [In] int cMediaTypes ); + + /// + /// Resets the enumeration sequence to the beginning. + /// + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Reset( ); + + /// + /// makes a copy of the enumerator. + /// + /// + /// Clone of the enumerator + /// + /// Return's HRESULT error code. + /// + [PreserveSig] + int Clone( [Out] out IEnumPins enumMediaTypes ); + } +} \ No newline at end of file diff --git a/Sources/Video.DirectShow/Internals/IPin.cs b/Sources/Video.DirectShow/Internals/IPin.cs index ceed73f2..e340917b 100644 --- a/Sources/Video.DirectShow/Internals/IPin.cs +++ b/Sources/Video.DirectShow/Internals/IPin.cs @@ -125,12 +125,12 @@ internal interface IPin /// Provides an enumerator for this pin's preferred media types. /// /// - /// Address of a variable that receives a pointer to the IEnumMediaTypes interface. + /// Receives . /// /// Return's HRESULT error code. /// [PreserveSig] - int EnumMediaTypes( IntPtr enumerator ); + int EnumMediaTypes( [Out] out IEnumMediaTypes enumMediaTypes ); /// /// Provides an array of the pins to which this pin internally connects. diff --git a/Sources/Video.DirectShow/Internals/Uuids.cs b/Sources/Video.DirectShow/Internals/Uuids.cs index 8dc18745..f60aad11 100644 --- a/Sources/Video.DirectShow/Internals/Uuids.cs +++ b/Sources/Video.DirectShow/Internals/Uuids.cs @@ -256,6 +256,15 @@ static internal class MediaSubType /// public static readonly Guid Asf = new Guid( 0x3DB80F90, 0x9412, 0x11D1, 0xAD, 0xED, 0x00, 0x00, 0xF8, 0x75, 0x4B, 0x99 ); + + /// + /// Motion JPEG. + /// + /// + /// Equals to MEDIASUBTYPE_MJPG. + /// + public static readonly Guid MJpeg = + new Guid( 0x47504A4D, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); } /// diff --git a/Sources/Video.DirectShow/Video.DirectShow.csproj b/Sources/Video.DirectShow/Video.DirectShow.csproj index cfebb5e7..7af331e3 100644 --- a/Sources/Video.DirectShow/Video.DirectShow.csproj +++ b/Sources/Video.DirectShow/Video.DirectShow.csproj @@ -68,6 +68,7 @@ + @@ -76,6 +77,7 @@ + diff --git a/Sources/Video.DirectShow/VideoCaptureDevice.cs b/Sources/Video.DirectShow/VideoCaptureDevice.cs index 8b76c5a3..fe20f26c 100644 --- a/Sources/Video.DirectShow/VideoCaptureDevice.cs +++ b/Sources/Video.DirectShow/VideoCaptureDevice.cs @@ -2,8 +2,8 @@ // AForge.NET framework // http://www.aforgenet.com/framework/ // -// Copyright © AForge.NET, 2009-2013 -// contacts@aforgenet.com +// Copyright © AForge.NET, 2009-2017 +// aforge.net@gmail.com // namespace AForge.Video.DirectShow @@ -14,6 +14,7 @@ namespace AForge.Video.DirectShow using System.Drawing.Imaging; using System.Threading; using System.Runtime.InteropServices; + using System.IO; using AForge.Video; using AForge.Video.DirectShow.Internals; @@ -68,6 +69,11 @@ public class VideoCaptureDevice : IVideoSource // provide snapshots or not private bool provideSnapshots = false; + // JPEG encoding preference + private bool preferJpegEncoding = true; + // check if JPEG encoding is enabled + private bool jpegEncodingEnabled = false; + private Thread thread = null; private ManualResetEvent stopEvent = null; @@ -205,6 +211,45 @@ public bool ProvideSnapshots set { provideSnapshots = value; } } + /// + /// Specifies preference for JPEG encoding mode for video acquisition. + /// + /// + /// Many video devices can provide video frames encoded as JPEG image, + /// which increases frame rate (as transferring uncompressed RGB data over USB interface + /// is much slower). If device supports it, then we can instruct it to provide JPEG images. + /// If not, it is configured to provide RGB data. + /// + /// The property must be set before starting video device to take an effect. + /// + /// The event provides uncompressed image anyway. + /// This property is only affects internal mode of video acquisition. + /// + /// Default value of the property is set to , which should + /// be kept this way unless there is some strong reason otherwise. + /// + /// + public bool PreferJpegEncoding + { + get { return preferJpegEncoding; } + set { preferJpegEncoding = value; } + } + + /// + /// Check if JPEG encoding is enabled for running device. + /// + /// + /// Specifies if video acquisition is configured to provide + /// JPEG encoded images. + /// + /// + /// + /// + public bool JpegEncodingEnabled + { + get { return JpegEncodingEnabled; } + } + /// /// New frame event. /// @@ -1070,13 +1115,23 @@ private void WorkerThread( bool runGraph ) graph.AddFilter( videoGrabberBase, "grabber_video" ); graph.AddFilter( snapshotGrabberBase, "grabber_snapshot" ); - // set media type - AMMediaType mediaType = new AMMediaType( ); - mediaType.MajorType = MediaType.Video; - mediaType.SubType = MediaSubType.RGB24; + // check if we need and can do JPEG encoding + if ( preferJpegEncoding ) + { + jpegEncodingEnabled = IsJpegEncodingAvailable( sourceBase ); + } - videoSampleGrabber.SetMediaType( mediaType ); - snapshotSampleGrabber.SetMediaType( mediaType ); + // set media types + AMMediaType videoMediaType = new AMMediaType( ); + videoMediaType.MajorType = MediaType.Video; + videoMediaType.SubType = ( jpegEncodingEnabled ) ? MediaSubType.MJpeg : MediaSubType.RGB24; + + AMMediaType snapshotMediaType = new AMMediaType( ); + snapshotMediaType.MajorType = MediaType.Video; + snapshotMediaType.SubType = MediaSubType.RGB24; + + videoSampleGrabber.SetMediaType( videoMediaType ); + snapshotSampleGrabber.SetMediaType( snapshotMediaType ); // get crossbar object to to allows configuring pins of capture card captureGraph.FindInterface( FindDirection.UpstreamOnly, Guid.Empty, sourceBase, typeof( IAMCrossbar ).GUID, out crossbarObject ); @@ -1097,7 +1152,8 @@ private void WorkerThread( bool runGraph ) { VideoControlFlags caps; videoControl.GetCaps( pinStillImage, out caps ); - isSapshotSupported = ( ( caps & VideoControlFlags.ExternalTriggerEnable ) != 0 ); + isSapshotSupported = ( ( ( caps & VideoControlFlags.ExternalTriggerEnable ) != 0 ) || + ( ( caps & VideoControlFlags.Trigger ) != 0 ) ); } } @@ -1142,6 +1198,8 @@ private void WorkerThread( bool runGraph ) if ( runGraph ) { + AMMediaType mediaType = new AMMediaType( ); ; + // render capture pin captureGraph.RenderStream( PinCategory.Capture, MediaType.Video, sourceBase, null, videoGrabberBase ); @@ -1313,6 +1371,62 @@ private void WorkerThread( bool runGraph ) { PlayingFinished( this, reasonToStop ); } + + jpegEncodingEnabled = false; + } + + // Check if the filter can provide JPEG encoded images + private bool IsJpegEncodingAvailable( IBaseFilter baseFilter ) + { + bool ret = false; + IEnumPins pinEnum = null; + IPin[] pins = new IPin[1]; + int pinsFetched; + + if ( ( baseFilter.EnumPins( out pinEnum ) == 0 ) && ( pinEnum != null ) ) + { + try + { + while ( ( pinEnum.Next( 1, pins, out pinsFetched ) == 0 ) && ( !ret ) ) + { + PinDirection pinDir; + + if ( ( pins[0].QueryDirection( out pinDir ) == 0 ) && ( pinDir == PinDirection.Output ) ) + { + // enum preferred media types of the pin + IEnumMediaTypes mediaEnum = null; + AMMediaType[] mediaTypes = new AMMediaType[1]; + int typesFetched; + + if ( pins[0].EnumMediaTypes( out mediaEnum ) == 0 ) + { + try + { + while ( ( mediaEnum.Next( 1, mediaTypes, out typesFetched ) == 0 ) && ( !ret ) ) + { + if ( ( mediaTypes[0].MajorType == MediaType.Video ) && ( mediaTypes[0].SubType == MediaSubType.MJpeg )) + { + ret = true; + } + + mediaTypes[0].Dispose( ); + } + } + finally + { + Marshal.ReleaseComObject( mediaEnum ); + } + } + } + } + } + finally + { + Marshal.ReleaseComObject( pinEnum ); + } + } + + return ret; } // Set resolution for the specified stream configuration @@ -1396,6 +1510,8 @@ private void GetPinCapabilitiesAndConfigureSizeAndRate( ICaptureGraphBuilder2 gr SetResolution( streamConfig, resolutionToSet ); } } + + Marshal.ReleaseComObject( streamConfigObject ); } // if failed resolving capabilities, then just create empty capabilities array, @@ -1648,47 +1764,62 @@ public int BufferCB( double sampleTime, IntPtr buffer, int bufferLen ) { if ( parent.NewFrame != null ) { - // create new image - System.Drawing.Bitmap image = new Bitmap( width, height, PixelFormat.Format24bppRgb ); + System.Drawing.Bitmap image = null; - // lock bitmap data - BitmapData imageData = image.LockBits( - new Rectangle( 0, 0, width, height ), - ImageLockMode.ReadWrite, - PixelFormat.Format24bppRgb ); + if ( !parent.jpegEncodingEnabled ) + { + // create new image + image = new Bitmap( width, height, PixelFormat.Format24bppRgb ); - // copy image data - int srcStride = imageData.Stride; - int dstStride = imageData.Stride; + // lock bitmap data + BitmapData imageData = image.LockBits( + new Rectangle( 0, 0, width, height ), + ImageLockMode.ReadWrite, + PixelFormat.Format24bppRgb ); - unsafe - { - byte* dst = (byte*) imageData.Scan0.ToPointer( ) + dstStride * ( height - 1 ); - byte* src = (byte*) buffer.ToPointer( ); + // copy image data + int srcStride = imageData.Stride; + int dstStride = imageData.Stride; - for ( int y = 0; y < height; y++ ) + unsafe { - Win32.memcpy( dst, src, srcStride ); - dst -= dstStride; - src += srcStride; - } - } + byte* dst = (byte*) imageData.Scan0.ToPointer( ) + dstStride * ( height - 1 ); + byte* src = (byte*) buffer.ToPointer( ); - // unlock bitmap data - image.UnlockBits( imageData ); + for ( int y = 0; y < height; y++ ) + { + Win32.memcpy( dst, src, srcStride ); + dst -= dstStride; + src += srcStride; + } + } - // notify parent - if ( snapshotMode ) - { - parent.OnSnapshotFrame( image ); + // unlock bitmap data + image.UnlockBits( imageData ); } else { - parent.OnNewFrame( image ); + unsafe + { + image = (Bitmap)Bitmap.FromStream( new UnmanagedMemoryStream( (byte*)buffer.ToPointer( ), bufferLen ) ); + } } - // release the image - image.Dispose( ); + if ( image != null ) + { + // notify parent + if (snapshotMode) + { + parent.OnSnapshotFrame( image ); + } + else + { + parent.OnNewFrame( image ); + } + + // release the image + image.Dispose( ); + } } return 0; diff --git a/Sources/Video.DirectShow/VideoCaptureDeviceForm.Designer.cs b/Sources/Video.DirectShow/VideoCaptureDeviceForm.Designer.cs index c300a4f8..031aaa1c 100644 --- a/Sources/Video.DirectShow/VideoCaptureDeviceForm.Designer.cs +++ b/Sources/Video.DirectShow/VideoCaptureDeviceForm.Designer.cs @@ -118,7 +118,7 @@ private void InitializeComponent( ) this.snapshotsLabel.Name = "snapshotsLabel"; this.snapshotsLabel.Size = new System.Drawing.Size( 101, 13 ); this.snapshotsLabel.TabIndex = 15; - this.snapshotsLabel.Text = "Snapshot resoluton:"; + this.snapshotsLabel.Text = "Snapshot resolution:"; // // snapshotResolutionsCombo // @@ -145,7 +145,7 @@ private void InitializeComponent( ) this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size( 83, 13 ); this.label2.TabIndex = 12; - this.label2.Text = "Video resoluton:"; + this.label2.Text = "Video resolution:"; // // label1 // diff --git a/Sources/Video.FFMPEG/VideoFileWriter.cpp b/Sources/Video.FFMPEG/VideoFileWriter.cpp index afcefada..d9ad7f47 100644 --- a/Sources/Video.FFMPEG/VideoFileWriter.cpp +++ b/Sources/Video.FFMPEG/VideoFileWriter.cpp @@ -208,18 +208,12 @@ void VideoFileWriter::Close( ) libffmpeg::av_free( data->VideoOutputBuffer ); } - for ( unsigned int i = 0; i < data->FormatContext->nb_streams; i++ ) - { - libffmpeg::av_freep( &data->FormatContext->streams[i]->codec ); - libffmpeg::av_freep( &data->FormatContext->streams[i] ); - } - if ( data->FormatContext->pb != NULL ) { libffmpeg::avio_close( data->FormatContext->pb ); } - libffmpeg::av_free( data->FormatContext ); + libffmpeg::avformat_free_context( data->FormatContext ); } if ( data->ConvertContext != NULL )