mirror of
https://git.femboyfinancial.jp/james/lipsync.git
synced 2024-11-24 19:51:59 -08:00
497 lines
20 KiB
C#
497 lines
20 KiB
C#
/*
|
|
* VfwAviWriter.cs
|
|
* Copyright (c) 2009 kbinani
|
|
*
|
|
* This file is part of Boare.Lib.Media.
|
|
*
|
|
* Boare.Lib.Media is free software; you can redistribute it and/or
|
|
* modify it under the terms of the BSD License.
|
|
*
|
|
* Boare.Lib.Media is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*/
|
|
using System;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.Runtime.InteropServices;
|
|
using System.Windows.Forms;
|
|
using System.IO;
|
|
|
|
namespace Boare.Lib.Media {
|
|
|
|
public class AviWriterVfw : IAviWriter {
|
|
private int m_file_handle = 0;
|
|
private IntPtr m_video = new IntPtr( 0 );
|
|
private IntPtr m_video_compressed = new IntPtr( 0 );
|
|
private IntPtr m_audio = new IntPtr( 0 );
|
|
private IntPtr m_audio_compressed = new IntPtr( 0 );
|
|
private uint m_scale;
|
|
private uint m_rate;
|
|
private int m_count = 0;
|
|
private UInt32 m_width = 0;
|
|
private UInt32 m_stride = 0;
|
|
private UInt32 m_height = 0;
|
|
private static readonly UInt32 _STREAM_TYPE_VIDEO = (UInt32)mmioFOURCC( 'v', 'i', 'd', 's' );
|
|
private static readonly UInt32 _STREAM_TYPE_AUDIO = (UInt32)mmioFOURCC( 'a', 'u', 'd', 's' );
|
|
private uint m_strh_fcc = 0;
|
|
private string m_file = "";
|
|
|
|
private const int OF_READ = 0;
|
|
private const int OF_READWRITE = 2;
|
|
private const int OF_WRITE = 1;
|
|
private const int OF_SHARE_COMPAT = 0;
|
|
private const int OF_SHARE_DENY_NONE = 64;
|
|
private const int OF_SHARE_DENY_READ = 48;
|
|
private const int OF_SHARE_DENY_WRITE = 32;
|
|
private const int OF_SHARE_EXCLUSIVE = 16;
|
|
private const int OF_CREATE = 4096;
|
|
|
|
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
|
|
private struct AVISTREAMINFOW {
|
|
public UInt32 fccType, fccHandler, dwFlags, dwCaps;
|
|
|
|
public UInt16 wPriority, wLanguage;
|
|
|
|
public UInt32 dwScale, dwRate,
|
|
dwStart, dwLength, dwInitialFrames, dwSuggestedBufferSize,
|
|
dwQuality, dwSampleSize, rect_left, rect_top,
|
|
rect_right, rect_bottom, dwEditCount, dwFormatChangeCount;
|
|
public UInt16 szName0, szName1, szName2, szName3, szName4, szName5,
|
|
szName6, szName7, szName8, szName9, szName10, szName11,
|
|
szName12, szName13, szName14, szName15, szName16, szName17,
|
|
szName18, szName19, szName20, szName21, szName22, szName23,
|
|
szName24, szName25, szName26, szName27, szName28, szName29,
|
|
szName30, szName31, szName32, szName33, szName34, szName35,
|
|
szName36, szName37, szName38, szName39, szName40, szName41,
|
|
szName42, szName43, szName44, szName45, szName46, szName47,
|
|
szName48, szName49, szName50, szName51, szName52, szName53,
|
|
szName54, szName55, szName56, szName57, szName58, szName59,
|
|
szName60, szName61, szName62, szName63;
|
|
}
|
|
|
|
// vfw.h
|
|
[Serializable]
|
|
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
|
|
public struct AVICOMPRESSOPTIONS {
|
|
public UInt32 fccType;
|
|
public UInt32 fccHandler;
|
|
public UInt32 dwKeyFrameEvery; // only used with AVICOMRPESSF_KEYFRAMES
|
|
public UInt32 dwQuality;
|
|
public UInt32 dwBytesPerSecond; // only used with AVICOMPRESSF_DATARATE
|
|
public UInt32 dwFlags;
|
|
public IntPtr lpFormat;
|
|
public UInt32 cbFormat;
|
|
public IntPtr lpParms;
|
|
public UInt32 cbParms;
|
|
public UInt32 dwInterleaveEvery;
|
|
public override string ToString() {
|
|
return "fccType=" + fccType + "\n" +
|
|
"fccHandler=" + fccHandler + "\n" +
|
|
"dwKeyFrameEvery=" + dwKeyFrameEvery + "\n" +
|
|
"dwQuality=" + dwQuality + "\n" +
|
|
"dwBytesPerSecond=" + dwBytesPerSecond + "\n" +
|
|
"dwFlags=" + dwFlags + "\n" +
|
|
"lpFormat=" + lpFormat + "\n" +
|
|
"cbFormat=" + cbFormat + "\n" +
|
|
"lpParms=" + lpParms + "\n" +
|
|
"cbParms=" + cbParms + "\n" +
|
|
"dwInterleaveEvery=" + dwInterleaveEvery;
|
|
}
|
|
}
|
|
|
|
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
|
|
public struct BITMAPINFOHEADER {
|
|
public UInt32 biSize;
|
|
public Int32 biWidth;
|
|
public Int32 biHeight;
|
|
public Int16 biPlanes;
|
|
public Int16 biBitCount;
|
|
public UInt32 biCompression;
|
|
public UInt32 biSizeImage;
|
|
public Int32 biXPelsPerMeter;
|
|
public Int32 biYPelsPerMeter;
|
|
public UInt32 biClrUsed;
|
|
public UInt32 biClrImportant;
|
|
}
|
|
|
|
public Size Size {
|
|
get {
|
|
return new Size( (int)m_width, (int)m_height );
|
|
}
|
|
}
|
|
|
|
public uint Scale {
|
|
get {
|
|
return m_scale;
|
|
}
|
|
}
|
|
|
|
public uint Rate {
|
|
get {
|
|
return m_rate;
|
|
}
|
|
}
|
|
|
|
public class AviException : ApplicationException {
|
|
public AviException( string s )
|
|
: base( s ) {
|
|
}
|
|
public AviException( string s, Int32 hr )
|
|
: base( s ) {
|
|
|
|
if ( hr == AVIERR_BADPARAM ) {
|
|
err_msg = "AVIERR_BADPARAM";
|
|
} else {
|
|
err_msg = "unknown";
|
|
}
|
|
}
|
|
|
|
public string ErrMsg() {
|
|
return err_msg;
|
|
}
|
|
private const Int32 AVIERR_BADPARAM = -2147205018;
|
|
private string err_msg;
|
|
}
|
|
|
|
public bool Open( string file_name, uint scale, uint rate, int width, int height, IntPtr hwnd ) {
|
|
m_file = file_name;
|
|
this.m_scale = scale;
|
|
this.m_rate = rate;
|
|
this.m_width = (UInt32)width;
|
|
this.m_height = (UInt32)height;
|
|
using ( Bitmap bmp = new Bitmap( width, height, PixelFormat.Format24bppRgb ) ) {
|
|
BitmapData bmpDat = bmp.LockBits( new Rectangle( 0, 0, width, height ), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb );
|
|
this.m_stride = (UInt32)bmpDat.Stride;
|
|
bmp.UnlockBits( bmpDat );
|
|
}
|
|
AVIFileInit();
|
|
int hr = AVIFileOpenW( ref m_file_handle, file_name, OF_WRITE | OF_CREATE, 0 );
|
|
if ( hr != 0 ) {
|
|
throw new AviException( "error for AVIFileOpenW" );
|
|
}
|
|
|
|
CreateStream();
|
|
return SetOptions( hwnd );
|
|
}
|
|
|
|
public void AddFrame( Bitmap bmp ) {
|
|
BitmapData bmpDat = bmp.LockBits( new Rectangle( 0, 0, (int)m_width, (int)m_height ),
|
|
ImageLockMode.ReadOnly,
|
|
PixelFormat.Format24bppRgb );
|
|
|
|
int hr = AVIStreamWrite( m_video_compressed, m_count, 1,
|
|
bmpDat.Scan0,
|
|
(Int32)(m_stride * m_height),
|
|
0,
|
|
0,
|
|
0 );
|
|
|
|
if ( hr != 0 ) {
|
|
throw new AviException( "AVIStreamWrite" );
|
|
}
|
|
|
|
bmp.UnlockBits( bmpDat );
|
|
|
|
m_count++;
|
|
}
|
|
|
|
unsafe public static AVICOMPRESSOPTIONS RequireVideoCompressOption( AVICOMPRESSOPTIONS current_option ) {
|
|
AviWriterVfw temp = new AviWriterVfw();
|
|
temp.m_scale = 1000;
|
|
temp.m_rate = 30 * temp.m_scale;
|
|
int width = 10, height = 10;
|
|
temp.m_width = (UInt32)width;
|
|
temp.m_height = (UInt32)height;
|
|
using ( Bitmap bmp = new Bitmap( width, height, PixelFormat.Format24bppRgb ) ) {
|
|
BitmapData bmpDat = bmp.LockBits( new Rectangle( 0, 0, width, height ), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb );
|
|
temp.m_stride = (UInt32)bmpDat.Stride;
|
|
bmp.UnlockBits( bmpDat );
|
|
}
|
|
AVIFileInit();
|
|
string temp_file = Path.GetTempFileName() + ".avi";// .aviを付けないと、AVIFileOpenWが失敗する
|
|
int hr = AVIFileOpenW( ref temp.m_file_handle, temp_file, OF_WRITE | OF_CREATE, 0 );
|
|
if ( hr != 0 ) {
|
|
throw new AviException( "error for AVIFileOpenW" );
|
|
}
|
|
|
|
temp.CreateStream();
|
|
|
|
AVICOMPRESSOPTIONS opts = new AVICOMPRESSOPTIONS();
|
|
opts.fccType = 0; //fccType_;
|
|
opts.fccHandler = 0;//fccHandler_;
|
|
opts.dwKeyFrameEvery = 0;
|
|
opts.dwQuality = 0; // 0 .. 10000
|
|
opts.dwFlags = 0; // AVICOMRPESSF_KEYFRAMES = 4
|
|
opts.dwBytesPerSecond = 0;
|
|
opts.lpFormat = new IntPtr( 0 );
|
|
opts.cbFormat = 0;
|
|
opts.lpParms = new IntPtr( 0 );
|
|
opts.cbParms = 0;
|
|
opts.dwInterleaveEvery = 0;
|
|
|
|
opts = current_option;
|
|
AVICOMPRESSOPTIONS* p = &opts;
|
|
AVICOMPRESSOPTIONS** pp = &p;
|
|
IntPtr x = temp.m_video;
|
|
IntPtr* ptr_ps = &x;
|
|
AVISaveOptions( IntPtr.Zero, 0, 1, ptr_ps, pp );
|
|
//MessageBox.Show( "AVISaveOptions ok" );
|
|
AVICOMPRESSOPTIONS copied = new AVICOMPRESSOPTIONS();
|
|
copied = opts;
|
|
AVIStreamRelease( temp.m_video );
|
|
//MessageBox.Show( "AVIStreamRelease(temp.m_video) ok" );
|
|
|
|
AVIFileRelease( temp.m_file_handle );
|
|
//MessageBox.Show( "AVIFileRelease ok" );
|
|
AVIFileExit();
|
|
//MessageBox.Show( "AVIFileExit ok" );
|
|
File.Delete( temp_file );
|
|
//MessageBox.Show( "File.Delete(fileName) ok" );
|
|
return copied;
|
|
}
|
|
|
|
/// <summary>
|
|
/// オーディオ圧縮の設定ダイアログを表示し、オーディオ圧縮の設定を取得します
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
unsafe public static AVICOMPRESSOPTIONS RequireAudioCompressOption() {
|
|
string temp_file = Path.GetTempFileName();
|
|
byte[] buf = new byte[] {
|
|
82, 73, 70, 70, 94, 0, 0, 0, 87, 65, 86, 69, 102, 109, 116, 32, 16, 0, 0, 0, 1,
|
|
0, 1, 0, 64, 31, 0, 0, 64, 31, 0, 0, 1, 0, 8, 0, 100, 97, 116, 97, 58, 0, 0, 0,
|
|
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
|
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
|
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
|
128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
|
};
|
|
using ( FileStream fs = new FileStream( temp_file, FileMode.Create ) ) {
|
|
fs.Write( buf, 0, buf.Length );
|
|
}
|
|
|
|
AVIFileInit();
|
|
//MessageBox.Show( "AVIFileInit ok" );
|
|
IntPtr audio;
|
|
int hr = AVIStreamOpenFromFileW( out audio, temp_file, _STREAM_TYPE_AUDIO, 0, OF_READ, 0 );
|
|
//MessageBox.Show( "AVIStreamOpenFromFileW ok" );
|
|
|
|
AVICOMPRESSOPTIONS opts = new AVICOMPRESSOPTIONS();
|
|
opts.fccType = _STREAM_TYPE_AUDIO; //fccType_;
|
|
opts.fccHandler = 0;//fccHandler_;
|
|
opts.dwKeyFrameEvery = 0;
|
|
opts.dwQuality = 0; // 0 .. 10000
|
|
opts.dwFlags = 0; // AVICOMRPESSF_KEYFRAMES = 4
|
|
opts.dwBytesPerSecond = 0;
|
|
opts.lpFormat = new IntPtr( 0 );
|
|
opts.cbFormat = 0;
|
|
opts.lpParms = new IntPtr( 0 );
|
|
opts.cbParms = 0;
|
|
opts.dwInterleaveEvery = 0;
|
|
AVICOMPRESSOPTIONS* p = &opts;
|
|
AVICOMPRESSOPTIONS** pp = &p;
|
|
IntPtr x = audio;
|
|
IntPtr* ptr_ps = &x;
|
|
AVISaveOptions( IntPtr.Zero, 0, 1, ptr_ps, pp );
|
|
//MessageBox.Show( "AVISaveOptions ok" );
|
|
AVICOMPRESSOPTIONS copied = new AVICOMPRESSOPTIONS();
|
|
copied = opts;
|
|
AVIStreamRelease( audio );
|
|
//MessageBox.Show( "AVIStreamRelease(audio) ok" );
|
|
|
|
AVIFileExit();
|
|
//MessageBox.Show( "AVIFileExit ok" );
|
|
File.Delete( temp_file );
|
|
return copied;
|
|
}
|
|
|
|
public void Close() {
|
|
AVIStreamRelease( m_video );
|
|
AVIStreamRelease( m_video_compressed );
|
|
AVIFileRelease( m_file_handle );
|
|
AVIFileExit();
|
|
using ( FileStream fs = new FileStream( m_file, FileMode.Open ) ) {
|
|
fs.Seek( 0x70, SeekOrigin.Begin );
|
|
byte ch3 = (byte)(m_strh_fcc >> 24);
|
|
uint b = (uint)(m_strh_fcc - (ch3 << 24));
|
|
byte ch2 = (byte)(b >> 16);
|
|
b = (uint)(b - (ch2 << 16));
|
|
byte ch1 = (byte)(b >> 8);
|
|
byte ch0 = (byte)(b - (ch1 << 8));
|
|
fs.Write( new byte[] { ch0, ch1, ch2, ch3 }, 0, 4 );
|
|
}
|
|
}
|
|
|
|
private void CreateStream() {
|
|
// video stream
|
|
AVISTREAMINFOW strhdr = new AVISTREAMINFOW();
|
|
strhdr.fccType = _STREAM_TYPE_VIDEO;
|
|
strhdr.fccHandler = 0;// fccHandler_;
|
|
strhdr.dwFlags = 0;
|
|
strhdr.dwCaps = 0;
|
|
strhdr.wPriority = 0;
|
|
strhdr.wLanguage = 0;
|
|
strhdr.dwScale = m_scale;
|
|
strhdr.dwRate = m_rate;
|
|
strhdr.dwStart = 0;
|
|
strhdr.dwLength = 0;
|
|
strhdr.dwInitialFrames = 0;
|
|
strhdr.dwSuggestedBufferSize = m_height * m_stride;
|
|
strhdr.dwQuality = 0xffffffff;
|
|
strhdr.dwSampleSize = 0;
|
|
strhdr.rect_top = 0;
|
|
strhdr.rect_left = 0;
|
|
strhdr.rect_bottom = m_height;
|
|
strhdr.rect_right = m_width;
|
|
strhdr.dwEditCount = 0;
|
|
strhdr.dwFormatChangeCount = 0;
|
|
strhdr.szName0 = 0;
|
|
strhdr.szName1 = 0;
|
|
int hr = AVIFileCreateStream( m_file_handle, out m_video, ref strhdr );
|
|
if ( hr != 0 ) {
|
|
throw new AviException( "AVIFileCreateStream; Video" );
|
|
}
|
|
#if DEBUG
|
|
Console.WriteLine( "AviWrierVfw+CreateStream" );
|
|
Console.WriteLine( " strhdr.fccHandler=" + strhdr.fccHandler );
|
|
#endif
|
|
}
|
|
|
|
internal static void CalcScaleAndRate( decimal fps, out uint scale, out uint rate ) {
|
|
scale = 100;
|
|
rate = (uint)(fps * 1000m);
|
|
int max = (int)(Math.Log10( uint.MaxValue ));
|
|
for ( int i = 0; i <= max; i++ ) {
|
|
scale = (uint)pow10( i );
|
|
rate = (uint)(fps * scale);
|
|
decimal t_fps = (decimal)rate / (decimal)scale;
|
|
if ( t_fps == fps ) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static int pow10( int x ) {
|
|
int result = 1;
|
|
for ( int i = 1; i <= x; i++ ) {
|
|
result = result * 10;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
unsafe private bool SetOptions( IntPtr hwnd ) {
|
|
// VIDEO
|
|
AVICOMPRESSOPTIONS opts = new AVICOMPRESSOPTIONS();
|
|
opts.fccType = 0;
|
|
opts.fccHandler = 0;
|
|
opts.dwKeyFrameEvery = 0;
|
|
opts.dwQuality = 0;
|
|
opts.dwFlags = 0;
|
|
opts.dwBytesPerSecond = 0;
|
|
opts.lpFormat = new IntPtr( 0 );
|
|
opts.cbFormat = 0;
|
|
opts.lpParms = new IntPtr( 0 );
|
|
opts.cbParms = 0;
|
|
opts.dwInterleaveEvery = 0;
|
|
|
|
AVICOMPRESSOPTIONS* p = &opts;
|
|
AVICOMPRESSOPTIONS** pp;
|
|
pp = &p;
|
|
IntPtr x = m_video;
|
|
IntPtr* ptr_ps;
|
|
ptr_ps = &x;
|
|
if ( AVISaveOptions( hwnd, 0, 1, ptr_ps, pp ) == 0 ) {
|
|
return false;
|
|
}
|
|
int hr = AVIMakeCompressedStream( out m_video_compressed, m_video, ref opts, 0 );
|
|
if ( hr != 0 ) {
|
|
throw new AviException( "AVIMakeCompressedStream; Video" );
|
|
}
|
|
m_strh_fcc = opts.fccHandler;
|
|
#if DEBUG
|
|
Console.WriteLine( "AviWriterVfw+SetOptions" );
|
|
Console.WriteLine( " opts.fccHandler=" + opts.fccHandler );
|
|
#endif
|
|
|
|
// TODO: AVISaveOptionsFree(...)
|
|
BITMAPINFO bi = new BITMAPINFO();
|
|
bi.bmiHeader.biSize = 40;
|
|
bi.bmiHeader.biWidth = (Int32)m_width;
|
|
bi.bmiHeader.biHeight = (Int32)m_height;
|
|
bi.bmiHeader.biPlanes = 1;
|
|
bi.bmiHeader.biBitCount = 24;
|
|
bi.bmiHeader.biCompression = 0;
|
|
bi.bmiHeader.biSizeImage = m_stride * m_height;
|
|
bi.bmiHeader.biXPelsPerMeter = 0;
|
|
bi.bmiHeader.biYPelsPerMeter = 0;
|
|
bi.bmiHeader.biClrUsed = 0;
|
|
bi.bmiHeader.biClrImportant = 0;
|
|
|
|
hr = AVIStreamSetFormat( m_video_compressed, 0, ref bi, sizeof( BITMAPINFO ) );
|
|
if ( hr != 0 ) {
|
|
throw new AviException( "AVIStreamSetFormat", hr );
|
|
}
|
|
#if DEBUG
|
|
Console.WriteLine( " bi.bmiHeader.biCompression=" + bi.bmiHeader.biCompression );
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
[DllImport( "avifil32.dll" )]
|
|
private static extern int AVIStreamOpenFromFileW( out IntPtr ppavi,
|
|
[MarshalAs( UnmanagedType.LPWStr )]string szfile,
|
|
uint fccType,
|
|
int lParam,
|
|
int mode,
|
|
int dumy );
|
|
|
|
[DllImport( "avifil32.dll" )]
|
|
private static extern void AVIFileInit();
|
|
|
|
[DllImport( "avifil32.dll" )]
|
|
private static extern int AVIFileOpenW( ref int ptr_pfile,
|
|
[MarshalAs( UnmanagedType.LPWStr )]string fileName,
|
|
int flags,
|
|
int dummy );
|
|
|
|
[DllImport( "avifil32.dll" )]
|
|
private static extern int AVIFileCreateStream(
|
|
int ptr_pfile, out IntPtr ptr_ptr_avi, ref AVISTREAMINFOW ptr_streaminfo );
|
|
|
|
[DllImport( "avifil32.dll" )]
|
|
private static extern int AVIMakeCompressedStream(
|
|
out IntPtr ppsCompressed, IntPtr aviStream, ref AVICOMPRESSOPTIONS ao, int dummy );
|
|
|
|
[DllImport( "avifil32.dll" )]
|
|
private static extern int AVIStreamSetFormat(
|
|
IntPtr aviStream, Int32 lPos, ref BITMAPINFO lpFormat, Int32 cbFormat );
|
|
|
|
[DllImport( "avifil32.dll" )]
|
|
unsafe private static extern int AVISaveOptions(
|
|
IntPtr hwnd, UInt32 flags, int nStreams, IntPtr* ptr_ptr_avi, AVICOMPRESSOPTIONS** ao );
|
|
|
|
[DllImport( "avifil32.dll" )]
|
|
private static extern int AVIStreamWrite( IntPtr aviStream,
|
|
Int32 lStart,
|
|
Int32 lSamples,
|
|
IntPtr lpBuffer,
|
|
Int32 cbBuffer,
|
|
Int32 dwFlags,
|
|
Int32 dummy1,
|
|
Int32 dummy2 );
|
|
|
|
[DllImport( "avifil32.dll" )]
|
|
private static extern int AVIStreamRelease( IntPtr aviStream );
|
|
|
|
[DllImport( "avifil32.dll" )]
|
|
private static extern int AVIFileRelease( int pfile );
|
|
|
|
[DllImport( "avifil32.dll" )]
|
|
private static extern void AVIFileExit();
|
|
|
|
public static Int32 mmioFOURCC( char ch0, char ch1, char ch2, char ch3 ) {
|
|
return ((Int32)(byte)(ch0) | ((byte)(ch1) << 8) | ((byte)(ch2) << 16) | ((byte)(ch3) << 24));
|
|
}
|
|
}
|
|
|
|
}
|