/// <summary>
        /// Open AVI file.
        /// </summary>
        ///
        /// <param name="fileName">AVI file name to open.</param>
        ///
        /// <remarks><para>The method opens a video file and prepares the stream and decoder for
        /// reading video frames with the help of <see cref="GetNextFrame"/> method.</para>
        /// </remarks>
        ///
        /// <exception cref="System.IO.IOException">Failed opening the specified file.</exception>
        /// <exception cref="VideoException">A error occurred while opening the video file. See exception message.</exception>
        ///
        ///
        public void Open(string fileName)
        {
            // close previous file
            Close( );

            bool success = false;

            try
            {
                lock ( sync )
                {
                    // open AVI file
                    if (Win32.AVIFileOpen(out file, fileName, Win32.OpenFileMode.ShareDenyWrite, IntPtr.Zero) != 0)
                    {
                        throw new System.IO.IOException("Failed opening the specified AVI file.");
                    }

                    // get first video stream
                    if (Win32.AVIFileGetStream(file, out stream, Win32.mmioFOURCC("vids"), 0) != 0)
                    {
                        throw new VideoException("Failed getting video stream.");
                    }

                    // get stream info
                    Win32.AVISTREAMINFO info = new Win32.AVISTREAMINFO( );
                    Win32.AVIStreamInfo(stream, ref info, Marshal.SizeOf(info));

                    width    = info.rectFrame.right;
                    height   = info.rectFrame.bottom;
                    position = info.start;
                    start    = info.start;
                    length   = info.length;
                    rate     = (float)info.rate / (float)info.scale;
                    codec    = Win32.decode_mmioFOURCC(info.handler);

                    // prepare decompressor
                    Win32.BITMAPINFOHEADER bitmapInfoHeader = new Win32.BITMAPINFOHEADER( );

                    bitmapInfoHeader.size        = Marshal.SizeOf(bitmapInfoHeader.GetType( ));
                    bitmapInfoHeader.width       = width;
                    bitmapInfoHeader.height      = height;
                    bitmapInfoHeader.planes      = 1;
                    bitmapInfoHeader.bitCount    = 24;
                    bitmapInfoHeader.compression = 0; // BI_RGB

                    // get frame object
                    if ((getFrame = Win32.AVIStreamGetFrameOpen(stream, ref bitmapInfoHeader)) == IntPtr.Zero)
                    {
                        bitmapInfoHeader.height = -height;

                        if ((getFrame = Win32.AVIStreamGetFrameOpen(stream, ref bitmapInfoHeader)) == IntPtr.Zero)
                        {
                            throw new VideoException("Failed initializing decompressor.");
                        }
                    }

                    success = true;
                }
            }
            finally
            {
                if (!success)
                {
                    Close( );
                }
            }
        }
        /// <summary>
        /// Create new AVI file and open it for writing.
        /// </summary>
        ///
        /// <param name="fileName">AVI file name to create.</param>
        /// <param name="width">Video width.</param>
        /// <param name="height">Video height.</param>
        ///
        /// <remarks><para>The method opens (creates) a video files, configure video codec and prepares
        /// the stream for saving video frames with a help of <see cref="AddFrame"/> method.</para></remarks>
        ///
        /// <exception cref="System.IO.IOException">Failed opening the specified file.</exception>
        /// <exception cref="VideoException">A error occurred while creating new video file. See exception message.</exception>
        /// <exception cref="OutOfMemoryException">Insufficient memory for internal buffer.</exception>
        /// <exception cref="ArgumentException">Video file resolution must be a multiple of two.</exception>
        ///
        public void Open(string fileName, int width, int height)
        {
            // close previous file
            Close( );

            // check width and height
            if (((width & 1) != 0) || ((height & 1) != 0))
            {
                throw new ArgumentException("Video file resolution must be a multiple of two.");
            }

            bool success = false;

            try
            {
                lock ( sync )
                {
                    // calculate stride
                    stride = width * 3;
                    if ((stride % 4) != 0)
                    {
                        stride += (4 - stride % 4);
                    }

                    // create new file
                    if (Win32.AVIFileOpen(out file, fileName, Win32.OpenFileMode.Create | Win32.OpenFileMode.Write, IntPtr.Zero) != 0)
                    {
                        throw new System.IO.IOException("Failed opening the specified file.");
                    }

                    this.width  = width;
                    this.height = height;

                    // describe new stream
                    Win32.AVISTREAMINFO info = new Win32.AVISTREAMINFO( );

                    info.type                = Win32.mmioFOURCC("vids");
                    info.handler             = Win32.mmioFOURCC(codec);
                    info.scale               = 1;
                    info.rate                = rate;
                    info.suggestedBufferSize = stride * height;

                    // create stream
                    if (Win32.AVIFileCreateStream(file, out stream, ref info) != 0)
                    {
                        throw new VideoException("Failed creating stream.");
                    }

                    // describe compression options
                    Win32.AVICOMPRESSOPTIONS options = new Win32.AVICOMPRESSOPTIONS( );

                    options.handler = Win32.mmioFOURCC(codec);
                    options.quality = quality;

                    // uncomment if video settings dialog is required to show
                    // Win32.AVISaveOptions( stream, ref options );

                    // create compressed stream
                    if (Win32.AVIMakeCompressedStream(out streamCompressed, stream, ref options, IntPtr.Zero) != 0)
                    {
                        throw new VideoException("Failed creating compressed stream.");
                    }

                    // describe frame format
                    Win32.BITMAPINFOHEADER bitmapInfoHeader = new Win32.BITMAPINFOHEADER( );

                    bitmapInfoHeader.size        = Marshal.SizeOf(bitmapInfoHeader.GetType( ));
                    bitmapInfoHeader.width       = width;
                    bitmapInfoHeader.height      = height;
                    bitmapInfoHeader.planes      = 1;
                    bitmapInfoHeader.bitCount    = 24;
                    bitmapInfoHeader.sizeImage   = 0;
                    bitmapInfoHeader.compression = 0; // BI_RGB

                    // set frame format
                    if (Win32.AVIStreamSetFormat(streamCompressed, 0, ref bitmapInfoHeader, Marshal.SizeOf(bitmapInfoHeader.GetType( ))) != 0)
                    {
                        throw new VideoException("Failed setting format of the compressed stream.");
                    }

                    // alloc unmanaged memory for frame
                    buffer = Marshal.AllocHGlobal(stride * height);

                    if (buffer == IntPtr.Zero)
                    {
                        throw new OutOfMemoryException("Insufficient memory for internal buffer.");
                    }

                    position = 0;
                    success  = true;
                }
            }
            finally
            {
                if (!success)
                {
                    Close( );
                }
            }
        }