【C#】AviFile使用

来源:互联网 发布:linux ssh修改文件内容 编辑:程序博客网 时间:2024/04/30 11:47

最近在做一个视频识别项目,需要用到视频处理,在codeproject上找到了一个关于对Avi的操作库,感觉不错,在这里把一些要点记录下来

http://www.codeproject.com/Articles/7388/A-Simple-C-Wrapper-for-the-AviFile-Library

也可以在我的资源下载:http://download.csdn.net/detail/lijun7788/6786523

Avi视频文件的编码有很多,这个库只支持部分Avi文件,有些Avi文件不支持,具体哪些不支持还没搞清楚

AviFile库提供了

  1、从视频流中图片的处理

  2、视频中音频的处理

  3、压缩和解压视频流

 

1、使用

  1、从视频读取图片,还有一些参数可以通过aviStream查看到,可以把当前流信息输出到文件 

//Avi文件读取            string filepath = @"D:\test.avi";            AviManager aviManager = new AviManager(filepath, true);            VideoStream aviStream = aviManager.GetVideoStream();            //获取和保存音频流到文件            AudioStream audioStream = aviManager.GetWaveStream();            audioStream.ExportStream(@"D:\test.wav");            aviStream.GetFrameOpen();            //获取视频总帧数            int framecount = aviStream.CountFrames;            //获取第5帧的图片            Bitmap bmp = aviStream.GetBitmap(5);            //视频速度            double rate = aviStream.FrameRate;            //直接保存帧图片到文件            //aviStream.ExportBitmap(5, @"D:\frame_05.jpg");            //保存当前流到文件            //aviStream.ExportStream(@"D:\currenttest.avi")                        aviStream.GetFrameClose();            aviManager.Close();
2、把图片和音频写入视频流(部分参数后面会说到,这里只是简单的演示)

//读取图片文件            string[] files = Directory.GetFiles(@"D:\test\", "*.jpg");            AviManager aviManager = new AviManager(@"D:\newtest.avi", false);                        //添加音频            String fileName = @"D:\audio.wav";            aviManager.AddAudioStream(fileName, 0);                        //读取第一张图片,设置每秒3帧            VideoStream aviStream = aviManager.AddVideoStream(true, 3, new Bitmap(files[0]));            for (int i = 1; i < files.Length; i++)            {                aviStream.AddFrame(new Bitmap(files[i]));            }            aviManager.Close();

3、从视频截取一段保存到AviManager 

            AviManager aviManager = new AviManager(filepath, true);            float startSecond = 0;            float stopSecond = 10;            //截取0-10s放到newManager            AviManager newManager = aviManager.CopyTo(@"D:\newtest_0-10.avi", startSecond, stopSecond);

4、对已压缩的视频进行解压缩,放到另一个VideoStream中

            VideoStream newstream;            AviManager newManager = aviStream.DecompressToNewFile(@"D:\01.avi", false, out newstream);            newstream.GetFrameOpen();            Bitmap bitmap = newstream.GetBitmap(10);            newstream.GetFrameClose();

5、向视频流中添加一张图片帧,对于未压缩的视频流,我们可以直接进行添加,但是对于已经压缩过的视频流,添加的图片帧不能重新进行压缩,所以我们需要对其进行解压缩,操作完后在进行压缩
//读取图片文件            string[] files = Directory.GetFiles(@"D:\test\", "*.jpg");            //打开一个已存在的视频            AviManager aviManager = new AviManager(@"D:\test.avi", true);            VideoStream avistream = aviManager.GetVideoStream();            VideoStream newstream;            //解压视频到newstream中,最后已压缩的形式保存            AviManager newManager = avistream.DecompressToNewFile(@"newtest.avi", true, out newstream);            avistream = newManager.GetOpenStream(0);            for (int i = 1; i < files.Length; i++)            {                avistream.AddFrame(new Bitmap(files[i]));            }            aviManager.Close();            //关闭和保存文件newtest.avi            newManager.Close();

6、编辑视频流 EditableVideoStream (可以对视频流的帧,进行cut,delete,paste等早错)

//打开一个已存在的视频            AviManager aviManager = new AviManager(@"D:\test.avi", true);            VideoStream avistream = aviManager.GetVideoStream();            EditableVideoStream editableStream = new EditableVideoStream(avistream);            int start = 0;            int length =10;            int position = 10;            //Copy            IntPtr copiedData = editableStream.Copy(start, length);            //Insert            editableStream.Paste(copiedData, 0, position, length);            //Delete            IntPtr deletedData = editableStream.Cut(start, length);

同时也可以把一个 VideoStream 流 Paste 到 EditableVideoStream

 editableStream.Paste(stream, 0, position, stream.CountFrames);   
7、对视频流进行一些参数设置
            Avi.AVISTREAMINFO info = editableStream.StreamInfo;            //设置播放速度:每秒 3帧            info.dwRate = 3;            editableStream.SetInfo(info);  

2、AviFile后台工作

  AviManager 管理Avi文件的stream,构造函数传入文件名,当调用Close函数时,关闭所有打开的流和文件,并保存。

可以使用 AddVideoStream 和 AddAudioStream 把视频里和音频流添加到一个新的AviManager中,音频流只支持wav文件

Create a video stream

  VideoStream 有两个构造函数

public VideoStream AddVideoStream(                bool isCompressed, //display the compression dialog, create a compressed stream                int frameRate, //frames per second                int frameSize, //size of one frame in bytes                int width, int height, PixelFormat format //format of the bitmaps                )            {                VideoStream stream = new VideoStream(aviFile, isCompressed, frameRate, frameSize, width, height, format);                streams.Add(stream);                return stream;            }            public VideoStream AddVideoStream(                bool isCompressed, //display the compression dialog, create a compressed stream                int frameRate, //frames per second                Bitmap firstFrame //get the format from this image and add it to the new stream                )            {                VideoStream stream = new VideoStream(aviFile, isCompressed, frameRate, firstFrame);                streams.Add(stream);                return stream;            }

VideoStream使用格式化的数据创建新的流,调用 AVIFileCreateStream,如果 isCompressed参数为true,则调用 AVIMakeCompressedStream

public VideoStream(int aviFile, bool writeCompressed, int frameRate, ...)        {            //store format information            //...            //create the stream            CreateStream();        }                private void CreateStream()        {            //fill stream information            Avi.AVISTREAMINFO strhdr = new Avi.AVISTREAMINFO();            strhdr.fccType = Avi.mmioStringToFOURCC("vids", 0);            strhdr.fccHandler = Avi.mmioStringToFOURCC("CVID", 0);            strhdr.dwScale = 1;            strhdr.dwRate = frameRate;            strhdr.dwSuggestedBufferSize = frameSize;            strhdr.dwQuality = -1; //default            strhdr.rcFrame.bottom = (uint)height;            strhdr.rcFrame.right = (uint)width;            strhdr.szName = new UInt16[64];            //create the stream            int result = Avi.AVIFileCreateStream(aviFile, out aviStream, ref strhdr);            if(writeCompressed)            {                //create a compressed stream from                 CreateCompressedStream();            }        }        private void CreateCompressedStream()        {            Avi.AVICOMPRESSOPTIONS_CLASS options = new Avi.AVICOMPRESSOPTIONS_CLASS();            options.fccType = (uint)Avi.streamtypeVIDEO;            options.lpParms = IntPtr.Zero;            options.lpFormat = IntPtr.Zero;            //display the compression options dialog            Avi.AVISaveOptions(IntPtr.Zero, Avi.ICMF_CHOOSE_KEYFRAME | Avi.ICMF_CHOOSE_DATARATE, 1, ref aviStream, ref options);            //get a compressed stream            Avi.AVICOMPRESSOPTIONS structOptions = options.ToStruct();            int result = Avi.AVIMakeCompressedStream(out compressedStream, aviStream, ref structOptions, 0);            //format the compressed stream            SetFormat(compressedStream);        }

其中使用 AVICOMPRESSOPTIONS_CLASS 类代替 AVICOMPRESSOPTIONS 结构体,使用类代替结构体在使用指针的时候更加容易,如果你看不懂,你可能没在.Net使用过 AVISaveOptions 或 AVISaveV,下面看看 AVISaveOptions 的声明

        BOOL AVISaveOptions(                HWND hwnd,                UINT uiFlags,                int nStreams,                PAVISTREAM * ppavi,                LPAVICOMPRESSOPTIONS * plpOptions);   

    LPAVICOMPRESSOPTIONS 是一个指向  AVICOMPRESSOPTIONS 结构体指针的指针(指向指针的指针)

    在C#中,结构体是值传递的,如果使用ref来传递结构体,则传递的是指向结构体的指针

    而类使用的是引用,实际传递的是指针,地址,所以使用ref传递类时,实际传递的是指向类指针的指针(指向指针的指针),

    所以这里使用类代替结构体,下面是在C#中声明 AVISaveOptions 和 AVICOMPRESSOPTIONS 

[DllImport("avifil32.dll")]            public static extern bool AVISaveOptions(                IntPtr hwnd,                UInt32 uiFlags,                Int32 nStreams,                ref IntPtr ppavi,                ref AVICOMPRESSOPTIONS_CLASS plpOptions                );            [StructLayout(LayoutKind.Sequential, Pack=1)]            public struct AVICOMPRESSOPTIONS             {                public UInt32   fccType;                public UInt32   fccHandler;                public UInt32   dwKeyFrameEvery;                public UInt32   dwQuality;                public UInt32   dwBytesPerSecond;                public UInt32   dwFlags;                public IntPtr   lpFormat;                public UInt32   cbFormat;                public IntPtr   lpParms;                public UInt32   cbParms;                public UInt32   dwInterleaveEvery;            }            [StructLayout(LayoutKind.Sequential, Pack=1)]            public class AVICOMPRESSOPTIONS_CLASS             {                public UInt32   fccType;                public UInt32   fccHandler;                public UInt32   dwKeyFrameEvery;                public UInt32   dwQuality;                public UInt32   dwBytesPerSecond;                public UInt32   dwFlags;                public IntPtr   lpFormat;                public UInt32   cbFormat;                public IntPtr   lpParms;                public UInt32   cbParms;                public UInt32   dwInterleaveEvery;                public AVICOMPRESSOPTIONS ToStruct()                {                    AVICOMPRESSOPTIONS returnVar = new AVICOMPRESSOPTIONS();                    returnVar.fccType = this.fccType;                    returnVar.fccHandler = this.fccHandler;                    returnVar.dwKeyFrameEvery = this.dwKeyFrameEvery;                    returnVar.dwQuality = this.dwQuality;                    returnVar.dwBytesPerSecond = this.dwBytesPerSecond;                    returnVar.dwFlags = this.dwFlags;                    returnVar.lpFormat = this.lpFormat;                    returnVar.cbFormat = this.cbFormat;                    returnVar.lpParms = this.lpParms;                    returnVar.cbParms = this.cbParms;                    returnVar.dwInterleaveEvery = this.dwInterleaveEvery;                    return returnVar;                }            }

在这个工作区,可以调用 AVISaveOptions ,来设置Avi文件的一些参数

  通过AddFrame函数可以用图片填充视频流

public void AddFrame(Bitmap bmp)            {                bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);                if (countFrames == 0)                {                   //  the format of the first frame defines the format of the stream                   CopyPalette(bmp.Palette);                   SetFormat(writeCompressed ? compressedStream : aviStream,                             countFrames);                }                //lock the memory block                BitmapData bmpDat = bmp.LockBits(                    new Rectangle(0,0, bmp.Width, bmp.Height),                    ImageLockMode.ReadOnly, bmp.PixelFormat);                //add the bitmap to the (un-)compressed stream                int result = Avi.AVIStreamWrite(                    writeCompressed ? compressedStream : aviStream,                    countFrames, 1,                    bmpDat.Scan0,                    (Int32)(bmpDat.Stride * bmpDat.Height),                    0, 0, 0);                //unlock the memory block                bmp.UnlockBits(bmpDat);                //count the frames, so that we don't have to call AVIStreamLength for every new frame                countFrames++;            }

Add frames to an existing stream

public VideoStream(int aviFile, IntPtr aviStream)        {            this.aviFile = aviFile;            this.aviStream = aviStream;            //read the stream's format            Avi.BITMAPINFOHEADER bih = new Avi.BITMAPINFOHEADER();            int size = Marshal.SizeOf(bih);            Avi.AVIStreamReadFormat(aviStream, 0, ref bih, ref size);            Avi.AVISTREAMINFO streamInfo = GetStreamInfo(aviStream);            //store the important format values            this.frameRate = streamInfo.dwRate / streamInfo.dwScale;            this.width = (int)streamInfo.rcFrame.right;            this.height = (int)streamInfo.rcFrame.bottom;            this.frameSize = bih.biSizeImage;            this.countBitsPerPixel = bih.biBitCount;            //get the count of frames that are already there            int firstFrame = Avi.AVIStreamStart(aviStream.ToInt32());            countFrames =                firstFrame + Avi.AVIStreamLength(aviStream.ToInt32());        }

 如果视频流是未压缩的,可以直接调用AddFrame,否则,需要对其进行解压缩,并重新压缩到一个新的流

public AviManager DecompressToNewFile(String fileName, bool recompress)        {            //create a new AVI file            AviManager newFile = new AviManager(fileName, false);            //create a video stream in the new file            this.GetFrameOpen();            Bitmap frame = GetBitmap(0);            VideoStream newStream = newFile.AddVideoStream(recompress, frameRate, frame);            //decompress each frame and add it to the new stream            for(int n=1; n<countFrames; n++)            {                frame = GetBitmap(n);                newStream.AddFrame(frame);            }            this.GetFrameClose();            return newFile;        }

  DecompressToNewFile 创建一个可编辑的拷贝到一个新的文件流,可以添加frames到该流

 

Separate a stream

  有时,我们可能只想要视频的声音,或是只要没有声音的视频,我们没有必要重新创建视频,添加每一帧到视频流中,可以通过使用 AviSaveV 把当前流到处到文件,AVISaveV只是所有类型的流,只是压缩参数不一样而已

public override void ExportStream(String fileName)        {            Avi.AVICOMPRESSOPTIONS_CLASS opts = new Avi.AVICOMPRESSOPTIONS_CLASS();            //for video streams            opts.fccType = (UInt32)Avi.mmioStringToFOURCC("vids", 0);            opts.fccHandler = (UInt32)Avi.mmioStringToFOURCC("CVID", 0);            //for audio streams            //opts.fccType = (UInt32)Avi.mmioStringToFOURCC("auds", 0);            //opts.fccHandler = (UInt32)Avi.mmioStringToFOURCC("CAUD", 0);            //export the stream            Avi.AVISaveV(fileName, 0, 0, 1, ref aviStream, ref opts);        }

 Import sound from a Wave file

  现在,我们可以通过Bitmap来生成视频,也可以从视频中导出声音,那么当我们导入wav文件的时候,底层是如何工作的呢,我们还是可以用 AVISaveV 这个方法,来组合视频和音频成一个文件,但这里我们有更简单的方法,打开音频文件作为Avi文件,然后拷贝到另一个流中

public void AddAudioStream(String waveFileName)            {                //open the wave file                AviManager audioManager = new AviManager(waveFileName, true);                //get the wave sound as an audio stream...                AudioStream newStream = audioManager.GetWaveStream();                //...and add it to the file                AddAudioStream(newStream);                audioManager.Close();            }            public void AddAudioStream(AudioStream newStream)            {                Avi.AVISTREAMINFO streamInfo = new Avi.AVISTREAMINFO();                Avi.PCMWAVEFORMAT streamFormat = new Avi.PCMWAVEFORMAT();                int streamLength = 0;                //read header info, format and length,                 //and get a pointer to the wave data                IntPtr waveData = newStream.GetStreamData(                    ref streamInfo,                    ref streamFormat,                    ref streamLength);                //create new stream                IntPtr aviStream;                Avi.AVIFileCreateStream(aviFile, out aviStream, ref streamInfo);                //add format new stream                Avi.AVIStreamSetFormat(                    aviStream, 0,                    ref streamFormat,                    Marshal.SizeOf(streamFormat));                //copy the raw wave data into the new stream                Avi.AVIStreamWrite(                    aviStream, 0,                    streamLength,                    waveData,                    streamLength,                    Avi.AVIIF_KEYFRAME, 0, 0);                Avi.AVIStreamRelease(aviStream);            }
截取视频流

public AviManager CopyTo(String newFileName, int startAtSecond, int stopAtSecond)         {            AviManager newFile = new AviManager(newFileName, false);            try             {                //copy video stream                VideoStream videoStream = GetVideoStream();                int startFrameIndex = videoStream.FrameRate * startAtSecond;                int stopFrameIndex = videoStream.FrameRate * stopAtSecond;                videoStream.GetFrameOpen();                Bitmap bmp = videoStream.GetBitmap(startFrameIndex);                VideoStream 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                AudioStream 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 = waveStream.CountSamplesPerSecond * startAtSecond * waveStream.CountBitsPerSample / 8;                int stopByteIndex = waveStream.CountSamplesPerSecond * stopAtSecond * waveStream.CountBitsPerSample / 8;                ptrRawData = new IntPtr(ptrRawData.ToInt32() + startByteIndex);                byte[] rawData = new byte[stopByteIndex - startByteIndex];                Marshal.Copy(ptrRawData, rawData, 0, rawData.Length);                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);            }             catch (Exception ex)             {                newFile.Close();                throw ex;            }            return newFile;        }



0 0
原创粉丝点击