// AForge Video for Windows Library
// AForge.NET framework
// http://www.aforgenet.com/framework/
//
// Copyright © Andrew Kirillov, 2007-2009
// andrew.kirillov@aforgenet.com
//
//
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using GreenshotPlugin.UnmanagedHelpers;
namespace Greenshot.Helpers {
///
/// AVI files writing using Video for Windows interface.
///
///
/// The class allows to write AVI files using Video for Windows API.
///
/// Sample usage:
/// /// // instantiate AVI writer, use WMV3 codec
/// AVIWriter writer = new AVIWriter( "wmv3" );
/// // create new AVI file and open it
/// writer.Open( "test.avi", 320, 240 );
/// // create frame image
/// Bitmap image = new Bitmap( 320, 240 );
///
/// for ( int i = 0; i < 240; i++ )
/// {
/// // update image
/// image.SetPixel( i, i, Color.Red );
/// // add the image as a new frame of video file
/// writer.AddFrame( image );
/// }
/// writer.Close( );
///
///
///
public class AVIWriter : IDisposable {
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(AVIWriter));
// AVI file
private IntPtr file;
// video stream
private IntPtr stream;
// compressed stream
private IntPtr streamCompressed;
// width of video frames
private int width;
// height of vide frames
private int height;
// length of one line
private int stride;
// quality
private int quality = -1;
// frame rate
private int rate = 25;
// current position
private int position;
// codec used for video compression
private string codec = null; //"DIB ";
///
/// Width of video frames.
///
///
/// The property specifies the width of video frames, which are acceptable
/// by method for saving, which is set in
/// method.
///
public int Width {
get { return width; }
}
///
/// Height of video frames.
///
///
/// The property specifies the height of video frames, which are acceptable
/// by method for saving, which is set in
/// method.
///
public int Height {
get { return height; }
}
///
/// Current position in video stream.
///
///
/// The property tell current position in video stream, which actually equals
/// to the amount of frames added using method.
///
public int Position
{
get { return position; }
}
///
/// Desired playing frame rate.
///
///
/// The property sets the video frame rate, which should be use during playing
/// of the video to be saved.
///
/// The property should be set befor opening new file to take effect.
///
/// Default frame rate is set to 25.
///
public int FrameRate
{
get { return rate; }
set { rate = value; }
}
///
/// Codec used for video compression.
///
///
/// The property sets the FOURCC code of video compression codec, which needs to
/// be used for video encoding.
///
/// The property should be set befor opening new file to take effect.
///
/// Default video codec is set "DIB ", which means no compression.
///
public string Codec
{
get { return codec; }
set { codec = value; }
}
///
/// Compression video quality.
///
///
/// The property sets video quality used by codec in order to balance compression rate
/// and image quality. The quality is measured usually in the [0, 100] range.
///
/// The property should be set befor opening new file to take effect.
///
/// Default value is set to -1 - default compression quality of the codec.
///
public int Quality
{
get { return quality; }
set { quality = value; }
}
///
/// Initializes a new instance of the class.
///
///
/// Initializes Video for Windows library.
///
public AVIWriter() {
Avi32.AVIFileInit();
}
///
/// Initializes a new instance of the class.
///
///
/// Codec to use for compression. eg [CVID],[IV50]
///
/// Initializes Video for Windows library.
///
public AVIWriter(string codec) : this() {
this.codec = codec;
}
///
/// Destroys the instance of the class.
///
///
~AVIWriter() {
Dispose(false);
}
///
/// Dispose the object.
///
///
/// Frees unmanaged resources used by the object. The object becomes unusable
/// after that.
///
public void Dispose() {
Dispose(true);
// remove me from the Finalization queue
GC.SuppressFinalize(this);
}
///
/// Dispose the object.
///
///
/// Indicates if disposing was initiated manually.
///
protected virtual void Dispose(bool disposing) {
if (disposing) {
// dispose managed resources
}
// close current AVI file if any opened and uninitialize AVI library
Close();
Avi32.AVIFileExit();
}
///
/// Create new AVI file and open it for writing.
///
///
/// AVI file name to create.
/// Video width.
/// Video height.
///
/// The method opens (creates) a video files, configure video codec and prepares
/// the stream for saving video frames with a help of method.
///
/// Failure of opening video files (the exception message
/// specifies the issues).
///
public bool Open(string fileName, int width, int height) {
lock (this) {
// calculate stride
stride = width * 4;
if ((stride % 4) != 0)
stride += (4 - stride % 4);
// create new file
if (Avi32.AVIFileOpen(out file, fileName, Avi32.OpenFileMode.Create | Avi32.OpenFileMode.Write, IntPtr.Zero) != 0) {
throw new ApplicationException("Failed opening file");
}
this.width = width;
this.height = height;
// describe new stream
Avi32.AVISTREAMINFO info = new Avi32.AVISTREAMINFO();
info.type = Avi32.mmioFOURCC("vids");
if (codec != null) {
info.handler = Avi32.mmioFOURCC(codec);
} else {
info.handler = Avi32.mmioFOURCC("DIB ");
}
info.scale = 1;
info.rate = rate;
info.suggestedBufferSize = stride * height;
// describe compression options
Avi32.AVICOMPRESSOPTIONS options = new Avi32.AVICOMPRESSOPTIONS();
// create stream
if (Avi32.AVIFileCreateStream(file, out stream, ref info) != 0) {
throw new ApplicationException("Failed creating stream");
}
// uncomment if video settings dialog is required to show
int retCode = 0;
if (codec == null) {
retCode = Avi32.AVISaveOptions( stream, ref options );
if (retCode == 0) {
LOG.Debug("Cancel clicked!");
return false;
}
} else {
options.handler = Avi32.mmioFOURCC(codec);
options.quality = quality;
}
// create compressed stream
int retval = Avi32.AVIMakeCompressedStream(out streamCompressed, stream, ref options, IntPtr.Zero);
if (retval != 0) {
throw new ApplicationException("Failed creating compressed stream: " + retval);
}
// describe frame format
BitmapInfoHeader bitmapInfoHeader = new BitmapInfoHeader(width, height, 32);
// set frame format
retval = Avi32.AVIStreamSetFormat(streamCompressed, 0, ref bitmapInfoHeader, Marshal.SizeOf(bitmapInfoHeader.GetType()));
if (retval != 0) {
throw new ApplicationException("Failed creating compressed stream: "+ retval);
}
position = 0;
return true;
}
}
///
/// Close video file.
///
///
public void Close() {
LOG.Debug("Close called");
lock (this) {
// release compressed stream
if (streamCompressed != IntPtr.Zero) {
LOG.Debug("AVIStreamRelease streamCompressed");
Avi32.AVIStreamRelease(streamCompressed);
streamCompressed = IntPtr.Zero;
}
// release stream
if (stream != IntPtr.Zero) {
LOG.Debug("AVIStreamRelease stream");
Avi32.AVIStreamRelease(stream);
stream = IntPtr.Zero;
}
// release file
if (file != IntPtr.Zero) {
LOG.Debug("AVIFileRelease file");
Avi32.AVIFileRelease(file);
file = IntPtr.Zero;
}
}
}
public void AddEmptyFrame() {
lock (this) {
position++;
}
}
///
/// Add new frame to the AVI file.
///
/// New frame data.
public void AddLowLevelFrame(IntPtr frameData) {
lock (this) {
// write to stream
if (Avi32.AVIStreamWrite(streamCompressed, position, 1, frameData, stride * height, 0, IntPtr.Zero, IntPtr.Zero) != 0) {
throw new ApplicationException("Failed adding frame");
}
position++;
}
}
}
///
/// Windows API functions and structures.
///
///
/// The class provides Video for Windows and some other Avi32 functions and structurs.
///
internal static class Avi32 {
///
/// Copy a block of memory.
///
///
/// Destination pointer.
/// Source pointer.
/// Memory block's length to copy.
///
/// Return's the value of dst - pointer to destination.
///
[DllImport("ntdll.dll")]
public static extern int memcpy(int dst, int src, int count);
// --- Video for Windows Functions
///
/// Initialize the AVIFile library.
///
///
[DllImport("avifil32.dll")]
public static extern void AVIFileInit();
///
/// Exit the AVIFile library.
///
[DllImport("avifil32.dll")]
public static extern void AVIFileExit();
///
/// Open an AVI file.
///
///
/// Opened AVI file interface.
/// AVI file name.
/// Opening mode (see ).
/// Handler to use (null to use default).
///
/// Returns zero on success or error code otherwise.
///
[DllImport("avifil32.dll", CharSet = CharSet.Unicode)]
public static extern int AVIFileOpen(out IntPtr aviHandler, String fileName, OpenFileMode mode, IntPtr handler);
///
/// Release an open AVI stream.
///
///
/// Open AVI file interface.
///
/// Returns the reference count of the file.
///
[DllImport("avifil32.dll")]
public static extern int AVIFileRelease(IntPtr aviHandler);
///
/// Get stream interface that is associated with a specified AVI file
///
///
/// Handler to an open AVI file.
/// Stream interface.
/// Stream type to open.
/// Count of the stream type. Identifies which occurrence of the specified stream type to access.
///
///
///
[DllImport("avifil32.dll")]
public static extern int AVIFileGetStream(IntPtr aviHandler, out IntPtr streamHandler, int streamType, int streamNumner);
///
/// Create a new stream in an existing file and creates an interface to the new stream.
///
///
/// Handler to an open AVI file.
/// Stream interface.
/// Pointer to a structure containing information about the new stream.
///
/// Returns zero if successful or an error otherwise.
///
[DllImport("avifil32.dll")]
public static extern int AVIFileCreateStream(IntPtr aviHandler, out IntPtr streamHandler, ref AVISTREAMINFO streamInfo);
///
/// Release an open AVI stream.
///
///
/// Handle to an open stream.
///
/// Returns the current reference count of the stream.
///
[DllImport("avifil32.dll")]
public static extern int AVIStreamRelease(IntPtr streamHandler);
///
/// Set the format of a stream at the specified position.
///
///
/// Handle to an open stream.
/// Position in the stream to receive the format.
/// Pointer to a structure containing the new format.
/// Size, in bytes, of the block of memory referenced by format.
///
/// Returns zero if successful or an error otherwise.
///
[DllImport("avifil32.dll")]
public static extern int AVIStreamSetFormat(IntPtr streamHandler, int position, ref BitmapInfoHeader format, int formatSize);
///
/// Get the starting sample number for the stream.
///
///
/// Handle to an open stream.
///
/// Returns the number if successful or – 1 otherwise.
///
[DllImport("avifil32.dll")]
public static extern int AVIStreamStart(IntPtr streamHandler);
///
/// Get the length of the stream.
///
/// Handle to an open stream.
/// Returns the stream's length, in samples, if successful or -1 otherwise.
[DllImport("avifil32.dll")]
public static extern int AVIStreamLength(IntPtr streamHandler);
///
/// Obtain stream header information.
///
///
/// Handle to an open stream.
/// Pointer to a structure to contain the stream information.
/// Size, in bytes, of the structure used for streamInfo.
///
/// Returns zero if successful or an error otherwise.
///
[DllImport("avifil32.dll", CharSet = CharSet.Unicode)]
public static extern int AVIStreamInfo(IntPtr streamHandler, ref AVISTREAMINFO streamInfo, int infoSize);
///
/// Prepare to decompress video frames from the specified video stream
///
/// Pointer to the video stream used as the video source.
/// Pointer to a structure that defines the desired video format. Specify NULL to use a default format.
/// Returns an object that can be used with the function.
[DllImport("avifil32.dll")]
public static extern IntPtr AVIStreamGetFrameOpen(IntPtr streamHandler, ref BitmapInfoHeader wantedFormat);
///
/// Prepare to decompress video frames from the specified video stream.
///
/// Pointer to the video stream used as the video source.
/// Pointer to a structure that defines the desired video format. Specify NULL to use a default format.
/// Returns a GetFrame object that can be used with the function.
[DllImport("avifil32.dll")]
public static extern IntPtr AVIStreamGetFrameOpen(IntPtr streamHandler, int wantedFormat);
///
/// Releases resources used to decompress video frames.
///
/// Handle returned from the function.
/// Returns zero if successful or an error otherwise.
[DllImport("avifil32.dll")]
public static extern int AVIStreamGetFrameClose(IntPtr getFrameObject);
///
/// Return the address of a decompressed video frame.
///
/// Pointer to a GetFrame object.
/// Position, in samples, within the stream of the desired frame.
/// Returns a pointer to the frame data if successful or NULL otherwise.
[DllImport("avifil32.dll")]
public static extern IntPtr AVIStreamGetFrame(IntPtr getFrameObject, int position);
///
/// Write data to a stream.
///
/// Handle to an open stream.
/// First sample to write.
/// Number of samples to write.
/// Pointer to a buffer containing the data to write.
/// Size of the buffer referenced by buffer.
/// Flag associated with this data.
/// Pointer to a buffer that receives the number of samples written. This can be set to NULL.
/// Pointer to a buffer that receives the number of bytes written. This can be set to NULL.
///
/// Returns zero if successful or an error otherwise.
///
[DllImport("avifil32.dll")]
public static extern int AVIStreamWrite(IntPtr streamHandler, int start, int samples, IntPtr buffer, int bufferSize, int flags, IntPtr samplesWritten, IntPtr bytesWritten);
///
/// Retrieve the save options for a file and returns them in a buffer.
///
/// Handle to the parent window for the Compression Options dialog box.
/// Flags for displaying the Compression Options dialog box.
/// Number of streams that have their options set by the dialog box.
/// Pointer to an array of stream interface pointers.
/// Pointer to an array of pointers to AVICOMPRESSOPTIONS structures.
/// Returns TRUE if the user pressed OK, FALSE for CANCEL, or an error otherwise.
[DllImport("avifil32.dll")]
public static extern int AVISaveOptions(IntPtr window, int flags, int streams, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] streamInterfaces, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] options);
///
/// Free the resources allocated by the AVISaveOptions function.
///
/// Count of the AVICOMPRESSOPTIONS structures referenced in options.
/// Pointer to an array of pointers to AVICOMPRESSOPTIONS structures.
/// Returns 0.
[DllImport("avifil32.dll")]
public static extern int AVISaveOptionsFree(int streams, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] options);
///
/// Create a compressed stream from an uncompressed stream and a
/// compression filter, and returns the address of a pointer to
/// the compressed stream.
///
/// Pointer to a buffer that receives the compressed stream pointer.
/// Pointer to the stream to be compressed.
/// Pointer to a structure that identifies the type of compression to use and the options to apply.
/// Pointer to a class identifier used to create the stream.
/// Returns 0 if successful or an error otherwise.
[DllImport("avifil32.dll")]
public static extern int AVIMakeCompressedStream(out IntPtr compressedStream, IntPtr sourceStream, ref AVICOMPRESSOPTIONS options, IntPtr clsidHandler);
// --- structures
///
/// Structure, which contains information for a single stream .
///
///
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
public struct AVISTREAMINFO
{
///
/// Four-character code indicating the stream type.
///
///
[MarshalAs(UnmanagedType.I4)]
public int type;
///
/// Four-character code of the compressor handler that will compress this video stream when it is saved.
///
///
[MarshalAs(UnmanagedType.I4)]
public int handler;
///
/// Applicable flags for the stream.
///
///
[MarshalAs(UnmanagedType.I4)]
public int flags;
///
/// Capability flags; currently unused.
///
///
[MarshalAs(UnmanagedType.I4)]
public int ñapabilities;
///
/// Priority of the stream.
///
///
[MarshalAs(UnmanagedType.I2)]
public short priority;
///
/// Language of the stream.
///
///
[MarshalAs(UnmanagedType.I2)]
public short language;
///
/// Time scale applicable for the stream.
///
///
/// Dividing rate by scale gives the playback rate in number of samples per second.
///
[MarshalAs(UnmanagedType.I4)]
public int scale;
///
/// Rate in an integer format.
///
///
[MarshalAs(UnmanagedType.I4)]
public int rate;
///
/// Sample number of the first frame of the AVI file.
///
///
[MarshalAs(UnmanagedType.I4)]
public int start;
///
/// Length of this stream.
///
///
/// The units are defined by rate and scale.
///
[MarshalAs(UnmanagedType.I4)]
public int length;
///
/// Audio skew. This member specifies how much to skew the audio data ahead of the video frames in interleaved files.
///
///
[MarshalAs(UnmanagedType.I4)]
public int initialFrames;
///
/// Recommended buffer size, in bytes, for the stream.
///
///
[MarshalAs(UnmanagedType.I4)]
public int suggestedBufferSize;
///
/// Quality indicator of the video data in the stream.
///
///
/// Quality is represented as a number between 0 and 10,000.
///
[MarshalAs(UnmanagedType.I4)]
public int quality;
///
/// Size, in bytes, of a single data sample.
///
///
[MarshalAs(UnmanagedType.I4)]
public int sampleSize;
///
/// Dimensions of the video destination rectangle.
///
///
[MarshalAs(UnmanagedType.Struct, SizeConst = 16)]
public RECT rectFrame;
///
/// Number of times the stream has been edited.
///
///
[MarshalAs(UnmanagedType.I4)]
public int editCount;
///
/// Number of times the stream format has changed.
///
///
[MarshalAs(UnmanagedType.I4)]
public int formatChangeCount;
///
/// Description of the stream.
///
///
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string name;
}
///
/// Structure, which contains information about a stream and how it is compressed and saved.
///
///
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct AVICOMPRESSOPTIONS
{
///
/// Four-character code indicating the stream type.
///
///
[MarshalAs(UnmanagedType.I4)]
public int type;
///
/// Four-character code for the compressor handler that will compress this video stream when it is saved.
///
///
[MarshalAs(UnmanagedType.I4)]
public int handler;
///
/// Maximum period between video key frames.
///
///
[MarshalAs(UnmanagedType.I4)]
public int keyFrameEvery;
///
/// Quality value passed to a video compressor.
///
///
[MarshalAs(UnmanagedType.I4)]
public int quality;
///
/// Video compressor data rate.
///
///
[MarshalAs(UnmanagedType.I4)]
public int bytesPerSecond;
///
/// Flags used for compression.
///
///
[MarshalAs(UnmanagedType.I4)]
public int flags;
///
/// Pointer to a structure defining the data format.
///
///
[MarshalAs(UnmanagedType.I4)]
public int format;
///
/// Size, in bytes, of the data referenced by format.
///
///
[MarshalAs(UnmanagedType.I4)]
public int formatSize;
///
/// Video-compressor-specific data; used internally.
///
///
[MarshalAs(UnmanagedType.I4)]
public int parameters;
///
/// Size, in bytes, of the data referenced by parameters.
///
[MarshalAs(UnmanagedType.I4)]
public int parametersSize;
///
/// Interleave factor for interspersing stream data with data from the first stream.
///
///
[MarshalAs(UnmanagedType.I4)]
public int interleaveEvery;
}
// --- enumerations
///
/// File access modes.
///
///
[Flags]
public enum OpenFileMode {
Read = 0x00000000,
Write = 0x00000001,
ReadWrite = 0x00000002,
ShareCompat = 0x00000000,
ShareExclusive = 0x00000010,
ShareDenyWrite = 0x00000020,
ShareDenyRead = 0x00000030,
ShareDenyNone = 0x00000040,
Parse = 0x00000100,
Delete = 0x00000200,
Verify = 0x00000400,
Cancel = 0x00000800,
Create = 0x00001000,
Prompt = 0x00002000,
Exist = 0x00004000,
Reopen = 0x00008000
}
///
/// .NET replacement of mmioFOURCC macros. Converts four characters to code.
///
///
/// Four characters string.
///
/// Returns the code created from provided characters.
///
public static int mmioFOURCC(string str) {
return ( ((int)(byte)(str[0])) |
((int)(byte)(str[1]) << 8) |
((int)(byte)(str[2]) << 16) |
((int)(byte)(str[3]) << 24));
}
///
/// Inverse to . Converts code to fout characters string.
///
///
/// Code to convert.
///
/// Returns four characters string.
///
public static string decode_mmioFOURCC(int code) {
char[] chs = new char[4];
for (int i = 0; i < 4; i++) {
chs[i] = (char)(byte)((code >> (i << 3)) & 0xFF);
if (!char.IsLetterOrDigit(chs[i])) {
chs[i] = ' ';
}
}
return new string(chs);
}
///
/// Version of for one stream only.
///
///
/// Stream to configure.
/// Stream options.
///
/// Returns TRUE if the user pressed OK, FALSE for CANCEL, or an error otherwise.
///
public static int AVISaveOptions(IntPtr stream, ref AVICOMPRESSOPTIONS options) {
IntPtr[] streams = new IntPtr[1];
IntPtr[] infPtrs = new IntPtr[1];
IntPtr mem;
int ret;
// alloc unmanaged memory
mem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(AVICOMPRESSOPTIONS)));
// copy from managed structure to unmanaged memory
Marshal.StructureToPtr(options, mem, false);
streams[0] = stream;
infPtrs[0] = mem;
// show dialog with a list of available compresors and configuration
ret = AVISaveOptions(IntPtr.Zero, 0, 1, streams, infPtrs);
// copy from unmanaged memory to managed structure
options = (AVICOMPRESSOPTIONS)Marshal.PtrToStructure(mem, typeof(AVICOMPRESSOPTIONS));
// free AVI compression options
AVISaveOptionsFree(1, infPtrs);
// clear it, because the information already freed by AVISaveOptionsFree
options.format = 0;
options.parameters = 0;
// free unmanaged memory
Marshal.FreeHGlobal(mem);
return ret;
}
}
}