/* This class has been written by * Corinna John (Hannover, Germany) * cj@binary-universe.net * * You may do with this code whatever you like, * except selling it or claiming any rights/ownership. * * Please send me a little feedback about what you're * using this code for and what changes you'd like to * see in later versions. (And please excuse my bad english.) * * WARNING: This is experimental code. * Please do not expect "Release Quality". * */ using System; using System.IO; using System.Collections; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; namespace Scurvy.Media.Avi { internal sealed class AviManager { private int aviFile = 0; private ArrayList streams = new ArrayList(); /// Open or create an AVI file /// Name of the AVI file /// true: Open the file; false: Create or overwrite the file public AviManager(String fileName, bool open){ Avi.AVIFileInit(); int result; if(open){ //open existing file result = Avi.AVIFileOpen( ref aviFile, fileName, Avi.OF_READWRITE, 0); }else{ //create empty file result = Avi.AVIFileOpen( ref aviFile, fileName, Avi.OF_WRITE | Avi.OF_CREATE, 0); } if(result != 0) { throw new Exception("Exception in AVIFileOpen: "+result.ToString()); } } private AviManager(int aviFile) { this.aviFile = aviFile; } /// Get the first video stream - usually there is only one video stream /// VideoStream object for the stream public AviVideoStream GetVideoStream(){ IntPtr aviStream; int result = Avi.AVIFileGetStream( aviFile, out aviStream, Avi.streamtypeVIDEO, 0); if(result != 0){ throw new Exception("Exception in AVIFileGetStream: "+result.ToString()); } AviVideoStream stream = new AviVideoStream(aviFile, aviStream); streams.Add(stream); return stream; } /// Getthe first wave audio stream /// AudioStream object for the stream public AviAudioStream GetWaveStream(){ IntPtr aviStream; int result = Avi.AVIFileGetStream( aviFile, out aviStream, Avi.streamtypeAUDIO, 0); if(result != 0){ throw new Exception("Exception in AVIFileGetStream: "+result.ToString()); } AviAudioStream stream = new AviAudioStream(aviFile, aviStream); streams.Add(stream); return stream; } /// Get a stream from the internal list of opened streams /// Index of the stream. The streams are not sorted, the first stream is the one that was opened first. /// VideoStream at position [index] /// /// Use this method after DecompressToNewFile, /// to get the copied stream from the new AVI file /// /// /// //streams cannot be edited - copy to a new file /// AviManager newManager = aviStream.DecompressToNewFile(@"..\..\testdata\temp.avi", true); /// //there is only one stream in the new file - get it and add a frame /// VideoStream aviStream = newManager.GetOpenStream(0); /// aviStream.AddFrame(bitmap); /// public AviVideoStream GetOpenStream(int index){ return (AviVideoStream)streams[index]; } /// Add an empty video stream to the file /// true: Create a compressed stream before adding frames /// Frames per second /// Size of one frame in bytes /// Width of each image /// Height of each image /// PixelFormat of the images /// VideoStream object for the new stream public AviVideoStream AddVideoStream(bool isCompressed, double frameRate, int frameSize, int width, int height, PixelFormat format){ AviVideoStream stream = new AviVideoStream(aviFile, isCompressed, frameRate, frameSize, width, height, format); streams.Add(stream); return stream; } /// Add an empty video stream to the file /// Compresses the stream without showing the codecs dialog /// Compression options /// Frames per second /// Image to write into the stream as the first frame /// VideoStream object for the new stream public AviVideoStream AddVideoStream(Avi.AVICOMPRESSOPTIONS compressOptions, double frameRate, Bitmap firstFrame) { AviVideoStream stream = new AviVideoStream(aviFile, compressOptions, frameRate, firstFrame); streams.Add(stream); return stream; } /// Add an empty video stream to the file /// true: Create a compressed stream before adding frames /// Frames per second /// Image to write into the stream as the first frame /// VideoStream object for the new stream public AviVideoStream AddVideoStream(bool isCompressed, double frameRate, Bitmap firstFrame){ AviVideoStream stream = new AviVideoStream(aviFile, isCompressed, frameRate, firstFrame); streams.Add(stream); return stream; } /// Add a wave audio stream from another file to this file /// Name of the wave file to add /// Index of the video frame at which the sound is going to start public void AddAudioStream(String waveFileName, int startAtFrameIndex) { AviManager audioManager = new AviManager(waveFileName, true); AviAudioStream newStream = audioManager.GetWaveStream(); AddAudioStream(newStream, startAtFrameIndex); audioManager.Close(); } private IntPtr InsertSilence(int countSilentSamples, IntPtr waveData, int lengthWave, ref Avi.AVISTREAMINFO streamInfo) { //initialize silence int lengthSilence = countSilentSamples * streamInfo.dwSampleSize; byte[] silence = new byte[lengthSilence]; //initialize new sound int lengthNewStream = lengthSilence + lengthWave; IntPtr newWaveData = Marshal.AllocHGlobal(lengthNewStream); //copy silence Marshal.Copy(silence, 0, newWaveData, lengthSilence); //copy sound byte[] sound = new byte[lengthWave]; Marshal.Copy(waveData, sound, 0, lengthWave); IntPtr startOfSound = new IntPtr(newWaveData.ToInt32() + lengthSilence); Marshal.Copy(sound, 0, startOfSound, lengthWave); Marshal.FreeHGlobal(newWaveData); streamInfo.dwLength = lengthNewStream; return newWaveData; } /// Add an existing wave audio stream to the file /// The stream to add /// /// The index of the video frame at which the sound is going to start. /// '0' inserts the sound at the beginning of the video. /// public void AddAudioStream(AviAudioStream newStream, int startAtFrameIndex) { Avi.AVISTREAMINFO streamInfo = new Avi.AVISTREAMINFO(); Avi.PCMWAVEFORMAT streamFormat = new Avi.PCMWAVEFORMAT(); int streamLength = 0; IntPtr rawData = newStream.GetStreamData(ref streamInfo, ref streamFormat, ref streamLength); IntPtr waveData = rawData; if (startAtFrameIndex > 0) { //not supported //streamInfo.dwStart = startAtFrameIndex; double framesPerSecond = GetVideoStream().FrameRate; double samplesPerSecond = newStream.CountSamplesPerSecond; double startAtSecond = startAtFrameIndex / framesPerSecond; int startAtSample = (int)(samplesPerSecond * startAtSecond); waveData = InsertSilence(startAtSample - 1, waveData, streamLength, ref streamInfo); } IntPtr aviStream; int result = Avi.AVIFileCreateStream(aviFile, out aviStream, ref streamInfo); if(result != 0){ throw new Exception("Exception in AVIFileCreateStream: "+result.ToString()); } result = Avi.AVIStreamSetFormat(aviStream, 0, ref streamFormat, Marshal.SizeOf(streamFormat)); if(result != 0){ throw new Exception("Exception in AVIStreamSetFormat: "+result.ToString()); } result = Avi.AVIStreamWrite(aviStream, 0, streamLength, waveData, streamLength, Avi.AVIIF_KEYFRAME, 0, 0); if(result != 0){ throw new Exception("Exception in AVIStreamWrite: "+result.ToString()); } result = Avi.AVIStreamRelease(aviStream); if(result != 0){ throw new Exception("Exception in AVIStreamRelease: "+result.ToString()); } Marshal.FreeHGlobal(waveData); } /// Add an existing wave audio stream to the file /// The new stream's data /// Header info for the new stream /// The new stream' format info /// Length of the new stream public void AddAudioStream(IntPtr waveData, Avi.AVISTREAMINFO streamInfo, Avi.PCMWAVEFORMAT streamFormat, int streamLength) { IntPtr aviStream; int result = Avi.AVIFileCreateStream(aviFile, out aviStream, ref streamInfo); if (result != 0) { throw new Exception("Exception in AVIFileCreateStream: " + result.ToString()); } result = Avi.AVIStreamSetFormat(aviStream, 0, ref streamFormat, Marshal.SizeOf(streamFormat)); if (result != 0) { throw new Exception("Exception in AVIStreamSetFormat: " + result.ToString()); } result = Avi.AVIStreamWrite(aviStream, 0, streamLength, waveData, streamLength, Avi.AVIIF_KEYFRAME, 0, 0); if (result != 0) { throw new Exception("Exception in AVIStreamWrite: " + result.ToString()); } result = Avi.AVIStreamRelease(aviStream); if (result != 0) { throw new Exception("Exception in AVIStreamRelease: " + result.ToString()); } } /// Copy a piece of video and wave sound int a new file /// File name /// Start copying at second x /// Stop copying at second y /// AviManager for the new video public AviManager CopyTo(String newFileName, float startAtSecond, float stopAtSecond) { AviManager newFile = new AviManager(newFileName, false); try { //copy video stream AviVideoStream videoStream = GetVideoStream(); int startFrameIndex = (int)(videoStream.FrameRate * startAtSecond); int stopFrameIndex = (int)(videoStream.FrameRate * stopAtSecond); videoStream.GetFrameOpen(); Bitmap bmp = videoStream.GetBitmap(startFrameIndex); AviVideoStream newStream = newFile.AddVideoStream(false, videoStream.FrameRate, bmp); for (int n = startFrameIndex + 1; n <= stopFrameIndex; n++) { bmp = videoStream.GetBitmap(n); newStream.AddFrame(bmp); } videoStream.GetFrameClose(); //copy audio stream AviAudioStream waveStream = GetWaveStream(); Avi.AVISTREAMINFO streamInfo = new Avi.AVISTREAMINFO(); Avi.PCMWAVEFORMAT streamFormat = new Avi.PCMWAVEFORMAT(); int streamLength = 0; IntPtr ptrRawData = waveStream.GetStreamData( ref streamInfo, ref streamFormat, ref streamLength); int startByteIndex = (int)( startAtSecond * (float)(waveStream.CountSamplesPerSecond * streamFormat.nChannels * waveStream.CountBitsPerSample) / 8); int stopByteIndex = (int)( stopAtSecond * (float)(waveStream.CountSamplesPerSecond * streamFormat.nChannels * waveStream.CountBitsPerSample) / 8); IntPtr ptrWavePart = new IntPtr(ptrRawData.ToInt32() + startByteIndex); byte[] rawData = new byte[stopByteIndex - startByteIndex]; Marshal.Copy(ptrWavePart, rawData, 0, rawData.Length); Marshal.FreeHGlobal(ptrRawData); streamInfo.dwLength = rawData.Length; streamInfo.dwStart = 0; IntPtr unmanagedRawData = Marshal.AllocHGlobal(rawData.Length); Marshal.Copy(rawData, 0, unmanagedRawData, rawData.Length); newFile.AddAudioStream(unmanagedRawData, streamInfo, streamFormat, rawData.Length); Marshal.FreeHGlobal(unmanagedRawData); } catch (Exception ex) { newFile.Close(); throw ex; } return newFile; } /// Release all ressources public void Close(){ foreach(AviStream stream in streams){ stream.Close(); } Avi.AVIFileRelease(aviFile); Avi.AVIFileExit(); } public static void MakeFileFromStream(String fileName, AviStream stream) { IntPtr newFile = IntPtr.Zero; IntPtr streamPointer = stream.StreamPointer; Avi.AVICOMPRESSOPTIONS_CLASS opts = new Avi.AVICOMPRESSOPTIONS_CLASS(); opts.fccType = (uint)Avi.streamtypeVIDEO; opts.lpParms = IntPtr.Zero; opts.lpFormat = IntPtr.Zero; Avi.AVISaveOptions(IntPtr.Zero, Avi.ICMF_CHOOSE_KEYFRAME | Avi.ICMF_CHOOSE_DATARATE, 1, ref streamPointer, ref opts); Avi.AVISaveOptionsFree(1, ref opts); Avi.AVISaveV(fileName, 0, 0, 1, ref streamPointer, ref opts); } } }