FFMpeg相关
1、ffmpeg.exe的使用
ffmeg.exe功能强大,可用于视频格式转换,截图,或捕获视频音频等。这里只说明截图的方式,做个记录,以备将来参考。
截图的主要参数是:
-i :指定输入文件(视频文件),可以是相对路径或绝对路径。
-y :覆盖输出文件,即如果有同名文件的话,则直接覆盖。
-f :输出格式,一般采用 image2。
-ss: 表示相对于文件开始处的时间偏移值, 单位是秒,也支持 hh:mm:ss的格式。
-s : 指定输出图片的尺寸。 比如: -s 120*90
-vframes: 表示截图的帧数 。
上面各参数之间的顺序不重要,但一般好像都把 -i 作为第一个参数 。
看一个例子:ffmpeg -i aa.mpg -f image2 -ss 5 -s 120*90 -vframes 1 test.jpg
含义是: 对当前目录下的aa.mpg截图,从第5秒开始截图,图片尺寸是 120*90,只截第一个图,保存为 test.jpg。
如果要输出多个图,则可以在图片名称中加入%d的参数,输出时,%d会被序号代替(支持扩展的%02d等格式)。比如:
ffmpeg -i aa.mpg tt%02d.jpg 输出aa.mpg中的每一帧图像,依次保存为 tt01.jpg,tt02.jpg等等。
ffmpeg -i aa.mpg tt%02d.jpg -vframes 5 输出aa.mpg的前5帧图像,依次保存为 tt01.jpg,tt02.jpg等等。
ffmpeg -i aa.mpg -ss 2 tt%02d.jpg -vframes 5 输出从第2秒开始的连续5帧图,依次为 tt01.jpg,tt02.jpg等。
FreeType的光栅化功能
FreeType是一个字体格式开源工程。其中不但支持各种字体格式的解析,还提供了一个独立的光栅化引擎。
---------------------------下面的文字解释了什么是光栅化--------------------------------------------------------------------
现在流行的字体格式如TureType,Opentype等,其中描述的字形信息(就是字符的笔划信息)都是矢量的。
其中字符的每一个笔划都是由多条曲线或直线(直线可视为一次曲线)包围而形成的。一次曲线需要两个点来确定。
二次需要三个点,三次就需要四个点。字体内部就保存了这些点的坐标。显示字体的时候,因为显示器和打印机
都是点阵设备,所以必须将字符转化为用点阵来描述。这个过程就是光栅化。说白了,就是矢量描述转化为点阵描述。
不过字体的光栅化与与其它光栅化(如图形学中的Bresenham,DDA算法等)不同的是,它关心的是区域的点阵化,
即判断每个点属于那个区域。
----------------------------------------------------------------------------------------------------------------------------------------------
FreeType2.3.7实际提供了两个光栅化引擎。ft_grays_raster和ft_standard_raster。
分别位于:src\smooth\ftgrays.c 和 src\raster\ftraster.c 。二者的用法完全相同。FreeType自己用的是前者。
要想在FreeType之外独立使用两个引擎,我们分两个的步骤:
1、编译问题。把相关的文件加入工程,设置_STANDALONE_等宏。可能要修改代码,比如注释掉一些代码行等。
将int64_t 改为 __int64(for vc),等等。这些事,对于一个编程熟手来说,应该10分钟搞定了。我就不多啰嗦。
2、代码调用。主要的函数调用有三个:
ft_grays_raster.raster_reset(raster,pool_base,pool_size);
ft_grays_raster.raster_render(raster,¶m);
这三个调用中,头两个很easy。参数很简单:
FT_Raster raster;
int pool_size = 10240;
BYTE* pool_base = new BYTE[pool_size];
ft_grays_raster.raster_new(memory,&raster);
ft_grays_raster.raster_reset(raster,pool_base,pool_size);
第三个调用有点麻烦,先看一下参数param的如何设置:
param.target = &bmp;
param.source = &outline;
param.flags = FT_RASTER_FLAG_AA;
param.gray_spans = NULL;
param.black_spans = NULL;
param.bit_test = NULL;
param.bit_set = NULL;
param.user = NULL;
其中bmp和outline的定义如下:
FT_Outline outline;
这两个参数非常重要,一个是输出(位图格式),一个是输入(矢量格式)。
先看一下FT_Bitmap的格式,它描述了一个位图,其中的buffer是位图的位数据,是4字节对齐的,从上往下的数据(你必须先了解windows位图的格式才可以理解上面的话)。因此bmp应这样设置:
bmp.width = 16;//输出位图的size.
bmp.rows = 16;
bmp.pitch = 16;//每行需要的字节数,4字节对齐的.
bmp.buffer = new BYTE[bmp.rows*bmp.pitch];
bmp.num_grays = 256;
bmp.pixel_mode = FT_PIXEL_MODE_GRAY;
bmp.palette_mode = 0;
bmp.palette = NULL;
memset(bmp.buffer,0,bmp.rows*bmp.pitch);
FT_Outline的格式就比较复杂些。它描述了一系列的轮廓线。其n_contours和n_points成员是容易理解的,分别表示轮廓的数目和点的数目。轮廓指围成一个闭合区域的边界。比如字符”一“是一条轮廓,字符‘二’是两条轮廓。字符”十“是一条轮廓。对更复杂的字符,就不一定能直接看出有多少个轮廓,因为轮廓之间会有重叠。点的数目,是指末端点和首端点不重合情况下的数目。比如,对一个矩形,应该是4个端点,而不是5个。FreeType内部会自动将末端点连到首段点。
其它几个成员的含义如下:
outline.tags = new char [n_points]; //每个点的类型.
outline.contours = new short [n_contours]; //每条轮廓线占用的点数.
outline.flags = FT_OUTLINE_OWNER|FT_OUTLINE_HIGH_PRECISION;
points,tags,contours描述了每条轮廓的具体数据。三者必须严格对应上。这三个数组的具体格式也就是本篇文章的意义所在了。
points数组,tags数组 一般可以从字体文件数据中得到。但是其中的单位可能会有不同。这个跟字体的格式有关。不行就多试几个比例值。只要看到任何一点输出(比如一个字的其中一个笔画),就离成功不远了,在附近多试几个比例值。很快就可以让字符和位图的大小能符合了。
contours数组描述了一个轮廓使用的点数。比如:
contours[0]如果为8,则表示points数组中,元素0-8,都是第一条轮廓线的。
然后point数组中,9-contours[1]都是第二条轮廓线的。
依次类推,第n条轮廓线的点是: contours[n-2]+1 到 contours[n-1] (其中n>=2)
很明显,最后一个轮廓线对应的 contours[x]元素,其值必定等于 n_points-1。因为points数组最后一个元素就是 points[n_points-1]。
如果不满足此条件,ft_grays_raster直接跳出,认为数据错误。
几点心得
1.在多媒体定时器的响应函数中不能使用DestroyWindow函数。
因为多媒体定时器实际是内部开了一个线程,在该线程中调用定时器的回调函数。
而MSDN告诉我们:
A thread cannot use DestroyWindow to destroy a window created by a different thread.
所有就有上面的结论了。那么,如果确实要调用怎么办呢? 比如用多媒体定时器来控制一个窗口的生存时间,到了一定的秒数就关闭窗口。
应该用sendmessage。
{
TIMECAPS tc;
if(::timeGetDevCaps(&tc,sizeof(TIMECAPS)) == TIMERR_NOERROR)//查询设备能力。
{
m_timerRes = min(max(tc.wPeriodMin,1),tc.wPeriodMax);
if(::timeBeginPeriod(m_timerRes)==TIMERR_NOERROR)//设定定时器精度。
{
MMRESULT result= ::timeSetEvent(uDelay,m_timerRes,TimerProc,(DWORD)this, TIME_PERIODIC);
if (result != NULL)//设置成功,定时器开始起作用。
{
m_timerId = (UINT)result;
return TRUE;
}
}
}
return FALSE;
}
下面是响应函数:
{
::SendMessage(m_pThis->m_hWnd,WM_MY_TimeTick,0,0);
// if(m_pThis->m_LiveTime==0)
// {
//注意,这里不能直接调DestroyWindow,因为不在同一个线程中.
// }
}
注意:WM_MY_TimeTick是一个自定义的消息。可以在响应该消息是调用DestroyWindow。SendMessage函数为什么可以成功呢,因为该函数没有要求在同一个线程中(实际上连同一个进程都没要求,可以安全的发送消息到另一个进程的窗口,很多情况下会失败是因为地址空间的关系)。它之所以不要求,是因为该函数自己会处理线程切换等事务。
2、服务的类型和子进程的类型
如果服务注册为非交互式的,然后在其中产生子进程。那么子进程可以使用窗口吗(即是否能是一个GUI程序)?答案是非也。
这个问题大概根窗口站(window station)相关吧。窗口站包含了桌面,桌面包含了窗口。windows将可见的窗口站命名为WinSta0,其余的窗口站都是不可见的。
除非另行指定,windows子系统将所有运行在本地系统账户下的服务与一个非可见窗口站:Service-0x0-3e7$关联起来。所有非交互式服务都共享此窗口站。
凡是指定了某个账户(非本地系统账户)的服务,都运行在一个独立的非可见窗口站中。
总结:如果是非交互式的服务,则其子进程也在同一个窗口站(该窗口站不可见)运行,因此子进程的窗口也是不可见的。
以上内容,详见《深入解析windows操作系统第4版》p221。
3、如何屏蔽Ctrl+alt+del
简单的说,hook winlogon.exe进程中的SAS 窗口的窗口过程。在其中拦截该组合键即可。主要步骤是:
1. 打开winlogon.exe进程。这个又分为: a.取进程id. b.提升权限。 c.打开进程.
2.写该进程的内存。 写入一些数据,为加载DLL做准备。
3.创建远程线程,让winlogon加载一个dll。
4.DLL的主函数中,替换winlogon的SAS窗口过程。注意,该窗口在桌面Winlogon下,必须先打开该桌面,才能枚举窗口。
注:2,3,4也可以完全不使用dll,直接把代码写入winlogon进程,这就需要几个占位函数以便计算各函数的代码长度。
或者,使用DLL,但只是加入代码,并不在dll主函数中做hook窗口的工作。而是创建远程线程,执行该dll中的代码。
相关的代码网上很容易找,不过自己做的话还是会碰到一些细节问题。只要基本概念有了,找到代码然后修改是很容易的。
虚拟表格显示控件
一个虚拟的大表格数据显示控件(MFC)
让vector析构时不释放内存
通过指定allocator的办法控制vector的内存释放。
慎用7za.exe的-r参数
7za.exe的一个bug.在遍历目录的时候初始目录搞错了一个层次.