转载请标明是引用于 http://blog.csdn.net/chenyujing1234
欢迎大家提出意见,一起讨论!
TCP的码源框架介绍可以参考我的文章: TCPMP之旅(一) TCPMP整体软体框架
最近因为在WINCE播放视频文件,画面很不流畅, 于是想弄个TCPMP来试下效果. 没想到这个X86 平台上的TCPMP网络上还真的是找不到, 只能找到ARM平台的, 一打开就提示不是有效的WINCE应用程序. 于是便自己编译. 说到这个编译过程那可也是一波三折, 具体不再多说啦, 下面我列出所有我在编译过程中的遇到的错误.
报以下错误
1>'yasm' 不是内部或外部命令,也不是可运行的程序
解决方法:
从 http://yasm.tortall.net/Download.html 下载得到yasm-1.2.0-win32.exe文件 (我的电脑是32位的,根据实际情况)
然后把yasm-1.2.0-win32.exe 名字改为 yasm.exe,并把它复制到你VS2005的安装路径下,
eg:
之后再编译可能报以下错误:
1>yasm: FATAL: Could not open input file
原因可能有两个:
(1)是不是有中文路径
(2)路径中是否有空格。
2、1 在其它工程出现链接时提示找不到标识符时,在链接lib中加入我们刚刚编译的common.lib。(当然也得加入链接目录)
2、2 在编译player时会报错:
1>正在链接... 1>main.obj : error LNK2001: 无法解析的外部符号 __tcscpy_s 1>../debug/player.exe : fatal error LNK1120: 1 个无法解析的外部命令
解决方法是将main.c中的 第122行改为 123行.
以下是目标文件:
效果:
补充:附后了interface.plg插件外的插件加载方法。
加载方法同样通过DLLRegister接口。加载插件的过程是在Context_Init函数中的Plugins_Init函数完成的。
void Plugins_Init() { tchar_t Path[MAXPATH]; array List = {NULL}; nodedef Def; int* i; // 查找所有的插件,放到List里 FindPlugins(&List); for (i=ARRAYBEGIN(List,int);i!=ARRAYEND(List,int);++i) { int Class = *i; HKEY Key; tchar_t Base[256]; NodeBase(Class,Base,TSIZEOF(Base)); if (RegOpenKeyEx(HKEY_ROOT, Base, 0, KEY_READ, &Key) == ERROR_SUCCESS) { if (LoadValue(Key,NODE_MODULE_PATH,Path,sizeof(Path),TYPE_STRING) && NodeFindModule(Path,0)) { memset(&Def,0,sizeof(Def)); Def.Class = Class; Def.Flags = 0; Def.ParentClass = 0; Def.Priority = PRI_DEFAULT; LoadValue(Key,NODE_PARENT,&Def.ParentClass,sizeof(int),TYPE_INT); LoadValue(Key,NODE_PRIORITY,&Def.Priority,sizeof(int),TYPE_INT); LoadValue(Key,NODE_FLAGS,&Def.Flags,sizeof(int),TYPE_INT); LoadValueString(Key,Class,NODE_NAME); LoadValueString(Key,Class,NODE_CONTENTTYPE); LoadValueString(Key,Class,NODE_EXTS); LoadValueString(Key,Class,NODE_PROBE); NodeLoadClass(&Def,Path,0);// 把插件节点注册 } RegCloseKey(Key); } } ArrayClear(&List); }
(1) Plugins_Init先通过FindPlugins(&List);查找exe目录中的所有插件,找到即调用AddModule将
此节点存到Context p->NodeModule(用于管理外部插件模块数组)中去。
并根据注册表判断是否要加载此插件。
(2) 从common.dll目录下增加插件,从注册表中增加插件。
static void FindPlugins(array* List) { tchar_t* s; tchar_t Name[64]; tchar_t Path[MAXPATH]; tchar_t Path2[MAXPATH]; DWORD RegSize,RegType,NameSize,Disp; HKEY Key,KeyStamp; int No,Class; NodeBase(0,Path,TSIZEOF(Path)); tcscat_s(Path,TSIZEOF(Path),T("\\DLLStamp")); if (RegCreateKeyEx(HKEY_ROOT, Path, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &KeyStamp, &Disp) != ERROR_SUCCESS) KeyStamp = NULL; // 在exe目录中增加插件 GetModulePath(Path,NULL); FindFiles(Path,T("*.plg"),AddModule,&KeyStamp); // 可能exe是在和common.dll 不同的目录,所以去common.dll目录下查找其他插件 GetModulePath(Path2,T("common.dll")); if (tcsicmp(Path2,Path)!=0) FindFiles(Path2,T("*.plg"),AddModule,&KeyStamp); // additional plugins NodeBase(0,Path,TSIZEOF(Path)); tcscat_s(Path,TSIZEOF(Path),T(".Plugins")); if (RegOpenKeyEx(HKEY_ROOT, Path, 0, KEY_READ, &Key) == ERROR_SUCCESS) { for (No=0;;++No) { NameSize = TSIZEOF(Name); RegSize = sizeof(Path); if (RegEnumValue(Key,No,Name,&NameSize,NULL,&RegType,(LPBYTE)Path,&RegSize)!=ERROR_SUCCESS) break; if (RegType == REG_SZ) AddModule(Path,&KeyStamp); } RegCloseKey(Key); } if (KeyStamp) { // delete unused stamps ArrayClear(List); RegSize = TSIZEOF(Path); for (No=0;RegEnumValue(KeyStamp,No,Path,&RegSize,NULL,NULL,NULL,NULL)==ERROR_SUCCESS;++No) { if (!NodeFindModule(Path,0)) ArrayAppend(List,Path,(tcslen(Path)+1)*sizeof(tchar_t),256); RegSize = TSIZEOF(Path); } for (s=ARRAYBEGIN(*List,tchar_t);s!=ARRAYEND(*List,tchar_t);s+=tcslen(s)+1) RegDeleteValue(KeyStamp,s); RegCloseKey(KeyStamp); } ArrayClear(List); NodeBase(0,Path,TSIZEOF(Path)); if (RegOpenKeyEx(HKEY_ROOT, Path, 0, KEY_READ, &Key) == ERROR_SUCCESS) { RegSize = TSIZEOF(Name); for (No=0;RegEnumKeyEx(Key,No,Name,&RegSize,NULL,NULL,NULL,NULL)==ERROR_SUCCESS;++No) { if (tcslen(Name)==4) { Class = StringToFourCC(Name,0); ArrayAppend(List,&Class,sizeof(Class),128); } RegSize = TSIZEOF(Name); } RegCloseKey(Key); } }
(3) 把插件节点注册。即把节点加入到Context p->NodeClass中
void NodeLoadClass(const nodedef* Def,const tchar_t* Module,int ModuleId) { context* p = Context(); int ModuleNo = FindModule(p,Module,ModuleId); if (ModuleNo>=0) Register(p,Def,0,ModuleNo); // 注册节点 }
Interface插件功能模块包含在interface文件夹下,其中的interface.c实现图形界面的大部分功能,
还有小部分功能在win_win32.c文件中实现.程序运行过程基本有下面几个步骤:
它先加载e:\Au1380_forVolo\tcpmp_from_xianshengWin32\debug\ilayer.exe ,假如它不存在就加载 interface.plg。
然后转到interface.plg的main函数中。
#if !defined(TARGET_WINCE) && defined(UNICODE) int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hParent,LPSTR CmdA,int CmdShow) { WCHAR Cmd[2048]; mbstowcs(Cmd,CmdA,sizeof(Cmd)/sizeof(WCHAR)); //!!! #else int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hParent,TCHAR* Cmd,int CmdShow) { #endif #ifndef NDEBUG // DLLTest(); // just to help debugging plugins. comment out if not needed Context(); #endif #if defined(TARGET_WINCE) && defined(ARM) if (ProgramId == 2) { OSVERSIONINFO Ver; Ver.dwOSVersionInfoSize = sizeof(Ver); GetVersionEx(&Ver); if (Ver.dwMajorVersion*100 + Ver.dwMinorVersion >= 421) { // old shell menu not supported after WM2003SE MessageBox(NULL,T("This ARM_CE2 version of the player is not compatible with this device. Please install ARM_CE3 version."),NULL,MB_OK|MB_ICONERROR); return 1; } } #endif #ifdef NDEBUG if (!FindRunning(Cmd)) { HANDLE Handle = CreateMutex(NULL,FALSE,ProgramName); if (GetLastError() != ERROR_ALREADY_EXISTS) #endif { #ifndef NO_PLUGINS HMODULE Module; SetCursor(LoadCursor(NULL, IDC_WAIT)); Module = Load(T("interface.plg")); if (Module) { void (*Main)(const tchar_t* Name,const tchar_t* Version,int Id,const tchar_t* CmdLine); *(FARPROC*)&Main = GetProcAddress(Module,TWIN("Main")); if (!Main) *(FARPROC*)&Main = GetProcAddress(Module,TWIN("_Main@16")); if (Main) Main(ProgramName,ProgramVersion,ProgramId,Cmd); FreeLibrary(Module); } #else Main(ProgramName,ProgramVersion,ProgramId,Cmd); #endif #ifdef NDEBUG CloseHandle(Handle); #endif SetCursor(LoadCursor(NULL, IDC_ARROW)); } #ifdef NDEBUG } #endif return 0; }
是一个开放的集数据输入、转换、音/视频解码、信号输出等功能为一体的完整的多媒体播放框架。WinMain后调用 common.dll中的Contxt_Init函数,完成各个功能模块ID的注册工作.除Interface功能外,其他功能模块的注册基本在 Contxt_Init中完成.
DLLEXPORT void Main(const tchar_t* Name,const tchar_t* Version,int Id,const tchar_t* CmdLine) { SAFE_BEGIN // 完成各个功能模块ID的注册工作.除Interface功能外,其他功能模块的注册基本在 Contxt_Init中完成. if (Context_Init(Name,Version,Id,CmdLine,NULL)) { WaitEnd(); // 在这里才把界面显示出来 WinPopupClass(INTERFACE_ID,NULL); Context_Done(); } SAFE_END }
int WinPopupClass(int Class,win* Parent) { int Result = 0; // 创建节点 node* p = NodeCreate(Class); // 是WIN_CLASS类 if (p && NodeIsClass(p->Class,WIN_CLASS)) { // 创建窗口 Result = ((win*)p)->Popup((win*)p,Parent); NodeDelete(p); } return Result; }
NodeCreate完成创建节点功能,它是common.lib中的接口。此接口不仅被interface模块调用,也被很多模块调用。
node* NodeCreate(int ClassId) { context* p = Context(); // 从类中创建节点 return NodeCreateFromClass(p,FindClass(p,ClassId)); }
NodeCreateFromClass调用interface.plg中DLLRegister函数。
通过DLLRegister函数对INTREFACE_ID对象进行注册,对象注册完成创建和内存分配等功能.
====================补充 start ==================================================
interface插件的注册过程( 其实与其它插件的注册过程一致,都是通过调用common.dll库完成的):
(1)NodeCreateFromClass中有LoadModule,LoadModule中有NodeLoadModule,
它寻找对应于ID为INTREFACE_ID的插件对应的plg,从plg中找到DLLRegister接口,来注册自己。
void* NodeLoadModule(const tchar_t* Path,int* Id,void** AnyFunc,void** Db) { HMODULE Module; int Error = 0; #if !defined(TARGET_WINCE) int OldMode = SetErrorMode(SEM_NOOPENFILEERRORBOX| SEM_FAILCRITICALERRORS); #endif Module = LoadLibrary(Path); if (!Module) { Error = GetLastError(); if (Error == ERROR_NOT_ENOUGH_MEMORY || Error == ERROR_OUTOFMEMORY) { NodeHibernate(); Module = LoadLibrary(Path); if (!Module) Error = GetLastError(); } } Context()->LoadModule = Module; #if !defined(TARGET_WINCE) SetErrorMode(OldMode); #endif if (Module) { FARPROC Func = GetProcAddress(Module,TWIN("DLLRegister")); if (!Func) Func = GetProcAddress(Module,TWIN("_DLLRegister@4")); if (Func) { int Result; if (AnyFunc) *(FARPROC*)AnyFunc = Func; // set before calling DLLRegister // DLLRegister注册自己. Result = ((int(*)(int))Func)(CONTEXT_VERSION); if (Result != ERR_NONE) { Func = NULL; if (AnyFunc) *(FARPROC*)AnyFunc = NULL; if (Result == ERR_NOT_COMPATIBLE) PluginError(Path); } } if (!Func) { FreeLibrary(Module); Module = NULL; } } else if (Error == ERROR_NOT_ENOUGH_MEMORY || Error == ERROR_OUTOFMEMORY) ShowOutOfMemory(); else PluginError(Path); return Module; }
====================补充 end==================================================
static node* NodeCreateFromClass(context* p, nodeclass* Class) { node **Empty = NULL; node **i; nodemodule* Module; node* Node; int Size; nodeclass *j; if (!Class || (Class->Def.Flags & CF_ABSTRACT)) return NULL; Size = 0; for (j=Class;j;j=j->Parent) if (Size < (j->Def.Flags & CF_SIZE)) Size = (j->Def.Flags & CF_SIZE); if (!Size) return NULL; LockEnter(p->NodeLock); if ((Class->Def.Flags & CF_GLOBAL) && (Module = LoadModule(p,Class->ModuleNo))!=NULL && (Node = NodeEnumObject(NULL,Class->Def.Class))!=NULL) { ++Module->ObjectCount; LockLeave(p->NodeLock); return Node; } for (i=ARRAYBEGIN(p->Node,node*);i!=ARRAYEND(p->Node,node*);++i) if (!*i) { Empty = i; break; } if (!Empty) { if (!ArrayAppend(&p->Node,NULL,sizeof(node**),256)) { LockLeave(p->NodeLock); return NULL; } Empty = ARRAYEND(p->Node,node*)-1; } *Empty = Node = (node*) malloc(Size); if (Node) { memset(Node,0,Size); Node->Class = Class->Def.Class; // 创建 Node节点 if (CallCreate(p,Node,Class,(Class->Def.Flags & CF_GLOBAL)!=0) != ERR_NONE) { for (i=ARRAYBEGIN(p->Node,node*);i!=ARRAYEND(p->Node,node*);++i) if (*i == Node) *i = NULL; free(Node); Node = NULL; } else { #ifndef REGISTRY_GLOBAL if (Class->Def.Flags & CF_GLOBAL) NodeRegLoad(Node); #endif Node->Set(Node,NODE_SETTINGSCHANGED,NULL,0); // should be after NodeRegLoad } } LockLeave(p->NodeLock); return Node; }
上面的代码主要调用CallCreate函数.
WinPopupClass(INTERFACE_ID,NULL);
// 创建窗口 Result = ((win*)p)->Popup((win*)p,Parent);
6.Popup函数中调用HandleMessage(p,&Msg),该函数为消息响应函数,Popup完成主窗口创建的后给HandleMessage函数发送WM_CREATE消息.
static int Popup(win* p,win* Parent) { HWND Wnd; MSG Msg; int Style = WS_VISIBLE; int ExStyle = 0; int x,y; int Width,Height; int Priority; // we need higher priority in main window for better user interface responses // but open dialog and other parts can't have that, because of some buggy keyboard drivers... Priority = ThreadPriority(NULL,Parent?0:-1); p->Result = 0; p->Parent = Parent; p->BitmapNo = 0; p->AygShellTB = 0; #if defined(TARGET_WINCE) if (AygShell && Parent) ExStyle |= WS_EX_CAPTIONOKBTN; if (Parent) Style |= WS_POPUP; { RECT r; GetWorkArea(p,&r); x = r.left; y = r.top; Width = r.right - r.left; Height = r.bottom - r.top; } #else Style |= WS_OVERLAPPEDWINDOW; y = x = CW_USEDEFAULT; Width = WinUnitToPixelX(p,p->WinWidth); Height = WinUnitToPixelY(p,p->WinHeight); #endif // 创建窗口 Wnd = CreateWindowEx(ExStyle,WinClass.lpszClassName,LangStr(p->Node.Class,NODE_NAME),Style,x,y,Width,Height, Parent?Parent->Wnd:NULL,NULL,WinClass.hInstance,p); if (Wnd) { if (p->Parent) { p->Parent->Child = p; EnableWindow(p->Parent->Wnd,0); } else Main = p; while (p->Wnd && GetMessage(&Msg, NULL, 0, 0)) HandleMessage(p,&Msg);// HandleMessage(p,&Msg),该函数为消息响应函数,Popup完成主窗口创建的后给HandleMessage函数发送WM_CREATE消息 if (Main == p) Main = NULL; } ThreadPriority(NULL,Priority); return p->Result; }
分两部分:
win_win32.c中的CALLBACK Proc首先响应,完成窗口工具栏的创建;
static LRESULT CALLBACK Proc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) { int Result = 0; win* p = (win*)GetWindowLong(Wnd,GWL_USERDATA); switch (Msg) { #if defined(TARGET_WINCE) case WM_HOTKEY: if (p->Parent && HIWORD(lParam)==VK_ESCAPE) { HWND Focus = GetFocus(); wincontrol* i = (wincontrol*)GetWindowLong(Focus,GWL_USERDATA); if (FuncSHSendBackToFocusWindow && ((VALIDCONTROL(i) && i->Editor) || (!VALIDCONTROL(i) && i))) FuncSHSendBackToFocusWindow(Msg,wParam,lParam); else { p->HotKeyEscape = !p->HotKeyEscape; if (!p->HotKeyEscape) PostMessage(p->Wnd,WM_CLOSE,0,0); } return 0; } break; #endif // 完成工具栏中系统菜单,进度条,播放控件和声音控制的创建 case WM_CREATE: p = (win*)((CREATESTRUCT*)lParam)->lpCreateParams; p->Wnd = Wnd; p->Module = ((CREATESTRUCT*)lParam)->hInstance; p->Result = 1; p->Focus = NULL; p->Closed = 0; UpdateDPI(p); #if defined(TARGET_WINCE) p->Activate = malloc(sizeof(SHACTIVATEINFO)); if (p->Activate) { memset(p->Activate,0,sizeof(SHACTIVATEINFO)); ((SHACTIVATEINFO*)p->Activate)->cbSize = sizeof(SHACTIVATEINFO); } #endif if (p->Proc) p->Proc(p,MSG_PREPARE,0,0,&Result); CreateToolBar(p); SetWindowLong(Wnd,GWL_USERDATA,(LONG)p); // only after CreateToolBar (WM_MOVE) if (p->Flags & WIN_DIALOG) { RECT r; GetClientRect(p->Wnd,&r); CreateWindow(DialogClass.lpszClassName,NULL,WS_CLIPCHILDREN|WS_CHILD|WS_VISIBLE, 0,p->ToolBarHeight,r.right,r.bottom-p->ToolBarHeight,p->Wnd,NULL,DialogClass.hInstance,p); } if (p->Init) p->Init(p); if (!p->Focus) WinNext(p,0); break;
然后interface.c中的bool_t Proc函数再响应完成工具栏中系统菜单,进度条,播放控件和声音控制的创建.
主要是在interface.c中的Proc 函数中.
TCPMP包含的内容比较丰富,要研究透彻还需要花很多功夫,继续努力中...