lipsync/Boare.Lib.Media/PipedAviWriter.cs
2024-05-20 00:17:44 +00:00

252 lines
9.8 KiB
C#

/*
* PipedAviWriter.cs
* Copyright (c) 2008-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.
*/
#if !MONO
#define TEST
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
//using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.Threading;
using bocoree;
namespace Boare.Lib.Media {
public class PipedAviWriter {
private const string _PIPE_NAME = "fifo";
private uint m_scale = 1;
private uint m_rate = 30;
#if TEST
private FileStream m_stream = null;
#else
private NamedPipeServerStream m_stream = null;
#endif
private MainAVIHeader m_main_avi_header;
private uint m_bitmapsize;
private bool m_header_written = false;
private ulong m_frames;
private Thread m_ffmpeg = null;
private PixelFormat m_pix_fmt = PixelFormat.Format24bppRgb;
private int m_bit_count = 24;
public bool AddFrame( Bitmap bmp ) {
if ( bmp.PixelFormat != m_pix_fmt ) {
return false;
}
BitmapData bdat = bmp.LockBits( new Rectangle( 0, 0, bmp.Width, bmp.Height ),
ImageLockMode.ReadOnly,
bmp.PixelFormat );
BinaryWriter bw = new BinaryWriter( new MemoryStream() );
if ( !m_header_written ) {
m_bitmapsize = (uint)(bdat.Stride * bdat.Height);
//m_stream.SetLength( (long)(0xdc + (m_bitmapsize + 0x8) * m_frames) );
/*m_main_avi_header.dwWidth = (uint)bdat.Width;
m_main_avi_header.dwHeight = (uint)bdat.Height;// bmp%infoHeader%Height
m_main_avi_header.dwMaxBytesPerSec = (uint)((float)bdat.Stride * (float)bdat.Height * (float)m_rate / (float)m_scale);
m_main_avi_header.dwStreams = 1;
m_main_avi_header.dwSuggestedBufferSize = (uint)(bdat.Stride * bdat.Height);
AVIStreamHeader stream_header = new AVIStreamHeader();
stream_header.fccType = Util.mmioFOURCC( "vids" );
stream_header.fccHandler = 0;
stream_header.dwFlags = 0;
stream_header.dwReserved1 = 0;
stream_header.dwInitialFrames = 0;
stream_header.dwScale = m_scale;
stream_header.dwRate = m_rate;
stream_header.dwStart = 0;
stream_header.dwSuggestedBufferSize = m_main_avi_header.dwSuggestedBufferSize;
stream_header.dwQuality = 0;
stream_header.dwSampleSize = 0;
BITMAPINFOHEADER bih = new BITMAPINFOHEADER(); //(BITMAPINFOHEADER)Marshal.PtrToStructure( Marshal.AllocHGlobal( sizeof( BITMAPINFOHEADER ) ), typeof( BITMAPINFOHEADER ) );
bih.biSize = (uint)(Marshal.SizeOf( bih ));
bih.biWidth = bdat.Width;
bih.biHeight = bdat.Height;
bih.biPlanes = 1;
bih.biBitCount = m_pix_fmt == PixelFormat.Format24bppRgb ? (short)24 : (short)32;
bih.biCompression = 0;//BI_RGB
bih.biSizeImage = (uint)(bdat.Stride * bdat.Height);
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
bw.Write( "RIFF".ToCharArray() );
bw.Write( (uint)(0xdc + (m_bitmapsize + 0x8) * m_frames) );
bw.Write( "AVI ".ToCharArray() );
bw.Write( "LIST".ToCharArray() );
bw.Write( (uint)0xc0 );
bw.Write( "hdrl".ToCharArray() );
bw.Write( "avih".ToCharArray() );
bw.Write( (uint)0x38 );
m_main_avi_header.Write( bw.BaseStream );
bw.Write( "LIST".ToCharArray() );
bw.Write( (uint)0x7C );
bw.Write( "strl".ToCharArray() );
bw.Write( "strh".ToCharArray() );
bw.Write( (uint)0x38 );
stream_header.Write( bw.BaseStream );
bw.Write( (uint)0x0 );
bw.Write( (uint)0x0 );
bw.Write( "strf".ToCharArray() );
bw.Write( (uint)0x28 );
bih.Write( bw.BaseStream );
bw.Write( "LIST".ToCharArray() );
bw.Write( (uint)((m_bitmapsize + 0x8) * m_frames) );
bw.Write( "movi".ToCharArray() );*/
m_header_written = true;
}
//bw.Write( "00db" );
//bw.Write( (uint)m_bitmapsize );
int address = bdat.Scan0.ToInt32();
byte[] bitmapData = new byte[bdat.Stride * bdat.Height];
Marshal.Copy( new IntPtr( address ), bitmapData, 0, bitmapData.Length );
//bw.Write( (uint)m_main_avi_header.dwSuggestedBufferSize );
bw.Write( "BM".ToCharArray() );
bw.Write( m_bitmapsize );
bw.Write( (uint)0x0 );
bw.Write( (uint)0x36 );
BITMAPINFOHEADER bih = new BITMAPINFOHEADER(); //(BITMAPINFOHEADER)Marshal.PtrToStructure( Marshal.AllocHGlobal( sizeof( BITMAPINFOHEADER ) ), typeof( BITMAPINFOHEADER ) );
bih.biSize = (uint)(Marshal.SizeOf( bih ));
bih.biWidth = bdat.Width;
bih.biHeight = bdat.Height;
bih.biPlanes = 1;
bih.biBitCount = m_pix_fmt == PixelFormat.Format24bppRgb ? (short)24 : (short)32;
bih.biCompression = 0;//BI_RGB
bih.biSizeImage = (uint)(bdat.Stride * bdat.Height);
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
bih.Write( bw );
bw.Write( bitmapData, 0, bitmapData.Length );
//const int _BUF_LEN = 512;
byte[] buf = new byte[m_bitmapsize + 6];
bw.BaseStream.Seek( 0, SeekOrigin.Begin );
int len = bw.BaseStream.Read( buf, 0, (int)(m_bitmapsize + 6) );
if ( len > 0 ) {
m_stream.BeginWrite( buf, 0, len, null, null );
}
m_stream.Flush();
bw.Close();
bmp.UnlockBits( bdat );
return true;
}
private void WriteFourCC( string value ) {
byte[] b = new byte[4];
for ( int i = 0; i < 4; i++ ) {
b[i] = (byte)value[i];
}
m_stream.Write( b, 0, 4 );
}
private void Write4Byte( uint value ) {
byte[] b;
b = BitConverter.GetBytes( value );
if ( !BitConverter.IsLittleEndian ) {
Array.Reverse( b );
}
m_stream.Write( b, 0, 4 );
}
public void Close() {
if ( m_stream != null ) {
#if !TEST
m_stream.Disconnect();
#endif
m_stream.Close();
}
if ( m_ffmpeg != null ) {
if ( m_ffmpeg.IsAlive ) {
m_ffmpeg.Abort();
while ( m_ffmpeg.IsAlive ) {
}
}
}
}
public void Open( uint scale, uint rate, ulong frames, PixelFormat pix_fmt ) {
if ( m_stream != null ) {
m_stream.Close();
}
#if TEST
m_stream = new FileStream( "test.out.avi", FileMode.Create );
#else
m_stream = new NamedPipeServerStream( _PIPE_NAME, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.None, 1, 1 );
#endif
m_scale = scale;
m_rate = rate;
m_frames = frames;
m_main_avi_header.dwMicroSecPerFrame = (uint)(1.0e6 * (double)scale / (double)rate);// ! 1秒は10^6μ秒
m_main_avi_header.dwReserved1 = 0;
m_main_avi_header.dwFlags = 2064;
m_main_avi_header.dwInitialFrames = 0;
m_main_avi_header.dwStreams = 0;
m_main_avi_header.dwScale = scale;
m_main_avi_header.dwRate = rate;
m_main_avi_header.dwStart = 0;
m_main_avi_header.dwLength = 0;
m_main_avi_header.dwTotalFrames = (uint)m_frames;
m_pix_fmt = pix_fmt;
if ( m_pix_fmt != PixelFormat.Format24bppRgb && m_pix_fmt != PixelFormat.Format32bppArgb ) {
throw new ApplicationException( "pixel format not supported" );
}
#if !TEST
m_ffmpeg = new Thread( new ThreadStart( FFmpegEnc ) );
m_ffmpeg.Start();
m_stream.WaitForConnection();
#endif
}
private void FFmpegEnc() {
Thread.Sleep( 1000 );
Process client = new Process();
client.StartInfo.FileName = "ffmpeg";
// windowsの場合
client.StartInfo.Arguments = @"-f image2pipe -vcodec bmp -i \\.\pipe\" + _PIPE_NAME + " -isync -an -f avi -y test.avi";
// その他
// client.StartInfo.Arguments = @"-i " + _PIPE_NAME + " -y test.mp3";
client.StartInfo.RedirectStandardOutput = true;
client.StartInfo.UseShellExecute = false;
client.Start();
StreamReader sr = client.StandardOutput;
string line = "";
while ( (line = sr.ReadLine()) != null ) {
Console.WriteLine( line );
}
}
}
}
#endif