我想打开一个小视频文件并映射内存中的每个帧(以应用一些自定义滤镜)。 我不想处理视频编解码器,而是希望库为我自己处理。
我尝试将Direct Show与SampleGrabber过滤器一起使用(使用此示例http://msdn.microsoft.com/zh-cn/library/ms787867(VS.85).aspx),但是我只设法抓取了一些帧 (不是每帧!)。 我在视频软件编程方面还很陌生,也许我没有使用最好的库,或者我做错了。
我已经粘贴了一部分代码(主要是msdn示例中的修改后的复制/粘贴),不幸的是,它没有按预期抓取前25帧...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| [...]
hr = pGrabber->SetOneShot(TRUE);
hr = pGrabber->SetBufferSamples(TRUE);
pControl->Run(); // Run the graph.
pEvent->WaitForCompletion(INFINITE, &evCode); // Wait till it's done.
// Find the required buffer size.
long cbBuffer = 0;
hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);
for( int i = 0 ; i < 25 ; ++i )
{
pControl->Run(); // Run the graph.
pEvent->WaitForCompletion(INFINITE, &evCode); // Wait till it's done.
char *pBuffer = new char[cbBuffer];
hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);
AM_MEDIA_TYPE mt;
hr = pGrabber->GetConnectedMediaType(&mt);
VIDEOINFOHEADER *pVih;
pVih = (VIDEOINFOHEADER*)mt.pbFormat;
[...]
}
[...] |
有没有具有视频软件经验的人可以为我提供有关代码或其他更简单库的建议?
谢谢
编辑:
MSDN链接似乎不起作用(请参见错误)
当前,这些是Win32平台上最流行的视频框架:
Windows视频:旧的Windows框架来自Win95时代,但由于使用非常简单而仍被广泛使用。不幸的是,它仅支持已安装正确VFW编解码器的AVI文件。
DirectShow:标准WinXP框架,它基本上可以加载Windows Media Player可以播放的所有格式。相当难用。
Ffmpeg:更确切地说是Ffmpeg开源多媒体实用程序随附的libavcodec和libavformat。它非常强大,即使没有在系统上安装编解码器,也可以读取许多格式(几乎可以用VLC播放的所有内容)。它使用起来非常复杂,但是您总是可以从它附带的ffplay代码或开源软件中的其他实现中获得启发。无论如何,我认为它仍然比DS容易使用(并且速度更快)。它必须由Windows上的MinGW来处理,但是这里对所有步骤都进行了很好的解释(此刻链接已断开,希望不会死)。
QuickTime:Apple框架不是Windows平台的最佳解决方案,因为它需要安装QuickTime应用程序以及每种格式的正确QuickTime编解码器;它不支持多种格式,但是在专业领域却很常见(因此某些编解码器实际上仅用于QuickTime)。实施起来应该不会太困难。
Gstreamer:最新的开源框架。我对此了解不多,我猜想它会覆盖其他一些系统(但我不确定)。
除DirectShow外,所有这些框架都已在OpenCv Highgui中作为后端实现。 Win32 OpenCV的默认框架使用的是VFW(因此只能打开一些AVI文件),如果要使用其他框架,则必须下载CVS而不是官方版本,并且仍然会对代码进行一些修改,但是无论如何太完整了,例如FFMPEG后端不允许在流中查找。
如果您想将QuickTime与OpenCV一起使用,可以为您提供帮助。
我知道在C ++中获取视频文件的正确细分并自己动手是很诱人的。但是,尽管信息已经存在,但处理此类文件的过程却耗时长,要处理每种文件格式,并使其易于更改以考虑到将来的结构更改,坦率地说,这样做是不值得的。
相反,我推荐ffmpeg。上面提到过,但是说很难,这并不困难。还有比大多数人需要的更多的选择,这使它看起来比实际要困难。对于大多数操作,您可以让ffmpeg自己解决。
例如文件转换
ffmpeg -i inputFile.mp4 outputFile.avi
从一开始就确定要在线程或更确切地说是线程库中运行ffmpeg操作。但是,请使用您自己的线程类包装它,以便您可以使用自己的EventAgs和检查线程完成的方法。就像是 :-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ThreadLibManager()
{
List<MyThreads> listOfActiveThreads;
public AddThread(MyThreads);
}
Your thread class is something like:-
class MyThread
{
public Thread threadForThisInstance { get; set; }
public MyFFMpegTools mpegTools { get; set; }
}
MyFFMpegTools performs many different video operations, so you want your own event
args to tell your parent code precisely what type of operation has just raised and
event.
enum MyFmpegArgs
{
public int thisThreadID { get; set; } //Set as a new MyThread is added to the List<>
public MyFfmpegType operationType {get; set;}
//output paths etc that the parent handler will need to find output files
}
enum MyFfmpegType
{
FF_CONVERTFILE = 0, FF_CREATETHUMBNAIL, FF_EXTRACTFRAMES ...
} |
这是我的ffmpeg工具类的一小段,该部分收集有关视频的信息。
我将FFmpeg放在一个特定的位置,并在运行该软件的开始时确保已在其中。对于此版本,我已将其移至桌面,我可以肯定地为您正确编写了路径(我真的很讨厌MS的特殊文件夹系统,因此我会尽可能地忽略它)。
无论如何,这是使用无窗口ffmpeg的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| public string GetVideoInfo(FileInfo fi)
{
outputBuilder.Clear();
string strCommand = string.Concat(" -i \"", fi.FullName,"\"");
string ffPath =
System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop) +"\\\\ffmpeg.exe";
string oStr ="";
try
{
Process build = new Process();
//build.StartInfo.WorkingDirectory = @"dir";
build.StartInfo.Arguments = strCommand;
build.StartInfo.FileName = ffPath;
build.StartInfo.UseShellExecute = false;
build.StartInfo.RedirectStandardOutput = true;
build.StartInfo.RedirectStandardError = true;
build.StartInfo.CreateNoWindow = true;
build.ErrorDataReceived += build_ErrorDataReceived;
build.OutputDataReceived += build_ErrorDataReceived;
build.EnableRaisingEvents = true;
build.Start();
build.BeginOutputReadLine();
build.BeginErrorReadLine();
build.WaitForExit();
string findThis ="start";
int offset = 0;
foreach (string str in outputBuilder)
{
if (str.Contains("Duration"))
{
offset = str.IndexOf(findThis);
oStr = str.Substring(0, offset);
}
}
}
catch
{
oStr ="Error collecting file information";
}
return oStr;
}
private void build_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
string strMessage = e.Data;
if (outputBuilder != null && strMessage != null)
{
outputBuilder.Add(string.Concat(strMessage,"\
"));
}
} |
使用SampleGrabber的"回调"模型可以为您带来更好的结果。请参见Samples \ C ++ \ DirectShow \ Editing \ GrabBitmaps中的示例。
在Samples \ C ++ \ DirectShow \ Filters \ Grabber2 \ grabber_text.txt和readme.txt中也有很多信息。
我已经使用OpenCV加载视频文件并进行处理。它对于许多类型的视频处理(包括那些对计算机视觉有用的视频处理)也非常方便。
如果您的视频仅需要产生一系列图片,则OpenCV是最佳解决方案。如果您愿意进行真实的视频处理,那么ViDeo等于" Visual Audio",则需要跟上" martjno"提供的视频。同样适用于Win7的新Windows解决方案还包括3种新功能:
Windows Media Foundation:DirectShow的继承者;清理界面
Windows Media Encoder 9:它不仅包含程序,还附带用于编码的库
Windows Expression 4:2的后继。
最后两个是仅供商业使用的解决方案,但第一个是免费的。要编码WMF,您需要安装Windows SDK。
如果是用于AVI文件,我自己会从AVI文件中读取数据并提取帧。现在,使用视频压缩管理器对其进行解压缩。
AVI文件格式非常简单,请参见:http://msdn.microsoft.com/zh-cn/library/dd318187(VS.85).aspx(并使用google)。
打开文件后,只需提取每个帧并将其传递给ICDecompress()对其进行解压缩。
似乎需要做很多工作,但这是最可靠的方法。
如果工作量太大,或者您想要的不是AVI文件,请使用ffmpeg。
尝试使用OpenCV库。它绝对具有您需要的功能。
本指南包含有关从视频文件访问帧的部分。