/* This class has been written by
* Corinna John (Hannover, Germany)
* cj@binary-universe.net
*
* You may do with this code whatever you like,
* except selling it or claiming any rights/ownership.
*
* Please send me a little feedback about what you're
* using this code for and what changes you'd like to
* see in later versions. (And please excuse my bad english.)
*
* WARNING: This is experimental code.
* Please do not expect "Release Quality".
* */
using System;
using System.IO;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace Scurvy.Media.Avi
{
internal sealed class AviManager
{
private int aviFile = 0;
private ArrayList streams = new ArrayList();
/// Open or create an AVI file
/// Name of the AVI file
/// true: Open the file; false: Create or overwrite the file
public AviManager(String fileName, bool open){
Avi.AVIFileInit();
int result;
if(open){ //open existing file
result = Avi.AVIFileOpen(
ref aviFile, fileName,
Avi.OF_READWRITE, 0);
}else{ //create empty file
result = Avi.AVIFileOpen(
ref aviFile, fileName,
Avi.OF_WRITE | Avi.OF_CREATE, 0);
}
if(result != 0) {
throw new Exception("Exception in AVIFileOpen: "+result.ToString());
}
}
private AviManager(int aviFile) {
this.aviFile = aviFile;
}
/// Get the first video stream - usually there is only one video stream
/// VideoStream object for the stream
public AviVideoStream GetVideoStream(){
IntPtr aviStream;
int result = Avi.AVIFileGetStream(
aviFile,
out aviStream,
Avi.streamtypeVIDEO, 0);
if(result != 0){
throw new Exception("Exception in AVIFileGetStream: "+result.ToString());
}
AviVideoStream stream = new AviVideoStream(aviFile, aviStream);
streams.Add(stream);
return stream;
}
/// Getthe first wave audio stream
/// AudioStream object for the stream
public AviAudioStream GetWaveStream(){
IntPtr aviStream;
int result = Avi.AVIFileGetStream(
aviFile,
out aviStream,
Avi.streamtypeAUDIO, 0);
if(result != 0){
throw new Exception("Exception in AVIFileGetStream: "+result.ToString());
}
AviAudioStream stream = new AviAudioStream(aviFile, aviStream);
streams.Add(stream);
return stream;
}
/// Get a stream from the internal list of opened streams
/// Index of the stream. The streams are not sorted, the first stream is the one that was opened first.
/// VideoStream at position [index]
///
/// Use this method after DecompressToNewFile,
/// to get the copied stream from the new AVI file
///
///
/// //streams cannot be edited - copy to a new file
/// AviManager newManager = aviStream.DecompressToNewFile(@"..\..\testdata\temp.avi", true);
/// //there is only one stream in the new file - get it and add a frame
/// VideoStream aviStream = newManager.GetOpenStream(0);
/// aviStream.AddFrame(bitmap);
///
public AviVideoStream GetOpenStream(int index){
return (AviVideoStream)streams[index];
}
/// Add an empty video stream to the file
/// true: Create a compressed stream before adding frames
/// Frames per second
/// Size of one frame in bytes
/// Width of each image
/// Height of each image
/// PixelFormat of the images
/// VideoStream object for the new stream
public AviVideoStream AddVideoStream(bool isCompressed, double frameRate, int frameSize, int width, int height, PixelFormat format){
AviVideoStream stream = new AviVideoStream(aviFile, isCompressed, frameRate, frameSize, width, height, format);
streams.Add(stream);
return stream;
}
/// Add an empty video stream to the file
/// Compresses the stream without showing the codecs dialog
/// Compression options
/// Frames per second
/// Image to write into the stream as the first frame
/// VideoStream object for the new stream
public AviVideoStream AddVideoStream(Avi.AVICOMPRESSOPTIONS compressOptions, double frameRate, Bitmap firstFrame) {
AviVideoStream stream = new AviVideoStream(aviFile, compressOptions, frameRate, firstFrame);
streams.Add(stream);
return stream;
}
/// Add an empty video stream to the file
/// true: Create a compressed stream before adding frames
/// Frames per second
/// Image to write into the stream as the first frame
/// VideoStream object for the new stream
public AviVideoStream AddVideoStream(bool isCompressed, double frameRate, Bitmap firstFrame){
AviVideoStream stream = new AviVideoStream(aviFile, isCompressed, frameRate, firstFrame);
streams.Add(stream);
return stream;
}
/// Add a wave audio stream from another file to this file
/// Name of the wave file to add
/// Index of the video frame at which the sound is going to start
public void AddAudioStream(String waveFileName, int startAtFrameIndex) {
AviManager audioManager = new AviManager(waveFileName, true);
AviAudioStream newStream = audioManager.GetWaveStream();
AddAudioStream(newStream, startAtFrameIndex);
audioManager.Close();
}
private IntPtr InsertSilence(int countSilentSamples, IntPtr waveData, int lengthWave, ref Avi.AVISTREAMINFO streamInfo) {
//initialize silence
int lengthSilence = countSilentSamples * streamInfo.dwSampleSize;
byte[] silence = new byte[lengthSilence];
//initialize new sound
int lengthNewStream = lengthSilence + lengthWave;
IntPtr newWaveData = Marshal.AllocHGlobal(lengthNewStream);
//copy silence
Marshal.Copy(silence, 0, newWaveData, lengthSilence);
//copy sound
byte[] sound = new byte[lengthWave];
Marshal.Copy(waveData, sound, 0, lengthWave);
IntPtr startOfSound = new IntPtr(newWaveData.ToInt32() + lengthSilence);
Marshal.Copy(sound, 0, startOfSound, lengthWave);
Marshal.FreeHGlobal(newWaveData);
streamInfo.dwLength = lengthNewStream;
return newWaveData;
}
/// Add an existing wave audio stream to the file
/// The stream to add
///
/// The index of the video frame at which the sound is going to start.
/// '0' inserts the sound at the beginning of the video.
///
public void AddAudioStream(AviAudioStream newStream, int startAtFrameIndex) {
Avi.AVISTREAMINFO streamInfo = new Avi.AVISTREAMINFO();
Avi.PCMWAVEFORMAT streamFormat = new Avi.PCMWAVEFORMAT();
int streamLength = 0;
IntPtr rawData = newStream.GetStreamData(ref streamInfo, ref streamFormat, ref streamLength);
IntPtr waveData = rawData;
if (startAtFrameIndex > 0) {
//not supported
//streamInfo.dwStart = startAtFrameIndex;
double framesPerSecond = GetVideoStream().FrameRate;
double samplesPerSecond = newStream.CountSamplesPerSecond;
double startAtSecond = startAtFrameIndex / framesPerSecond;
int startAtSample = (int)(samplesPerSecond * startAtSecond);
waveData = InsertSilence(startAtSample - 1, waveData, streamLength, ref streamInfo);
}
IntPtr aviStream;
int result = Avi.AVIFileCreateStream(aviFile, out aviStream, ref streamInfo);
if(result != 0){
throw new Exception("Exception in AVIFileCreateStream: "+result.ToString());
}
result = Avi.AVIStreamSetFormat(aviStream, 0, ref streamFormat, Marshal.SizeOf(streamFormat));
if(result != 0){
throw new Exception("Exception in AVIStreamSetFormat: "+result.ToString());
}
result = Avi.AVIStreamWrite(aviStream, 0, streamLength, waveData, streamLength, Avi.AVIIF_KEYFRAME, 0, 0);
if(result != 0){
throw new Exception("Exception in AVIStreamWrite: "+result.ToString());
}
result = Avi.AVIStreamRelease(aviStream);
if(result != 0){
throw new Exception("Exception in AVIStreamRelease: "+result.ToString());
}
Marshal.FreeHGlobal(waveData);
}
/// Add an existing wave audio stream to the file
/// The new stream's data
/// Header info for the new stream
/// The new stream' format info
/// Length of the new stream
public void AddAudioStream(IntPtr waveData, Avi.AVISTREAMINFO streamInfo, Avi.PCMWAVEFORMAT streamFormat, int streamLength) {
IntPtr aviStream;
int result = Avi.AVIFileCreateStream(aviFile, out aviStream, ref streamInfo);
if (result != 0) {
throw new Exception("Exception in AVIFileCreateStream: " + result.ToString());
}
result = Avi.AVIStreamSetFormat(aviStream, 0, ref streamFormat, Marshal.SizeOf(streamFormat));
if (result != 0) {
throw new Exception("Exception in AVIStreamSetFormat: " + result.ToString());
}
result = Avi.AVIStreamWrite(aviStream, 0, streamLength, waveData, streamLength, Avi.AVIIF_KEYFRAME, 0, 0);
if (result != 0) {
throw new Exception("Exception in AVIStreamWrite: " + result.ToString());
}
result = Avi.AVIStreamRelease(aviStream);
if (result != 0) {
throw new Exception("Exception in AVIStreamRelease: " + result.ToString());
}
}
/// Copy a piece of video and wave sound int a new file
/// File name
/// Start copying at second x
/// Stop copying at second y
/// AviManager for the new video
public AviManager CopyTo(String newFileName, float startAtSecond, float stopAtSecond) {
AviManager newFile = new AviManager(newFileName, false);
try {
//copy video stream
AviVideoStream videoStream = GetVideoStream();
int startFrameIndex = (int)(videoStream.FrameRate * startAtSecond);
int stopFrameIndex = (int)(videoStream.FrameRate * stopAtSecond);
videoStream.GetFrameOpen();
Bitmap bmp = videoStream.GetBitmap(startFrameIndex);
AviVideoStream 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
AviAudioStream 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 = (int)( startAtSecond * (float)(waveStream.CountSamplesPerSecond * streamFormat.nChannels * waveStream.CountBitsPerSample) / 8);
int stopByteIndex = (int)( stopAtSecond * (float)(waveStream.CountSamplesPerSecond * streamFormat.nChannels * waveStream.CountBitsPerSample) / 8);
IntPtr ptrWavePart = new IntPtr(ptrRawData.ToInt32() + startByteIndex);
byte[] rawData = new byte[stopByteIndex - startByteIndex];
Marshal.Copy(ptrWavePart, rawData, 0, rawData.Length);
Marshal.FreeHGlobal(ptrRawData);
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);
Marshal.FreeHGlobal(unmanagedRawData);
} catch (Exception ex) {
newFile.Close();
throw ex;
}
return newFile;
}
/// Release all ressources
public void Close(){
foreach(AviStream stream in streams){
stream.Close();
}
Avi.AVIFileRelease(aviFile);
Avi.AVIFileExit();
}
public static void MakeFileFromStream(String fileName, AviStream stream) {
IntPtr newFile = IntPtr.Zero;
IntPtr streamPointer = stream.StreamPointer;
Avi.AVICOMPRESSOPTIONS_CLASS opts = new Avi.AVICOMPRESSOPTIONS_CLASS();
opts.fccType = (uint)Avi.streamtypeVIDEO;
opts.lpParms = IntPtr.Zero;
opts.lpFormat = IntPtr.Zero;
Avi.AVISaveOptions(IntPtr.Zero, Avi.ICMF_CHOOSE_KEYFRAME | Avi.ICMF_CHOOSE_DATARATE, 1, ref streamPointer, ref opts);
Avi.AVISaveOptionsFree(1, ref opts);
Avi.AVISaveV(fileName, 0, 0, 1, ref streamPointer, ref opts);
}
}
}