AviFile 库的简单 C# 包装器






4.94/5 (203投票s)
在 .NET 中编辑 AVI 文件。
引言
这篇文章是为那些不想花费数小时处理 AVIFile 函数,而只是想读取或修改简单 AVI 视频的任何人准备的。我已将最重要的一些 AVIFile 函数封装到三个易于使用的 C# 类中,它们可以处理以下任务:
- 从视频流读取图像。
- 解压缩一个压缩的视频流。
- 压缩一个未压缩的视频流。
- 更改视频流的压缩格式。
- 将视频流导出到单独的.avi 文件。
- 将音频流导出到.wav 文件。
- 将音频和视频流的几秒钟复制到一个新的.avi 文件。
- 从.wav 文件向视频添加声音。
- 从位图列表创建一个新的视频流。
- 向现有视频流(已压缩或未压缩)添加帧。
- 将帧插入到流中。
- 从流中复制或删除帧。
这些功能涵盖了常见的用例,例如从几张图片和一个 wave 音频创建视频、从视频中提取音轨、剪切短片段或从电影中抓取单张图片。
本文包含两个部分:
- 首先,我将解释演示应用程序,以便您可以使用它来自己探索该库。
- 然后,我将逐步解释库的工作原理。
如何使用该库 - 演示应用程序的详细介绍
“探索”选项卡
“探索”选项卡允许您探索 AVI 文件,并提供不需要可编辑流的简单任务。
在表单的顶部,您可以选择要探索的 AVI 文件。text.avi(来自测试数据文件夹)已预先选中。在左侧,您可以显示有关视频和 wave 音频流(如果可用)的标题信息。此外,您还可以在 PictureBox
中看到视频的图像帧。
图像由 VideoStream
对象读取。GetFrameOpen
准备流以解压缩帧,GetFrameClose
释放用于解压缩帧的资源,而 GetBitmap
则解压缩帧并将其转换为 System.Drawing.Bitmap
。
AviManager aviManager = new AviManager(txtAviFileName.Text, true);
VideoStream aviStream = aviManager.GetVideoStream();
aviStream.GetFrameOpen();
picFrame.Image =
aviStream.GetBitmap(Convert.ToInt32(numPosition.Value));
aviStream.GetFrameClose();
aviManager.Close();
在演示表单的中间,您可以处理整个流。
解压缩会移除视频流的压缩。它会创建一个新的文件和视频流,从旧流中解压缩每个帧,并将其写入新流。结果是一个大的新.avi 文件,具有相同的视频但没有压缩。
压缩会更改视频的压缩格式,或对未压缩视频应用压缩。它的功能与解压缩相同,但会压缩新流。这两个函数使用同一个方法 CopyFile
。
private void CopyFile(String newName, bool compress){
//open compressed file
AviManager aviManager =
new AviManager(txtAviFileName.Text, true);
VideoStream aviStream = aviManager.GetVideoStream();
//create un-/re-compressed file
AviManager newManager =
aviStream.DecompressToNewFile(newName, compress);
//close compressed file
aviManager.Close();
//save and close un-/re-compressed file
newManager.Close();
}
每当 VideoStream
实例创建压缩流时,它会询问您有关编解码器和设置的信息。
提取位图会将视频分割成许多单独的位图文件。
VideoStream stream = aviManager.GetVideoStream();
stream.GetFrameOpen();
String path = @"..\..\testdata\";
for(int n=0; n<stream.CountFrames; n++){
stream.ExportBitmap(n, path+n.ToString()+".bmp");
}
stream.GetFrameClose();
当然,您可以将图像保存为任何格式。ExportBitmap
只是这三行代码的快捷方式。
Bitmap bmp = stream.GetBitmap(position);
bmp.Save(FileName);
bmp.Dispose();
提取视频会将整个视频流复制到一个新的 AVI 文件中。您可以使用它来删除所有其他流,如 MIDI、文本和 Wave 音频。
下面的框处理 Wave 音频。提取音频会将整个音频流复制到一个 Wave 文件中。这不是一项艰巨的任务,只需要四行代码。
AviManager aviManager =
new AviManager(txtAviFileName.Text, true);
AudioStream audioStream = aviManager.GetWaveStream();
audioStream.ExportStream( @"..\..\testdata\sound.wav" );
aviManager.Close();
提取几秒钟允许您在 X 秒和 Y 秒之间复制视频和音频。首先,一个 CopyForm
对话框让您输入 X 和 Y,然后这些部分将从视频和音频流中剪切出来。
AviManager aviManager = new AviManager(txtAviFileName.Text, true);
VideoStream aviStream = aviManager.GetVideoStream();
CopyForm dialog = new CopyForm(0,
aviStream.CountFrames / aviStream.FrameRate);
if (dialog.ShowDialog() == DialogResult.OK) {
int startSecond = dialog.Start;
int stopSecond = dialog.Stop;
AviManager newFile = aviManager.CopyTo(
"..\\..\\testdata\\video.avi", startSecond, stopSecond);
newFile.Close();
}
aviManager.Close();
添加音频允许您选择一个.wav 文件,并将其添加到视频中。例如,您可以使用此功能为静音视频添加音轨,或为通过提取视频提取的视频重新添加声音。添加音频是一项简单的任务,只需要三行代码。
String fileName = GetFileName("Sounds (*.wav)|*.wav");
if(fileName != null){
AviManager aviManager =
new AviManager(txtAviFileName.Text, true);
aviManager.AddAudioStream(fileName);
aviManager.Close();
}
最后一组功能是关于创建新的视频流。在框中输入图像文件列表并进行动画处理。
创建未压缩会从图像构建一个新视频,并将其保存而不进行任何压缩。创建并压缩执行相同操作,但它会显示压缩设置对话框并压缩图像。这两种方法都会创建一个新文件,并将一个示例位图传递给 AddVideoStream
。示例位图用于设置新流的格式。然后,列表中的所有图像都会被添加到视频中。
//load the first image
Bitmap bitmap = (Bitmap)Image.FromFile(txtFileNames.Lines[0]);
//create a new AVI file
AviManager aviManager =
new AviManager(@"..\..\testdata\new.avi", false);
//add a new video stream and one frame to the new file
VideoStream aviStream =
aviManager.AddVideoStream(true, 2, bitmap);
Bitmap bitmap;
int count = 0;
for(int n=1; n<txtFileNames.Lines.Length; n++){
if(txtFileNames.Lines[n].Trim().Length > 0){
bitmap =
(Bitmap)Bitmap.FromFile(txtFileNames.Lines[n]);
aviStream.AddFrame(bitmap);
bitmap.Dispose();
count++;
}
}
aviManager.Close();
添加帧会将图像追加到现有视频流。对于未压缩的视频流,我们可以通过简单地打开流并像平常一样添加帧来追加帧。但是,已压缩的流无法重新压缩。AVIStreamWrite
(由 AddFrame
使用)不会返回任何错误;但无论如何,它都会以未压缩的方式添加新帧,并产生奇怪的彩色像素风暴。要将帧添加到已压缩的流,必须先解压缩现有帧,然后将其添加到新的已压缩流中。然后才能将其他帧添加到该流中。
//open file
Bitmap bmp = (Bitmap)Image.FromFile(txtFileNames.Lines[0]);
AviManager aviManager =
new AviManager(txtAviFileName.Text, true);
VideoStream aviStream = aviManager.GetVideoStream();
//streams cannot be edited - copy to a new file
AviManager newManager = aviStream.DecompressToNewFile(
@"..\..\testdata\temp.avi", true);
aviStream = newManager.GetOpenStream(0);
//add images
Bitmap bitmap;
for(int n=0; n<txtFileNames.Lines.Length; n++){
if(txtFileNames.Lines[n].Trim().Length > 0){
bitmap =
(Bitmap)Bitmap.FromFile(txtFileNames.Lines[n]);
aviStream.AddFrame(bitmap);
bitmap.Dispose();
}
}
aviManager.Close(); //close old file
newManager.Close(); //save and close new file
//delete old file, replace with new file
System.IO.File.Delete(txtAviFileName.Text);
System.IO.File.Move(@"..\..\testdata\temp.avi",
txtAviFileName.Text);
现在您已经了解了 AVIFile 包装类如何使用,让我们看一下后台。
“编辑”选项卡
“编辑”选项卡演示了可编辑 AVI 流的任务,例如在流的任何位置粘贴帧或更改帧率。
选择文件进行编辑后,将从视频流创建一个可编辑流,并且编辑器按钮将被启用。正常的视频流是锁定的;要插入和删除帧,需要一个可编辑流。
AviManager file = new AviManager(fileName, true);
VideoStream stream = file.GetVideoStream();
EditableVideoStream editableStream =
new EditableVideoStream(stream);
file.Close();
在左侧,您可以复制或剪切帧序列,并将它们粘贴到同一流中的其他位置。
将帧从一个流复制到另一个流或同一流中,只需要两行代码。
//copy frames
IntPtr copiedData = editableStream.Copy(start, length);
//insert frames
editableStream.Paste(copiedData, 0, position, length);
删除帧没有其他方法,只能剪切并丢弃它们。
//cut and paste frames
IntPtr copiedData = editableStream.Cut(start, length);
editableStream.Paste(copiedData, 0, position, length);
//delete frames == cut without paste
IntPtr deletedData = editableStream.Cut(start, length);
在对话框的中间,您可以将图像文件中的帧插入到流中的任何位置,并更改帧率以使视频播放速度变慢或变快。
我们只能粘贴流,不能粘贴位图,因此列表中的位图会写入一个临时 AVI 文件,然后作为流粘贴。
//create temporary video file
String tempFileName = System.IO.Path.GetTempFileName() + ".avi";
AviManager tempFile = new AviManager(tempFileName, false);
//write the new frames into the temporary video stream
Bitmap bitmap =
(Bitmap)Image.FromFile(txtNewFrameFileName.Lines[0].Trim());
tempFile.AddVideoStream(false, 1, bitmap);
VideoStream stream = tempFile.GetVideoStream();
for (int n=1; n<txtNewFrameFileName.Lines.Length; n++) {
if (txtNewFrameFileName.Lines[n].Trim().Length > 0) {
stream.AddFrame(
(Bitmap)Image.FromFile(txtNewFrameFileName.Lines[n]));
}
}
//paste the video into the editable stream
editableStream.Paste(stream, 0,
(int)numPastePositionBitmap.Value, stream.CountFrames);
您的视频是否太慢或太快?告诉播放器应用程序每秒播放更多/更少帧。
Avi.AVISTREAMINFO info = editableStream.StreamInfo;
info.dwRate = (int)(numFrameRate.Value * 10000);
info.dwScale = 10000;
editableStream.SetInfo(info);
最后一个框不是用于编辑的,它只是一个预览播放器。在将可编辑流保存到 AVI 文件之前,您应该预览它。
实现预览播放器很容易,您只需要一个 PictureBox
和要播放的视频流。一个显示当前帧索引的标签也很有用。一个开始按钮,一个停止按钮,然后就可以了。
private void btnPlay_Click(object sender, EventArgs e) {
player = new AviPlayer(editableStream,
pictureboxPreview, labelFrameIndex);
player.Stopped += new System.EventHandler(player_Stopped);
player.Start();
SetPreviewButtonsState();
}
private void player_Stopped(object sender, EventArgs e) {
btnPlay.Invoke(
new SimpleDelegate(SetPreviewButtonsState));
}
private void SetPreviewButtonsState() {
btnPlay.Enabled = ! player.IsRunning;
btnStop.Enabled = player.IsRunning;
}
private void btnStop_Click(object sender, EventArgs e) {
player.Stop();
}
工作原理
AviManger
管理 AVI 文件中的流。构造函数接受文件名并打开它。Close
关闭所有打开的流和文件本身。您可以使用 AddVideoStream
和 AddAudioStream
添加新流。新的视频流是空的,Wave 流只能从 Wave 文件创建。在创建了一个空的视频流之后,使用 VideoStream
的方法来填充它。但是,当您添加流时,实际上发生了什么?
创建视频流
有两种方法可以创建新的视频流:从示例位图创建,或从显式格式信息创建。这两种方法都做同样的事情,它们将参数传递给 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
,如果 writeCompressed
指示,则调用 AVIMakeCompressedStream
。
public VideoStream(
int aviFile, //pointer to the file object
bool writeCompressed, //true: create compressed stream
int frameRate, //frames per second
...
){
//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
//the uncompressed stream
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
。看一下原始声明:
BOOL AVISaveOptions(
HWND hwnd,
UINT uiFlags,
int nStreams,
PAVISTREAM * ppavi,
LPAVICOMPRESSOPTIONS * plpOptions
);
LPAVICOMPRESSOPTIONS
是指向 AVICOMPRESSOPTIONS
结构指针的指针。在 C# 中,结构是按值传递的。如果您通过 ref
传递结构,则会传递指向该结构的指针。类的实例始终作为指针传递给方法。因此,按 ref
传递的类参数意味着指向对象指针的指针。AVISaveOptions
和 AVICOMPRESSOPTIONS
的 C# 声明是:
[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;
}
}
通过这个变通方法,我们能够在 C# 中调用 AVISaveOptions
和(稍后)AVISaveV
。现在,可以使用 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++;
}
现在,我们能够用图像填充一个空流。但是,我们如何才能向现有流添加帧呢?嗯,首先,我们必须用第三个构造函数打开该流。
重新压缩视频流
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
会在新的文件中创建一个可写流的副本。您可以向此新流添加帧,关闭新的 AviManager
来保存它,然后将旧文件中的音频流添加到其中以完成复制。向视频添加帧并不容易,但这种方式可行。
分离流
有时,您可能有一个带有音频的视频文件,但您只需要无声视频或只需要音频。不必逐帧复制,您可以像平常一样打开流并使用 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);
}
从 Wave 文件导入音频
现在,我们能够从位图构建视频,并从中提取音频。音频是如何进入文件的?我们可以再次使用 AVISaveV
,将视频和音频流合并到一个新文件中 - 但我们不必这样做。添加新音频流的最简单方法是将 Wave 文件作为一个只有一个流的 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);
}
从视频和音频中复制片段
我添加了此方法,因为许多人问我如何做到这一点。要从 X 秒复制到 Y 秒的视频流的一部分,必须根据帧率和秒数来计算第一个和最后一个帧的索引。对于 Wave 流,我们必须根据每秒采样数、每样本位数和请求的秒数来计算字节偏移量。其余的只是复制和粘贴。
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;
}
如果您仍然对 AVI 视频感兴趣,请下载包装器库和演示应用程序。最后,我敢说:祝您在使用 AVIFile 时玩得开心!
已知问题
向现有流添加帧并不适用于所有视频编解码器和/或位图。您可能会遇到 StackOverflowException
或损坏的帧。如果您找出原因,请告诉我。
历史
- 2004 年 7 月 9 日 - 更新了下载。
- 2004 年 10 月 3 日 - 新方法
AviManager.CopyTo
。 - 2004 年 12 月 18 日 - 新类
EditableVideoStream
和AviPlayer
,并修复了一些内存泄漏。 - 2005 年 11 月 26 日 - 移除了
VideoStream.GetBitmap
中的性能优化。 - 2006 年 1 月 1 日 - 修正了
VideoStream.GetFrameOpen
中无效的颜色深度和隔行扫描视频问题,新属性VideoStream.FirstFrame
。非常感谢 Michael Covington!