本 人转载自: http://blog.csdn.net/navi_dx/article/details/1885780

一. 引言

去年年底帮别人做一个项目,了解了一下TCPMP,觉得这个软件的结构写得很好就做了些记录,今天偶然翻出来看看觉得可能对正在研究这个软件的人有点帮助就贴出来。如果转载请注明出处,谢谢。

二. TCPMP简介

TCPMP是一个功能强大开放式的开源多媒体播放器,
播放器主要由核心框架模块(common工程)和解码器分离器插件组成。
TCPMP的插件非常多,libmad我们联合几个最常用的插件(ffmpeg、splitter)来说明,其中interface插件实现TCPMP的界面,由于他和媒体播放没有什么关系,这部分可以完全被替换掉,替换成自己的界面。在interface中有最开始的入口地址main:

DLLEXPORT void Main(const tchar_t* Name,const tchar_t* Version,int Id,const tchar_t* CmdLine) { SAFE_BEGIN if (Context_Init(Name,Version,Id,CmdLine,NULL)) { WaitEnd(); WinPopupClass(INTERFACE_ID,NULL); Context_Done(); } SAFE_END }

三. 目录简介

1、FFMPEG 简介

ffmpeg工程是系统主要的音视频解码模块,ffmpeg是一个集录制、转换、音/视频编码解码功能为一体的完整的开源解决方案。FFmpeg的开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用。

ffmpeg支持MPEG、DivX、MPEG4、AC3、DV、FLV等40多种编码,AVI、MPEG、OGG、Matroska、ASF等90多种解码。很多开源播放器都用到了ffmpeg。但是ffmpeg程序解码效率不是很高,系统仅仅使用了FFmpeg的部分解码功能。

ffmpeg主目录下主要有libavcodec、libavformat和libavutil等子目录。

其中libavcodec用于存放各个encode/decode模块,libavformat用于存放muxer/demuxer模块,libavutil用于存放内存操作等常用模块。本系统的媒体文件分离器有单独的splitter模块完成所以不需要libavformat子目录。ffmpeg目录下libavcodec、libavutil保留子目录。

2. libmad 简介

libmad工程用于MP3文件解码,该工程包含两个功能模块:

一个负责解析MP3文件格式,包括MPEG1音频文件 (MP1,MP2,MP3,MPA),读取每一帧音频数据;

另一个负责解码MPEG1音频数据,解码代码在libmad子目录中。
libmad是一个开源的高精度 MPEG1音频解码库,支持 MPEG-1(Layer I, Layer II 和 LayerIII,也就是 MP3)。libmad提供 24-bit 的 PCM 输出,完全是定点计算,非常适合没有浮点支持的平台上使用。使用 libmad 提供的一系列 API,就可以非常简单地实现 MP3 数据解码工作。在 libmad 的源代码文件目录下的 mad.h 文件中,可以看到绝大部分该库的数据结构和 API 等。libmad是用的fixed-integer,通过整数模拟小数计算的,精度只能保证到小数点后第9位(大于0的最小值 0.00000000372529),虽然解码精度会有损失,但是极大提高了解码效率,特别是在嵌入式设备上也可以实现高码率MP3文件的解码。

3. splitter工程

splitter工程用于解析多种音视频文件格式。可以解析的文件格式包括:ASF媒体文件,视频文件 (AVI,DIVX),Windows波形文件 (WAV,RMP),MPEG电影文件 (MPEG,MPG,MPV),MPEG4文件 (MP4,3GP,M4A,M4B,K3G)。以上格式可以被解析但是数据编码不一定能正确解码,需要依赖系统的解码器。

4. common 工程

common工程是核心模块,是一个开放的集数据输入、转换、音/视频解码、信号输出等功能为一体的完整的多媒体播放框架。这个框架自身不包含任何的Decode和Split功能,这些功能由插件实现,核心模块以一个树状结构管理所有的功能模块和插件模块,实现数据Render功能,对输入、转换、输出流程的控制,接受播放过程中的操作和对事件进行处理,同时也实现系统运行中经常使用的一些共用函数,比如解码过程中经常使用的逆离散余弦变换,内存操作,界面中需要使用的多语言字符处理等。

common工程的主目录下主要有:blit、dyncode、overlay、pcm、softidct、win32、zlib等子目录。

其中blit和overlay存放是视频信号渲染模块,

pcm存放PCM音频信号转换模块,

softidct存放逆离散余弦变换函数,

win32存放内存操作等常用模块,

dyncode这个目录的代码比较晦涩,存放的是程序运行是动态生成代码模块,针对不同的CPU指令集,PCM数据数据声道和采样率不同,视频渲染数据格式和色深等不同情况动态生成不同的优化代码(这段代码非常精彩,不能不让人佩服TCPMP作者的高超水平)。

四. 核心模块介绍

核心模块有一个上下文对象context,该对象在初始化函数bool_t Context_Init(……)中候创建了一个该对象实例。该对象实例记录管理各个功能模块,用户界面可以通过该对象和核心模块交互,管理控制播放过程。

Context对象说明:

typedef struct context { int Version; // 版本信息 uint32_t ProgramId; const tchar_t* ProgramName; // 应用程序名称 const tchar_t* ProgramVersion; // 程序版本号,字符串 const tchar_t* CmdLine; // 程序命令行信息 void* Wnd; // 视频渲染窗口句柄 void* NodeLock; // 功能模块访问临界区互斥变量 array Node; // 功能模块数据对象数组 array NodeClass; // 功能模块定义对象数组,按照系统逻辑关系组织 array NodeClassPri; // 功能模块定义对象数组,按照系统逻辑关系和模块优先级排列 array NodeModule; // 外部插件模块数组 int LoadModuleNo; // 当前正在装载的外部插件序号 void* LoadModule; array StrTable[2]; // 字符串资源数组,字符串分为 // 给底层使用的标准字符串资源和 // 给界面使用的显示字符串资源,两种资源用两个数组表示 array StrBuffer; array StrModule; // 未使用 void* StrLock; // 字符串数组访问临界区互斥变量 uint32_t Lang; // 当前使用语言标志 int CodePage; // 当前使用代码页标志 struct pcm_soft* PCM; // PCM音频信号转换模块 struct blitpack* Blit; // 视频信号渲染模块 struct node* Platform; // 得到平台相关信息 struct node* Advanced; // 得到播放模块高级信息 struct node* Player; // 播放控制模块 notify Error; // 信息错误回调函数 int (*HwOrientation)(void*); // 屏幕旋转信息,在某些系统中屏幕可以旋转90度或180度 void *HwOrientationContext; bool_t TryDynamic; // 未使用 int SettingsPage; // 未使用 size_t StartUpMemory; // 可以使用的有效内存数 bool_t InHibernate; // 是否进入休眠状态 bool_t WaitDisable; // 未使用 int FtrId; // 未使用 bool_t LowMemory; // 可以使用的有效内存数是否小于系统要求的最低要求 //动态代码生成中间状态及数据 bool_t CodeFailed; bool_t CodeMoveBack; bool_t CodeDelaySlot; void* CodeLock; void* CodeInstBegin; void* CodeInstEnd; int NextCond; bool_t NextSet; bool_t NextByte; bool_t NextHalf; bool_t NextSign; uint32_t* FlushCache; // 未使用 void* CharConvertUTF8; // 未使用 void* CharConvertCustom; // 未使用 int CustomCodePage; // 未使用 void* CharConvertAscii; // 未使用 void* Application; void* Logger; // 未使用 bool_t KeepDisplay; // 是否保持背光长亮 int DisableOutOfMemory; // 未使用 } context;

核心模块上下文指针可以通过全局函数获得context* Context();

static context* ContextPtr = NULL; void SetContext(context* Ptr) { ContextPtr=Ptr; NodeRegLoadValue(PLATFORM_ID,PLATFORM_LANG,&Ptr->Lang,sizeof(int),TYPE_INT); } context* Context() { return ContextPtr; }

初始化上下文对象的全局函数是bool_t Context_Init(const tchar_t* Name,const tchar_t* Version,int Id,const tchar_t* CmdLine,void* Application);其中Name参数为应用程序名称,Version为版本信息字符串。
释放上下文对象的全局函数是void Context_Done();。
void Context_Wnd(void*);函数将视频播放窗口句柄初始化给设备上下文。

五. 功能模块介绍(以节点方式组织模块)

功能模块包含定义对象和数据对象,定义对象描述功能模块相互间的逻辑结构,数据对象记录模块属性和方法。
所有的功能模块结构按一个树状结构来组织,结构关系如下,NODE是整个结构的根结点,其下为子节点,节点按类型可分为实节点,全局节点,设置节点,抽象节点。
#define CF_SIZE 0x00FFFFFF
#define CF_GLOBAL 0x01000000
#define CF_SETTINGS 0x02000000
#define CF_ABSTRACT0x08000000
抽象节点没有对应的对象实例,类似C++的抽象基类,为了按照逻辑关系组织系统结构而存在,例如NODE就是抽象节点。

全局节点全局只有一个对象的实例,如播放控制模块PLAYER_ID。

设置节点表示和系统播放设置相关,比如声音均衡器模块EQUALIZER_ID,颜色控制模块COLOR_ID。

实节点与抽象节点不同,指可以生成对象实例的节点,实节点没有特殊标识,一般以数据对象占用内存大小表示是否是一个实节点,创建节点时要根据该信息分配内存单元,实节点也可以有子节点. 例如:MMS_ID的父节点是HTTP_ID。

全局节点,设置节点和实节点可以相互组合,比如播放控制节点同时是全局节点,设置节点和实节点。节点名称后带_ID的就是实节点,否则就是抽象节点。

NODE (根节点)
├─FLOW (流控制模块)
│ ├─CODEC (解码模块)
│ │ ├─EQUALIZER_ID (声音均衡器模块)
│ │ ├─VBUFFER_ID (视频缓冲模块)
│ │ ├─DMO (DirectX Media Object)
│ │ │ ├─WMV_ID
│ │ │ ├─WMS_ID
│ │ │ ├─WMVA_ID
│ │ │ ├─WMA_ID
│ │ │ └─WMAV_ID
│ │ ├─FFMPEG VIDEO (FFMpeg 解码模块)
│ │ └─LIBMAD_ID (Libmad Mp3解码模块)
│ ├─OUT (信号渲染模块)
│ │ ├─AOUT (音频信号渲染)
│ │ │ ├─NULLAUDIO_ID
│ │ │ └─WAVEOUT_ID
│ │ └─VOUT (视频信号渲染)
│ │ ├─NULLVIDEO_ID
│ │ └─OVERLAY
│ ├─IDCT (离散余弦解码模块)
│ │ └─SOFTIDCT_ID
│ └─CODECIDCT(离散余弦解码模块,函数比IDCT要少)
│ └─MPEG1_ID
├─MEDIA (媒体文件格式编码解析模块)
│ ├─FORMAT (格式解析模块)
│ │ └─FORMATBASE
│ │ ├─RAWAUDIO
│ │ │ └─MP3_ID
│ │ ├─RAWIMAGE
│ │ ├─ASF_ID
│ │ ├─AVI_ID
│ │ ├─MP4_ID
│ │ ├─MPG_ID
│ │ ├─NSV_ID
│ │ └─WAV_ID
│ ├─PLAYLIST (播放列表模块)
│ │ ├─ASX_ID
│ │ ├─M3U_ID
│ │ └─PLS_ID
│ └─STREAMPROCESS (数据流处理模块)
├─STREAM (数据输入模块)
│ ├─MEMSTREAM_ID (内存数据流模块)
│ ├─FILE_ID (文件IO模块)
│ └─HTTP_ID (网络数据获取模块)
├─TIMER (定时器模块)
│ └─SYSTIMER_ID
├─ASSOCIATION_ID (文件扩展名自动关联模块)
├─ADVANCED_ID (高级设置模块)
├─COLOR_ID (颜色控制模块)
├─PLATFORM_ID (平台信息模块)
├─XSCALEDRIVER_ID (Intel XScale CPU 信息模块)
├─PLAYER_ID (播放控制模块)
└─PLAYER_BUFFER_ID (播放缓冲模块)

节点树状结构由若干个静态定义对象(nodedef)实例实现,
typedef struct nodedef { int Flags; int Class; int ParentClass; int Priority; nodecreate Create; nodedelete Delete; } nodedef;

Flags表示当前节点的类型:抽象、实节点、全局、设置。
Class表示当前节点的标识,如MEDIA_CLASS或ASF_ID等等。
ParentClass表示当前节点的父节点标识,如SYSTIMER_ID对象的父节点是TIMER_CLASS。
Priority表示当前节点优先级。
Create和Delete是两个函数指针,表示该节点的创建函数和销毁函数。
如播放控制模块的结构定义是
static const nodedef Player = { sizeof(player_base)|CF_GLOBAL|CF_SETTINGS, PLAYER_ID, NODE_CLASS, PRI_MAXIMUM+600, (nodecreate)Create, (nodedelete)Delete, };

绝大多数节点都有一个对应的数据对象,记录该节点的数据和方法,每一个子节点对象都是以父节点对象作为该节点一个元素,类似C++的封装继承机制。如果子节点的父节点没有数据对象,该节点可以从node节点直接继承。每一个节点都可以看成Node节点的直接或间接子节点,所以所有节点头以一个相同的node结构开头,子节点可能还有自己的属性,在继承父对象后就是子节点自己的元素。

typedef struct node { int Class; nodeenum Enum; nodeget Get; nodeset Set; } node;

Class表示该对象的标识,如PLAYER_ID。
Enum是一个函数指针,指向一个函数用于枚举当前节点的属性。
Get是一个函数指针,得到当前节点某一属性值。
Set是一个函数指针,设置当前节点的某一属性数值。

节点的属性值数据特性在一个static const datatable xxxParams[] = {……};的静态数组里定义。
typedef struct datatable { int No; int Type; int Flags; int Format1; int Format2; } datatable;
No表示属性的标识,如播放控制模块的#define PLAYER_PLAY 0x32 就表示控制播放器播放或暂停。
Type表示属性的数据类型,可用值在node.h中定义。
Flags是属性数据的标志,表示该数据是不是只读数据,是否有最大最小值等等,可用值在node.h中定义,如果该标志包含DF_SETUP同时不包含DF_NOSAVE和DF_RDONLY属性,该属性会被记录在注册表中,下次启动时用注册表的数据初始化该属性。
Format1和Format2是可选标志与Flags配合使用,比如如果Flags表示该属性存在最大最小值,Format1和Format2可以分别表示最小和最大数值。

在系统上下文对象中有两个元素记录节点信息array Node;和array NodeClass;,array是数组数据类型,Node是节点数据对象的数组,NodeClass节点对象的数组,按照系统逻辑关系组织。创建节点时传入nodedef对象到节点创建函数,函数会根据nodedef信息生成对应nodeclass对象添加到NodeClass数组,同时根据nodedef信息分配数据对象的内存空间。在该节点的Create函数里面再初始化该节点的数据对象。

在所有功能模块中和界面加交互的主要就是播放控制模块struct node* Player;使用方法如下:

context* p = Context(); player* myplayer = NULL; if(p) myplayer = (player*)(p->Player);

六、播放控制简介

1、控制播放参数

使用Set(void* This,int No,const void* Data,int Size);函数。

第一个参数是播放模块指针,

第二个参数是控制代码,即要进行什么操作,

第三个参数是需要赋值给控制代码的数值,

最后一个参数是所赋数值的占用内存的大小。
例如开始播放的代码是:

PLAYER_PLAY为控制代码,表示当前控制的是播放暂停功能,数值为1表示播放为0表示暂停。
得到某一控制属性使用Get(void* This,int No,void* Data,int Size);函数,参数含义和Set函数相同。
控制代码是一组宏,定义在player.h文件中。比较重要的控制参数有

播放控制模块所有可用参数见static const datatable PlayerParams[]结构。

static const datatable PlayerParams[] = { { PLAYER_AUTOPREROTATE, TYPE_BOOL, DF_SETUP }, { PLAYER_REPEAT, TYPE_BOOL, DF_SETUP|DF_HIDDEN }, { PLAYER_SHUFFLE, TYPE_BOOL, DF_SETUP|DF_HIDDEN }, { PLAYER_KEEPPLAY_AUDIO,TYPE_BOOL, DF_SETUP }, { PLAYER_KEEPPLAY_VIDEO,TYPE_BOOL, DF_SETUP }, { PLAYER_SHOWINBACKGROUND,TYPE_BOOL, DF_SETUP }, { PLAYER_SINGLECLICKFULLSCREEN, TYPE_BOOL, DF_SETUP }, { PLAYER_KEEPLIST, TYPE_BOOL, DF_SETUP }, { PLAYER_PLAYATOPEN, TYPE_BOOL, DF_SETUP }, { PLAYER_PLAYATOPEN_FULL,TYPE_BOOL, DF_SETUP },

2、 添加一个媒体文件到播放模块

使用int PlayerAdd(player* Player,int Index, const tchar_t* Path, const tchar_t* Title);
第一个参数为播放模块指针,

第二个参数是添加到播放模块文件队列的序号,如果是使文件成为第一个文件该参数设为0,

第三个参数是媒体文件的目录和名称,

第四个参数为媒体文件标题,该参数可以忽略。

3、多语言的管理

核心模块也管理多语言字符串,使用函数const tchar_t* LangStr(int Class, int Id);和const tchar_t* LangStrDef(int Class, int Id)可以得到对应字符串,系统字符串资源有两种,标准字符串和特殊字符集字符串。标准字符串资源文件是工程目录下的lang_std.txt文件,该文件字符串为ASCII字符,可与其他代码页字符兼容。该文件记录的是核心模块运行时需要使用的字符串,Decode和Splite模块可以处理的编码格式和文件格式也在这个文件中记录,例如lang_std.txt文件中的
MP3_0001=audio/mpeg
MP3_0002=mp1:A;mp2:A;mp3:A;mpa:A
MP3_0200=acodec/0x0055
纪录了MP3文件分离器对应的文件类型、扩展名和文件特征码。
要得到标准字符串使用函数LangStrDef,第一个参数表示字符类别,第二个参数表示字符ID。界面相关的是特殊字符集的字符串,使用函数LangStr,第一个参数表示字符类别,第二个参数表示字符ID。关于字符串资源文件结构含义将在以后的文档中说明。

TCPMP之旅(一) TCPMP整体软体框架相关推荐

  1. 软体机器人与拓扑优化

    背景介绍   软体机器人在军事以及医疗等方面都具有广泛的应用前景,因此,本推文对相关的研究进行简要概括,并针对现存的问题提出一些自己的思考与解决办法.   磁性软体机器在生物医学领域具有广泛的应用,例 ...

  2. 机器人参数化建模与仿真,软体机器人

    专题一:机器人参数化建模与仿真分析.优化设计专题课程大纲 机器人建模基础 机器人运动学基础 几何运动学闭环解解析法建模 运动学MATLAB脚本文件编写(封闭解.构型绘制).工具箱 机器人工作空间(离散 ...

  3. 软体机器人结构设计与仿真分析专题

    软体机器人技术是近年来机器人领域最为热门的研究领域之一.软体机器人具有天然的柔性.自适应性.低成本和被动安全性,在人机交互.医疗服务等领域具有广泛的应用前景.同时,软体机器人的研究涉及软材料.机构设计 ...

  4. 软体机器人对工业应用的影响

    原创 | 文 BFT机器人 软机器人模仿生物体的运动和动作,使它们具有高度的多功能性和迷人性. 软机器人领域正在迅速发展.它旨在为各种行业创造灵活的设备,包括医疗保健.太空探索.食品生产.地理.物流. ...

  5. 基于STM32 ARM+ FPGA 的软体机器人的 CAN总线运动控制器的设计

    针对在软体机器人控制时 , 多电机协同控制过程中难度大 . 通用性差 . 协同性差等缺点 , 设计了基于 ARM 和 FPGA 的软体机器人的控制器局域网络 ( controller area net ...

  6. 仿生软体机器人就业咋样_用这个软体机器人来摘水果,你觉得怎么样?

    据外媒报道,哈佛大学的研究人员研发了很多仿生软体机器人,它们能够爬行.游泳.抓取易碎物品,甚至帮助心脏跳动,但它们无一能够感知和响应周遭的世界. 然而,很快这将成为历史. 受到我们身体感官能力的启发, ...

  7. 明光市机器人_“柠檬皮”软体机器人!26岁小伙联合发明光磁场机器人|专访...

    会行走.能搬货,还会 "跳街舞".近日,美国西北大学发明出一款软体机器人,该机器人看起来像一只剥开的柠檬皮,它能在水箱中运动,并拥有多种本领,比如搬运物品.催化化学反应.输送治疗剂 ...

  8. 明光市机器人_“柠檬皮”软体机器人!26岁重庆小伙联合发明光磁场机器人,可爬坡、可卸货,有望用于体内药物递送|专访...

    会行走.能搬货,还会 "跳街舞".近日,美国西北大学发明出一款软体机器人,该机器人看起来像一只剥开的柠檬皮,它能在水箱中运动,并拥有多种本领,比如搬运物品.催化化学反应.输送治疗剂 ...

  9. ListControl 拉伸_《自然·材料》:本征可拉伸电致发光弹性体,无线供电点亮软体机器人...

    近年来,可拉伸电子器件发展迅速,成长为一个全新的科研大方向.拉伸发光显示器件是其中的重要组成部分,是整个可拉伸电子系统的信息输出窗口.可拉伸发光器件有多种制备方法,主要分为基于可拉伸电路的发光器件和本 ...

  10. 机器人大牛 Daniela Rus 领衔!MIT 新算法实现软体机器人「本体感知」

    本文转自雷锋网,如需转载请至雷锋网官网申请授权. 说起软体机器人,或许很多人都不觉得陌生了. 软体机器人的发展离不开包括材料学.机器人学.生物力学.传感与控制在内的多学科进步,近年来相关学科迅速发展, ...

最新文章

  1. 调试JDK源码-ConcurrentHashMap实现原理
  2. Guice学习(一)
  3. VS2010,C++ 制作静态库(*.lib),并使用
  4. juc原子类之五:AtomicLongFieldUpdater原子类
  5. git status
  6. android 选择多选图片
  7. 计算机图形学全代码,计算机图形学作业参考代码
  8. 哨兵2影像的分幅分割-深度学习的样本制备基础
  9. [技术讨论][DDS] AD9833原理介绍及chiliDDS驱动分享(上)
  10. 曲线运动与万有引力公式_专题三曲线运动与万有引力定律
  11. 【软件技巧】【截图】浏览器自带的全网页截图工具
  12. Chrome/Edge插件-在线视频画质增强,支持视频加速
  13. JAVA数据结构之Map和Set
  14. 工厂系列系统之 MES 系统8 生产计划-订单
  15. 考题篇(5.2) 06. IPsec ❀ FortiGate ❀ Fortinet 网络安全专家 NSE 4
  16. 求最大公约数和最小公倍数的方法:
  17. 【应用多元统计分析】——第三章(1)
  18. Windows 安装MySQL 8.0 超详细教程(mysql 8.0.30)
  19. 珞珈一号01星(luojia1-01)的夜间灯光影像python爬取
  20. 阅读《首先,打破一切常规》有感

热门文章

  1. python打印100以内质数_python输出100以内的质数与合数实例代码
  2. (曲率系列4:)基于点拟合二次曲面并求曲率
  3. python操作wps表格_python3怎么用pandas读wps表格,pandas python教程
  4. c语言自学教材百度网盘,110G超强C语言和C++编程0基础从入门到精通自学教程 百度云盘资源...
  5. 虚拟服务器连接监控,方案:浅谈虚拟化平台的监控管理
  6. kali linux 清华源_Kali Linux 更新源
  7. java snakeyaml_JAVA基于SnakeYAML实现解析与序列化YAML
  8. python chmod_使Python脚本可执行chmod755?
  9. 【爬虫】用Python爬取《Nature》杂志文章的题目、摘要、下载地址
  10. matlab神经网络