编程 - lhf 的窝

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_new(memory,&raster);
    ft_grays_raster.raster_reset(raster,pool_base,pool_size);
    ft_grays_raster.raster_render(raster,&param);

   这三个调用中,头两个很easy。参数很简单: 

void*       memory = NULL;
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的如何设置: 

        FT_Raster_Params  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_Bitmap bmp;   

      FT_Outline outline;

 这两个参数非常重要,一个是输出(位图格式),一个是输入(矢量格式)。

先看一下FT_Bitmap的格式,它描述了一个位图,其中的buffer是位图的位数据,是4字节对齐的,从上往下的数据(你必须先了解windows位图的格式才可以理解上面的话)。因此bmp应这样设置:

        FT_Bitmap 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.points     = new FT_Vector[n_points];//点的坐标
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。

 

BOOL CScreenWnd::CreateTimer(UINT uDelay)
{
        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;            
}

下面是响应函数:

 

void CALLBACK CScreenWnd::TimerProc(UINT id,UINT msg,DWORD dwUser,DWORD dw1,DWORD dw2)
{
        ::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.在遍历目录的时候初始目录搞错了一个层次.

阅读全文