宁静以致远
zgf的blog
<2012年5月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

留言簿(20)

随笔分类

随笔档案

文章档案

友情链接

资料收藏

搜索

最新评论

阅读排行榜

评论排行榜

 
VC知识库BLOG   首页  新随笔  联系  聚合  登录 
  随笔-42 文章-8 评论-115 Trackbacks-0
2011年4月27日

          任务需要ffmpeg库支持x264,原本有2年前编译好的库。可是有点小毛病。于是决定重新编译一番。回忆以前的编译步骤,用以前下载的代码编译,遇到的问题一个接一个。在最后准备放弃的时刻,发现了一篇博文讲述了最新版本的编译过程。出乎意料的非常简单。

链接在这   http://blog.csdn.net/ngwsx/archive/2011/02/22/6199788.aspx


下载

--------------------

*) mingw (mingw-get-inst-20110211.exe)

    http://www.mingw.org/
*) ffmpeg (ffmpeg-0.6.1.tar.bz2)

    http://www.ffmpeg.org/
*) x264 (last_x264.tar.bz2)

    http://www.videolan.org/developers/x264.html
*) yasm (yasm-1.1.0.tar.gz)

    http://www.tortall.net/projects/yasm/
*) lame (lame-3.98.4.tar.gz)

    http://lame.sourceforge.net/

编译、安装

--------------------

* mingw

双击运行,在列表中选择要安装的软件包,mingw-get-inst就会调用mingw-get下载相应的包并安装。

(这里假设把mingw安装在c:\mingw目录下。)

* yasm
1) ./configure
2) make & make install

* x264
1) ./configure --enable-win32thread
2) make & make install
3) 把x264.h和x264_config.h这两个文件从c:\mingw\msys\1.0\local\include目录拷贝到c:\mingw\include目录。

4) 把libx264.a文件从c:\mingw\msys\1.0\local\lib目录拷贝到c:\mingw\lib目录。


* lame
1) ./configure
2) make & make install
3) 把lame目录从c:\mingw\msys\1.0\local\include目录拷贝到c:\mingw\include目录。

4) 把libmp3lame.a文件从c:\mingw\msys\1.0\local\lib目录拷贝到c:\mingw\lib目录。


* ffmpeg:
1) ./configure --enable-memalign-hack --enable-gpl --enable-libx264 --enable-libmp3lame
2) make & make install

参考

发表于 2011-04-27 15:31 zgf的blog 阅读(1502) | 评论 (4)编辑 收藏
2010年12月17日
-mno-cygwin

转自http://hi.baidu.com/kaien_space/blog/item/ae765e0aa3de501695ca6bb9.html

一份粗糙的研究记录,有待补完和整理。

MinGW:
c -> o           gcc -c a.c
c -> exe         gcc a.c libs.o -o a.exe (从主程序a.c,附加libs,生成a.exe)
o -> exe         gcc a.o b.o ... -o main.exe
c -> dll,def,a   gcc a.c -shared -o a.dll -Wl,--output-def,a.def,--out-implib,liba.a
a -> dll           a2dll liba.a
dll -> a:          dlltool --dllname a.dll --def a.def --output-lib liba.a (需要def文件)
a -> def:        dumpbin /exports lib.a > lib.def (在windows上调用,def需要修改)
dll -> def :      pexports a.dll -o > a.def (这里的-o是指给函数标序号)
lib -> def :      reimp -d a.lib
lib -> a:          (for __cdecl functions in most case) reimp a.lib; (for __stdcall functions)

MSVC:
c -> lib     cl /LD a.c (注意已经定义了export列表)
c -> dll     cl /LD a.c
c -> obj     cl /c a.c
c -> exe     cl a.c /out:a.exe
dll ->lib   lib /machine:ix86 /def:a.def /out:a.lib (需要def文件)
obj ->lib   lib a.obj b.obj... /out:mylib.lib
dll ->def   DUMPBIN a.dll /EXPORTS /OUT:a.def (生成的def需要做修正)
lib ->def   reimp -d a.lib (这个要在MSYS+MinGW下用)

关于这些工具的适用范围可以很容易的理解和记忆。

dll和exe都是PE文件,所以可以使用pexports.

lib和a是静态库文件,都是归档类型,不是PE格式。所以不能使用pexports.

dll可以使用dlltool.

lib可以使用lib, 和reimp(lib->a工具)

所有的bin文件,包括dll,exe,lib,a都可以使用dumpbin.

参考:

http://hi.baidu.com/kaien_space/blog/item/5e77fafa2ba9ff16a8d3110a.html
Mingw官网文档:  
http://www.mingw.org/wiki/MSVC_and_MinGW_DLLs
http://oldwiki.mingw.org/index.php/CreateImportLibraries
http://www.mingw.org/wiki/FAQ
http://hi.baidu.com/opaquefog/blog/item/9b21b6deb324e25dccbf1ab7.html
http://qzone.qq.com/blog/8330936-1238659272

http://hi.baidu.com/jzinfo/blog/item/b0aa1d308de99f9da8018e00.html

本篇测试用代码:

1. main.cpp

#include <iostream>
#include <stdlib.h>
#include "mylib.h"

using namespace std;

int main()
{
char str[]="Hello world!";
printhello(str);

return 0;
}

2. mylib.cpp

#include <iostream>
#include <stdlib.h>
#include "mylib.h"

using namespace std;

void EXPORT printhello(char *str)
{
cout << str << endl;
}

3. mylib.h


#define EXPORT __declspec(dllexport)

extern "C"
{
void EXPORT printhello(char *str);
}

关于DLL的定义和使用:
1. 需要外部调用的函数,定义的时候要在函数声明前加入
__declspec(dllexport)
方便起见,可以定义成宏
#define EXPORT __declspec(dllexport)
然后在定义函数声明的时候使用,例如:
void EXPORT printhello(char *str);

只有注明EXPORT的函数才能出现在dll的输出表中,外部函数才能调用。
关于函数调用约定__cdecl 还是 __stdcall。我们可以在输出函数名前添加,也可以用cl编译的时候指明
/Gd 使用 __cdecl 调用约定 (C declaration,是C和C++默认格式),手动堆栈平衡(支持可变参数)
/Gz 使用 __stdcall 调用约定 (是pascal, fortran等的调用约定), 自动堆栈平衡
此外还有其他的调用约定,如_fastcall,把前两个参数通过寄存器传递,调用速度快。

要使用dll,我们可以动态调用它,也可以转换成lib库静态调用。
动态调用就是说,先用LoadLibrary加载到内存中。再用GetProcAddress得到函数地址就可以使用了。比较麻烦。
静态调用就是说,先为需要调用的函数生成def文件,然后制作静态库lib文件。再用这个lib调用dll里的函数。

__cdecl 和 __stdcall

VC上编译C和C++程序时,默认使用__cdecl函数调用约定。如果想生成__stdcall的函数,我们可以使用/GZ编译.例如:

cl /Gz /LD mylib.cpp

这样生成的dll和lib就是使用的__stdcall约定

通过下面的命令

dumpbin /exports mylib.dll 或mylib.lib 我们可以看到

    ordinal hint RVA      name

          1    0 0000107E _printhello@4

下面是__cdecl的函数名书写规格

    ordinal hint RVA      name

          1    0 0000107E printhello

可见__stdcall里多了前缀和后缀。

DEF文件格式:
LIBRARY DLLNAME.DLL
EXPORTS
fonctionname1   @1
fonctionname2    @2
................

(注意__cdecl和__stdcall调用约定的DEF文件书写区别。)


从DLL制作def文件:

MinGW上的实现方法:

pexports mydll.dll -o > mydll.def

MSVC上的实现方法:

1. 制作一份dll的导出函数表,使用VC的dumpbin命令
dumpbin mydll.dll /exports > mylib.def
2. 打开def文件修改之
i) 添加 LIBRARY mydll.dll
    EXPORTS
ii) 在EXPORTS后面加入改好的需要导出到lib中函数名列表

(注意: 因为函数调用约定的不同,所以导出的函数名会有前缀或后缀,这些都尽量不要修改。否则可能无法正常调用!具体操作,后面会举例说明)
(非常值得注意的是: 这个EXPORTS的函数名列表,有些情况下,你可能根本无法知道这些函数名的书写规则。不要总认为dumpbin中得到的名字就可以通用了。也不要认为pexports得到的def文件就不用修改了。事实上,如果我们声明函数的时候没有用extern "C"{}. 那么你在VC中调用一个MinGW的dll时你就会发现,问题变得很棘手。def的函数名书写规则和你要调用dll的函数约定有关,和dll里的前后缀无关。切记!)
另外, 在使用dll里的函数的时候,需要一份.h文件,对调用的函数进行声明。这里的函数名里没有那些附加的前后缀的,这点要注意。


在VC中生成dll和lib(调用dll库)

cl /LD mylib.cpp    (得到mylib.dll和mylib.lib)

在MinGW中生成dll, def和a(调用dll库)
g++ mylib.cpp -shared -o mylib_linux.dll -Wl,--output-def,mydll.def,--out-implib,mylib.a

在VC中生成静态库lib(不使用dll)

(注意: 静态库(lib和a)实际上就是一种归档文件)

cl -c mylib.cpp    编译cpp得到obj

lib mylib.obj /out:mylib.lib (生成归档文件mylib.lib)

在MinGW中生成静态库a(不使用dll)
g++ -c mylib.cpp   得到o文件

ar r mylib.a mylib.o 生成归档文件mylib.a

VC中调用VC的dll (lib方式)

分为有lib文件和无lib文件两种情况。第二种情况需要先生成lib文件再调用。

我们感兴趣的是第二种情况。具体操作实战如下:
我们只有一个mylib.dll, 需要调用一个输出函数printhello
1. 制作def
dumpbin mylib.dll /exports >mylib.def
得到一份完整的输出函数列表。
在开头添加 LIBRARY mylib.dll
再把下面的输出函数信息
    ordinal hint RVA      name

          1    0 0000107E printhello
    修改成
    EXPORTS
         
printhello       @1
其他的信息都删除。

也可以在MinGW上直接调用 pexports mylib.dll > mylib.def (这就是MinGW的方便之处了)

2. 生成lib
需要mylib.dll和mylib.def
lib /machine:ix86 /def:mylib.def
这样就会生成mylib.lib和mylib.exp两个文件。(mylib.exp可以删除)

3. 通过lib调用dll
在程序main.cpp中加入#include "mylib.h"
这样就可以调用这个函数了

cl main.cpp mylib.lib   编译生成main.exe文件。
(注意:这个程序的运行需要dll的参与!编译后lib文件可以删除,但mylib.dll不能删除,切记!)

MinGW调用MinGW的dll (直接连接 和 a连接)

MinGW的dll可以像静态库.a那样直接连接使用。

g++ main.cpp mylib.dll

也可以使用

g++ main.cpp -lmylib

直接调用dll编译生成main.exe文件。非常的简单方面。

当然,从研究的角度来说,我们讨论一下如何通过库文件a来实现连接。其方法和VC的情况雷同。

考虑无a文件的情况。
1. 制作def
直接调用 pexports mylib.dll > mylib.def

2. 生成a
需要mylib.dll和mylib.def
dlltool --dllname mylib.dll --def mylib.def --output-lib libmylib.a

这样就会生成库文件libmylib.a文件。

3. 通过a调用dll

在程序main.cpp中加入#include "mydll.h"
这样就可以调用dll里的函数了

g++ main.cpp libmylib.a -o main.exe 编译生成main.exe文件。

VC中调用MinGW的dll
现在如何在VC中调用MinGW生成的mylib_linux.dll呢?
(注意:VC无法使用MinGW的a文件,也无法像MinGW那样直接调用dll)
我们可以使用def文件生成VC可用的lib,通过lib调用

如果你没有def文件,那就用前面说过的方法(dumpbin(手动),或pexports(自动)) 生成一个。

如果你已经有了def文件。内容如下:
LIBRARY mylib_linux.dll
EXPORTS
printhello              @1

下面的命令可以根据def生成lib

lib /machine:ix86 /def:mydll.def      生成mydll.lib。
通过lib调用dll

cl main.cpp mydll.lib 生成main.exe调用dll

MinGW中调用VC的dll

如过dll是__cdecl约定,那么可以像静态库那样直接使用。如果

如果使用的是__stdcall调用约定。这时,我们无法像__cdecl那样直接使用了。我们有两种思路,一种是生成VC上的lib,然后直接调用。另一种是制作def和a文件,通过他们调用dll。

(注意,在这种情况下,不能用reimp从lib得到a。即使这个lib可以直接使用。生成的a也不能用。想得到可用的a文件需要按以下步骤操作)

1. 制作def
调用 pexports mylib.dll | sed "s/_//" > mylib.def (sed部分去除函数名前的_)

未完待续。。。

2. 生成a
需要mylib.dll和mylib.def
dlltool -U -D mylib.dll -d mylib.def -l libmylib.a (注意,这个-U绝对不能少)

这样就会生成库文件libmylib.a文件。

3. 通过a调用dll

在程序main.cpp中加入#include "mydll.h"
这样就可以调用dll里的函数了

g++ main.cpp libmylib.a -o main.exe 编译生成main.exe文件。

注意,调用__stdcall的函数也必须声明为__stdcall如下:

extern "C"
{
void __stdcall printhello(char *str);
}

在VC中则不需要修改代码,cl编译的时候使用/Gz即可。

写到这里,基本上已经讨论的差不多了。至于静态库.lib和.a之间的转换。据说这是同一种类型的归档文件,不同的只是归档里包含的内容。lib里包含的是.obj文件,a里包含的是.o文件。然而,这两种文件的格式据说也是相同的,然而我们发现lib和a无法通用!(请注意,这里的静态库lib不是调用dll时的那种静态库lib。那种lib只是起到索引和连接dll的功能,而这里所说的静态库是脱离dll工作的库,函数过程都包含在库里了)。以前我曾经写过一篇文章,讨论过cygwin上的库和mingw通用的方法。其实现在我们可以更清晰的明白,他们之所以通用是因为两者都是用gcc编译的。同一种编译器出来的结果当然可以兼容。那篇文章的价值在于。cygwin虽然工具包很多,但是要独立运行还需要dll支持。而mingw则可以生成不依赖于dll独立运行的程序。

那么lib和a不能通用的原因到底在哪里呢?起初觉得有可能在编译器上。因为vc的编译器cl和gcc的编译器编译出来的obj和o文件虽然格式相同却不能通用。实验表明,拿o文件给cl用,或拿obj给gcc用都通不过。然而,我对这点还不表示怀疑!因为我觉得最可能的原因并不在这里。行不通的原因很可能是两个编译器调用了不同的库函数。cl调用了MSVC提供的库函数,而gcc调用了他自己的库函数。所以我们回发现,通用静态库失败时显示的都是库函数没定义之类的错误。其实是函数名的符号不能识别。如果知道所需的库函数的具体文件,并加入到项目的编译中,相信很可能就会解决问题。不论如何,目前静态库的通用方法还有待进一步的探讨。

发表于 2010-12-17 16:08 zgf的blog 阅读(3681) | 评论 (7)编辑 收藏
2010年7月23日

一直发现网络传输比较耗CPU,昨天做了个比较全面的测试,发现了许多比较有趣的东西。
测试平台为在396MHZ的arm 926ejs  64M DDR133, 系统为wince6.0 r3版。  测试的方法为PC以恒定的速率发送数据,arm平台全力接收。
数据中百分比为系统空闲百分比,初始时为87%的空闲。tcp没有分包的概念,这里指每次用send发送时的长度。数据中720kbytes 等表示每秒传输的数据长度。

1. TCP测试
1)tcp   分包大小1024  流量720kbytes   CPU空闲56%      14byte +  20byte + 20byte + 920byte  = 974
2)tcp   分包大小921   流量648kbytes   CPU空闲57%
3)tcp   分包大小920   流量700kbytes   CPU空闲66%
    分析:2)比3)指是发送的包长度多了1个字节,但CPU占用率多了9%,而1)比3)发送的包长度大更多,CPU占用率却和2)差不多。为什么会这样呢?通过Ethereasl抓包分析,发现PC发送时一包最长为920字节。3)每次send只需要一个包就够了。2)呢,先发一个920字节的包,然后再发一个1字节的包。问题就在这里了,1)和2)每send一次实际上发了2个TCP包,所以CPU占用率差不多,而1)只需要发一个TCP包,所以CPU占用率低9%。  另外,由最大长度为920字节可以知道,这个平台的MTU应为920 + 20 + 20 = 960字节。第一个20为IP头,第二个20为TCP头。

2. UDP测试
1)udp   分包大小1473  流量1037kbytes  CPU空闲57%     14byte +  20byte + 8byte + 1472byte  = 1514
2)udp   分包大小1472  流量1036kbytes  CPU空闲60%
3)udp   分包大小1400  流量986kbytes   CPU空闲60%
4)udp   分包大小1025  流量722kbytes   CPU空闲60%
5)udp   分包大小1024  流量720kbytes   CPU空闲66%
6)udp   分包大小920   流量706kbytes   CPU空闲68%
7)udp   分包大小800   流量718kbytes   CPU空闲67%
    分析: 1)比2)包长只多了1字节,CPU占用率相差3%。 同样是MTU的问题。用Ethereasl抓包可以看到分包大小为1473时会先发一个1472字节的UDP包,然后再发一个1字节的IP包。而2)只用发一个包。 1472这个门槛怎么来的呢?是这样的,PC的MTU为1500,减去20字节的IP头,8个字节的UDP头,剩下就是1472字节了。又有个问题,既然前面知道arm平台上的MTU为960字节,那PC上发出的包已经超过960字节了,怎么arm平台没丢包呢?这个问题我也不清楚。也许wince的UDP接收部分根本就没管这个MTU。 另外,从4)到5)也有一个跳变。具体原因也不清楚。


从上面数据可以看到,大约720kbytes/s的网络流量,占了20%的CPU时间。并且TCP和UDP都差不多,与之前想的TCP协议应该比UDP协议更耗CPU的想法并不一致。另外按老大的推论:arm平台接收了所有的广播包,读取其中的目的地址,有效的往上面的TCP/IP协议栈传,无效的丢弃。因此可以发送广播包,看到底是接收网络包占CPU还是TCP/IP协议栈占CPU。下面是测试数据。

192.168.1.255广播    分包大小256  720kbytes     76%
192.168.1.255广播    分包大小1024  720kbytes     81.6%
192.168.1.255广播    分包大小1400  720kbytes     80.3%
192.168.1.255广播    分包大小1480  720kbytes     76% 14byte +  20byte + 8byte + 1472byte  = 1514
192.168.1.255广播    分包大小1500  720kbytes     76%

可以看到,网络接收并没耗多少CPU(大约6%),大部分还是TCP/IP协议栈处理占了CPU时间。

发表于 2010-07-23 13:20 zgf的blog 阅读(1701) | 评论 (3)编辑 收藏
2010年7月13日
编译过程参考 http://jeremiah.blog.51cto.com/539865/d-1中的文章
这里给一个完整的过程

1。 安装cygwin, 
         确定以下安装包被选中
Archive        
                unzip    
                zip    
Devel        
                autoconf    
                automake    
                binutils    
                cvs    
                gcc    
                gcc-core    
                gcc-g++    
                gcc-mingw    
                gcc-mingw-core    
                gcc-mingw-g++    
                gdb    
                gettext    
                gettext-devel
                git    
                libiconv
                libgcrypt-devel
                libtool    
                make    
                mingw-runtime
                nasm
                patchutils
                pkg-config    
                subversion    
Editor        
                vim    
Libs        
                expat
                libgcrypt
Web        
                curl
                wget
2。 下载vlc-1.1.0的源码  http://download.videolan.org/pub/videolan/vlc/1.1.0/vlc-1.1.0.tar.bz2
          用到的库 http://download.videolan.org/pub/testing/win32/contrib-20100616-win32-bin-gcc-4.4.4-runtime-3.17-only.tar.bz2
         LUA库  http://www.lua.org/ftp/lua-5.1.4.tar.gz
3。 将contrib-20100616-win32-bin-gcc-4.4.4-runtime-3.17-only.tar.bz2库解压到 X:/cygwin 下面,用RAR解压就可以
4。 删除/usr/win32/bin下面的moc,rcc,uic这三个文件
5。  解压lua-5.1.4.tar.gz库,  修改src/Makefile,将 CC = gcc 改为 CC = gcc -mno-cygwin
6。 在lua-5.1.4目录下执行  make mingw和make install
7。 解压vlc-1.1.0.tar.bz2
8。 cd到vlc-1.1.0目录下,运行  ./bootstrap     成功后显示 Successfully bootstrapped
7。 新建myvlc.sh, 注意是unix格式的哦。用UltraEdit子类的创建
         将下面的脚本粘贴过去
PATH=/usr/win32/bin:$PATH \
PKG_CONFIG_LIBDIR=/usr/win32/lib/pkgconfig \
CPPFLAGS="-I/usr/win32/include -I/usr/win32/include/ebml" \
LDFLAGS=-L/usr/win32/lib \
CC="gcc -mno-cygwin" CXX="g++ -mno-cygwin" \
./configure \
  --host=i686-pc-mingw32 \
  --enable-nls --enable-sdl \
  --enable-avcodec --enable-avformat --enable-swscale \
  --enable-faad --enable-flac --enable-theora \
  --enable-freetype \
  --enable-fribidi \
  --disable-fluidsynth \
  --enable-live555 --with-live555-tree=/usr/win32/live.com \
  --enable-caca \
  --with-dvdnav-config-path=/usr/win32/bin \
  --disable-vcdx --enable-goom \
  --enable-twolame --enable-dvdread \
  --enable-debug --enable-dca \
  --disable-mkv --disable-taglib --disable-projectm \
  --disable-zvbi --disable-schroedinger --disable-dirac \
  --enable-mozilla --with-mozilla-sdk-path=/usr/win32/gecko-sdk
8。执行./myvlc.sh
9。修改工作
         jeremiah文章中提到的错只用修改第二个就可以了,也就是:
(行号不准,附近找找)
注释掉Makefile.am第700,730,741行,就是行首加入#。
#             cp "$(top_srcdir)/extras/package/win32/vlc.exe.manifest" "$(win3
2_destdir)/"

#             cp $(top_srcdir)/projects/mozilla/npvlc.dll.manifest $(win32_des
tdir)/mozilla/

#             cp $(top_srcdir)/projects/activex/axvlc.dll.manifest $(win32_des
tdir)/activex/

10。 然后make ,  make package-win32-base
     
发表于 2010-07-13 08:25 zgf的blog 阅读(2060) | 评论 (6)编辑 收藏
2010年5月8日

以关联.rmvb文件格式到MediaPlayer为例
1. 在 HKEY_CLASSES_ROOT下建键 .rmvb
2. 将.rmvb的(Default) 值改为videofile
3. 在 .rmvb下添加 名为Content Type的值,将Value设为video/rmvb

好啦。现在直接双击就可以用mediaplayer打开rmvb格式的文件了。当然,系统中要有rmvb的splitter和decoder才行。

发表于 2010-05-08 15:08 zgf的blog 阅读(3371) | 评论 (20)编辑 收藏
2009年12月21日
        播放的过程一般是:读取码流、解码,显示格式转换,显示。其中“显示格式转换”是指YUV到RGB的转换。因为大部分解码器输出的格式是YV12格式的,而framebuf的格式是RGB格式的。“显示格式转换”虽然过程简单,却极耗时间。在200M Hz的arm cpu上单解352x288的mpeg4视频,能达到24fps。如果转换成RGB输出就只有12fps多一点了。幸运的是一般嵌入式cpu显示部分都有单独的video层,可以直接接收yuv数据。这样可以去掉“显示格式转换”步骤,性能直接提升一倍。不幸的是我用的芯片的video层格式只支持yuv422打包格式。写到这我再一次忍不住bs  n下中星微。同样是用arm核做cpu,怎么就比别人差一大截呢。其实只要稍微用点心就可以做好的事。
发表于 2009-12-21 13:20 zgf的blog 阅读(2410) | 评论 (2)编辑 收藏
2009年12月18日
今天安装PB6后创建一个ce6的项目,结果点击完成后又弹回创建窗口,在任务栏显示项目创建失败。
在网上搜索大部分都是说IE7的安全导致的。vs2005 sp1已修正了这个问题。可是我既没装IE7,也安装了
SP1补丁。找了好久,终于发现了一个方法:
运行
x:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe /ResetSkipPkgs
然后再新建项目
发表于 2009-12-18 18:12 zgf的blog 阅读(4821) | 评论 (32)编辑 收藏
2009年11月7日
    按上一篇文章的步骤,原本以为已编译完成。谁知在生成安装包的时候出问题了。

1.  安装NSIS,这是个制作安装包的编译器。我用的是2.45汉化版。
2.  用NSIS打开trunk\bin\distrib下的ffdshow.nsi文件,然后编译。这时NSIS提示找不到libavcodec.dll文件
3.  原来还少了几个工程。用vs2005继续编译以下几个工程。
ffdshow\src\ffmpeg\libavcodec.vcproj
ffdshow\src\mplayer\libmplayer_dll.vcproj
ffdshow\src\codecs\x264\ff_x264.vcproj
ffdshow\src\codecs\libmpeg2\libmpeg2_ff.vcproj
4. 再执行步骤2,应该可以生成ffdshow的安装包。


    至此ffdshow编译完全了。参考ffdshow.nsi文件,完全可以将ffshow的安装集成到自己的程序中。也就是说,只要加上UI就可以很容易做出个像暴风那样的播放器了。
发表于 2009-11-07 20:32 zgf的blog 阅读(3961) | 评论 (6)编辑 收藏
 

1. 用TortoiseSVN从 https://ffdshow.svn.sourceforge.net/svnroot/ffdshow下载最新的ddshow源代码
2. 安装好direct2008sdk,包括编译baseclass和添加到vs2005的库包含路径
3. 下载nasm-2.07-win32.zip,解压后将nasm.exe复制到vsvs2005安装目录下的vc\bin目录下。例如,我机器的目录是E:\Program Files\Microsoft Visual Studio 8\VC\bin。
  并改名为nasmw.exe

4. 打开ffdshow_2005.sln,点生成解决方案。
5. 这时会遇到链接不到dinput.lib库的错误。在ffdshow项目上右键,选择属性,将链接库中的dinput.lib改为dinput8.lib
6. 修改如下代码
    1)搜索DIRECTINPUT_VERSION,将#define DIRECTINPUT_VERSION 0x0300
改为#define DIRECTINPUT_VERSION 0x0800
    2)将TkeyboardDirect.h中的
struct IDirectInput;
struct IDirectInputDevice;
改为:
struct IDirectInput8;
struct IDirectInputDevice8;
     3) 将TkeyboardDirect.h中的
 IDirectInput *di;
 IDirectInputDevice *did;
改为:
 IDirectInput8 *di;
 IDirectInputDevice8 *did;
     4)将TkeyboardDirect.cpp中的
DirectInputCreate(0,DIRECTINPUT_VERSION,&di,NULL);if (!di) return;
改为
 DirectInput8Create(0,DIRECTINPUT_VERSION,IID_IDirectInput8,(void**)&di,NULL);if (!di) return;
7. 再重新生成解决方案。应该可以成功了

发表于 2009-11-07 08:30 zgf的blog 阅读(4930) | 评论 (17)编辑 收藏
2009年3月12日
     前几天同事用sf上的一个网络类库写了个服务器。一测试发现性能很差。最多每秒才能处理500次请求,并且是在网络很好的情况下,隔两个交换机后客户端就只能收到200次/秒的正确响应了。同事忙着做其它事,改进服务器的任务就交给我了。
     项目中客户端的请求仅是有20bytes的数据,并且只有一小部分需要服务器回复500bytes左右的数据。综合考虑各种网络模型后我决得IOCP模型更适合当前的应用。
     IOCP模型的使用方法很多资料都有。《windows网络编程》(第二版)讲得很好。随书光盘中有使用IOCP模型的简单但很好的例子。我的服务器程序写完时还没看到这个示例,正被其它示例代码里的锁弄的晕头转向。当看到这个示例后发现那些锁都是多余的。剩下的代码基本上差不多。

     有点不同的是PER_IO_DATA的处理上。所有的例子在往完成端口投递请求时都先分配了一个PER_IO_DATA,请求处理后马上释放。考虑到我的应用中所有请求全部是短连接,并且数据量很小,我觉得分配和释放是浪费的。每个PER_IO_DATA对应一个socket,当socket关闭后这个PER_IO_DATA不必释放,而是用来准备接受下一个连接socket。
     下面是我写的工作线程代码:
DWORD WINAPI ServerWorkerThread(LPVOID lpparam)
{
    CIocpServer 
* pServer = (CIocpServer*)lpparam;
    DWORD BytesTransferred;
    SOCKET socket;
    LPPER_IO_OPERATION_DATA PerIoData;
    BOOL close_socket 
= FALSE;
    
int ret;
    
while(pServer->bRun)
    
{
        ret 
= GetQueuedCompletionStatus(pServer->CompletionPort, &BytesTransferred,
            (LPDWORD)
&socket, (LPOVERLAPPED *&PerIoData, INFINITE);
        
if (ret == ERROR_SUCCESS)
        
{
            DWORD last_error 
= GetLastError();
            
if(last_error == ERROR_SUCCESS)
                
return 0;                //完成端口被关闭,退出
            if(ERROR_NETNAME_DELETED == last_error   
                
|| ERROR_OPERATION_ABORTED == last_error)
                close_socket 
= TRUE;    //socket被关闭 或者 操作被取消
            else
                
continue;
        }


        
if (BytesTransferred == 0)
        
{
            
if(socket == 0 && PerIoData == 0)
                
break;

            closesocket(PerIoData
->socket);

            pServer
->Accept(PerIoData);

            
continue;
        }


        
if(PerIoData->eType == IO_EVENT_ACCEPT)
        
{
            setsockopt(PerIoData
->socket,SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&(pServer->m_server), sizeof(pServer->m_server) ) ;

            
if(CreateIoCompletionPort((HANDLE) PerIoData->socket, pServer->CompletionPort, (DWORD) PerIoData->socket,0== NULL)
                printf(
"CreateIoCompletionPort error:%d ", GetLastError());

            ret 
= pServer->Recv(PerIoData,BytesTransferred);
        }

        
else if(PerIoData->eType == IO_EVENT_WSARECV)
        
{
            ret 
= pServer->Recv(PerIoData,BytesTransferred);
        }

        
else if(PerIoData->eType == IO_EVENT_WSASEND)
        
{
            ret 
= pServer->Send(PerIoData,BytesTransferred);
        }


        
if(ret == FALSE)
        
{
            closesocket(PerIoData
->socket);
            pServer
->Accept(PerIoData);

        }

    }


    
return 0;
}


      可以看到,每个AccepteEx接入的客户端连接对应着一个PER_IO_DATA,并伴随着该连接的整个生命周期。当连接结束后,PER_IO_DATA马上又通过投递AccepteEx准备下一个连接。这样可以避免平凡的分配和释放内存。
发表于 2009-03-12 00:01 zgf的blog 阅读(5347) | 评论 (15)编辑 收藏