AVIファイルからBMPを作成する
id:Seasonsさんとこより。
http://d.hatena.ne.jp/Seasons/20071223/1198420293
今度は、Excel上で動画ですか! どーやっているんだろう・・・
私が実現するとしたら、
って感じかしら。
じゃ、やってみようっと。
先ずは、AVIからBMPの切り出し。Video for Windows APIを使えば切り出せますが、API直呼びはやめて、C#でラッパークラスを作成します。C++/CLIを使わずにP/Invokeで押し通すのは私の趣味(^^;。
using System; using System.Drawing; using System.Runtime.InteropServices; using System.IO; namespace AviFileUtil { static class BitmapUtil { [StructLayout(LayoutKind.Sequential, Pack = 1)] struct BITMAPFILEHEADER { public ushort bfType; public uint bfSize; public ushort bfReserved1; public ushort bfReserved2; public uint bfOffBits; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct BITMAPINFOHEADER { public uint biSize; public int biWidth; public int biHeight; public ushort biPlanes; public ushort biBitCount; public uint biCompression; public uint biSizeImage; public int biXPelsPerMeter; public int biYPelsPerMeter; public uint biClrUsed; public uint biClrImportant; public const int BI_RGB = 0; } const uint DIB_RGB_COLORS = 0; /* color table in RGBs */ const uint DIB_PAL_COLORS = 1; /* color table in palette indices */ public static Bitmap ToBitmap(IntPtr pBITMAPINFOHEADER) { unsafe { BITMAPINFOHEADER* pbmi = (BITMAPINFOHEADER*)pBITMAPINFOHEADER; BITMAPFILEHEADER pbmfi; pbmfi.bfType = (int)'M' << 8 | (int)'B'; pbmfi.bfOffBits = (uint)(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)); pbmfi.bfSize = pbmfi.bfOffBits + pbmi->biSizeImage; MemoryStream stream = new MemoryStream(); BinaryWriter bw = new BinaryWriter(stream); byte[] data = new byte[sizeof(BITMAPFILEHEADER)]; Marshal.Copy((IntPtr)(&pbmfi), data, 0, data.Length); bw.Write(data); data = new byte[sizeof(BITMAPINFOHEADER)]; Marshal.Copy(pBITMAPINFOHEADER, data, 0, data.Length); bw.Write(data); data = new byte[pbmi->biSizeImage]; ++pbmi; Marshal.Copy((IntPtr)pbmi, data, 0, data.Length); bw.Write(data); bw.Flush(); bw.BaseStream.Position = 0; return new Bitmap(bw.BaseStream); } } } /// <summary> /// Video for Windows APIを手軽に使うためのラッパークラス。 /// </summary> public class AviFile : IDisposable { const string AVIFILE32 = "AVIFIL32"; const int AVIGETFRAMEF_BESTDISPLAYFMT = 1; internal enum OpenFileFlags : uint { OF_READ = 0x00000000, OF_WRITE = 0x00000001, OF_READWRITE = 0x00000002, OF_SHARE_COMPAT = 0x00000000, OF_SHARE_EXCLUSIVE = 0x00000010, OF_SHARE_DENY_WRITE = 0x00000020, OF_SHARE_DENY_READ = 0x00000030, OF_SHARE_DENY_NONE = 0x00000040, OF_PARSE = 0x00000100, OF_DELETE = 0x00000200, OF_VERIFY = 0x00000400, OF_CANCEL = 0x00000800, OF_CREATE = 0x00001000, OF_PROMPT = 0x00002000, OF_EXIST = 0x00004000, OF_REOPEN = 0x00008000, } #region DllImport [DllImport(AVIFILE32)] extern internal static void AVIFileInit(); [DllImport(AVIFILE32)] extern internal static void AVIFileExit(); [DllImport(AVIFILE32)] extern internal static uint AVIFileOpen(out IntPtr ppfile, string szFile, OpenFileFlags mode, IntPtr pclsidHandler); [DllImport(AVIFILE32)] extern internal static int AVIFileRelease(IntPtr pfile); [DllImport(AVIFILE32)] extern internal static uint AVIFileGetStream(IntPtr pfile, out IntPtr ppavi, uint fccType, int lParam); [DllImport(AVIFILE32)] extern internal static int AVIStreamRelease(IntPtr pavi); [DllImport(AVIFILE32)] extern internal static IntPtr AVIStreamGetFrameOpen(IntPtr pavi, int lpbiWanted); [DllImport(AVIFILE32)] extern internal static IntPtr AVIStreamGetFrame(IntPtr pgf, int lPos); [DllImport(AVIFILE32)] extern internal static int AVIStreamLength(IntPtr pavi); [DllImport(AVIFILE32)] extern internal static uint AVIStreamGetFrameClose(IntPtr pget); #endregion static uint mmioFOURCC(char c0, char c1, char c2, char c3) { return (uint)c3 << 24 | (uint)c2 << 16 | (uint)c1 << 8 | (uint)c0; } static readonly uint streamtypeVIDEO = mmioFOURCC('v', 'i', 'd', 's'); static readonly uint streamtypeAUDIO = mmioFOURCC('a', 'u', 'd', 's'); static readonly uint streamtypeMIDI = mmioFOURCC('m', 'i', 'd', 's'); static readonly uint streamtypeTEXT = mmioFOURCC('t', 'x', 't', 's'); IntPtr aviFile = IntPtr.Zero; IntPtr aviStream = IntPtr.Zero; bool disposed = false; public static void Initialize() { AVIFileInit(); } public static void Terminate() { AVIFileExit(); } public AviFile(string filename) { uint result; result = AVIFileOpen(out aviFile, filename, OpenFileFlags.OF_READ, IntPtr.Zero); if (result != 0) { Release(); throw new Exception("AVIFileOpen failure."); } result = AVIFileGetStream(aviFile, out aviStream, streamtypeVIDEO, 0); if (result != 0) { Release(); throw new Exception("AVIFileGetStream failure."); } } ~AviFile() { Dispose(false); } void Release() { if (aviStream != IntPtr.Zero) { AVIStreamRelease(aviStream); aviStream = IntPtr.Zero; } if (aviFile != IntPtr.Zero) { AVIFileRelease(aviFile); aviFile = IntPtr.Zero; } } public int GetMaxFrameCount() { if (aviStream == IntPtr.Zero) throw new InvalidOperationException(); return AVIStreamLength(aviStream); } public Bitmap GetFrame(int no) { if (aviStream == IntPtr.Zero) throw new InvalidOperationException(); IntPtr frame = IntPtr.Zero; try { frame = AVIStreamGetFrameOpen(aviStream, AVIGETFRAMEF_BESTDISPLAYFMT); IntPtr pbmi = AVIStreamGetFrame(frame, no); return BitmapUtil.ToBitmap(pbmi); } finally { if (frame != IntPtr.Zero) AVIStreamGetFrameClose(frame); } } protected void Dispose(bool disposing) { if (disposed) return; disposed = true; Release(); } #region IDisposable Members public void Dispose() { GC.SuppressFinalize(this); Dispose(true); } #endregion } }
このクラスを使って切り出しします。
using System; using System.IO; using AviFileUtil; using System.Drawing; namespace WindowsFormsApplication1 { static class Program { public static void Main() { string filename = "op1_.avi"; AviFile.Initialize(); using (AviFile avi = new AviFile(filename)) { int max = avi.GetMaxFrameCount(); for (int i = 0; i < max; ++i) { Bitmap bmp = avi.GetFrame(i); bmp.Save(i + ".bmp"); bmp.Dispose(); } } AviFile.Terminate(); } } }
試した動画では1000ファイルくらいのBMPが作成されました。で、これを以前作ったbmp2xls.pyで変換・・・なのですが、この処理は非常に時間が掛かります。連休も終わりなので、ここで時間切れ。技術的興味は満たせたので、ま、いいか(^^;