zoukankan      html  css  js  c++  java
  • 【C#】AviFile使用(播放AVI文件,兼容性比较差)

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

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

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

    AviFile库提供了

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

      2、视频中音频的处理

      3、压缩和解压视频流

    1、使用

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

    复制代码
                //Avi文件读取
                string filepath = @"D:	est.avi";
                AviManager aviManager = new AviManager(filepath, true);
                VideoStream aviStream = aviManager.GetVideoStream();
    
                //获取和保存音频流到文件
                AudioStream audioStream = aviManager.GetWaveStream();
                audioStream.ExportStream(@"D:	est.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:	est", "*.jpg");
                AviManager aviManager = new AviManager(@"D:
    ewtest.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:
    ewtest_0-10.avi", startSecond, stopSecond);

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

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

        5、向视频流中添加一张图片帧,对于未压缩的视频流,我们可以直接进行添加,但是对于已经压缩过的视频流,添加的图片帧不能重新进行压缩,所以我们需要对其进行解压缩,操作完后在进行压缩

    复制代码
               //读取图片文件
                string[] files = Directory.GetFiles(@"D:	est", "*.jpg");
    
                //打开一个已存在的视频
                AviManager aviManager = new AviManager(@"D:	est.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:	est.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;
            }
  • 相关阅读:
    纯CSS打造可折叠树状菜单
    c++ Constructor FAQ 继续
    Java设计模式偷跑系列(六)Singleton模式的建模与实现
    优秀的产品经理是怎样炼成的?
    Pki原则
    屌丝男初中丰富的工作实践反击
    Android Material Design带UI变化
    unity多边形uv地图
    BZOJ 1208 HNOI2004 宠物收容所 平衡树/set
    [连载]Java程序设计(04)---任务驱动的方法:工资结算系统
  • 原文地址:https://www.cnblogs.com/liushunli/p/5293793.html
Copyright © 2011-2022 走看看