加载介质的流程主要在common工程中player.c文件的Load函数
static int Load(player_base* p,bool_t Silent,bool_t OnlyLocal,bool_t Nested)
//首先获取播放地址串的MIME,即scheme。如:file,mem,http,mms等。
URL = p->PlayList[p->Current].URL;
GetMime(URL,Mime,TSIZEOF(Mime),&p->HasHost);
//枚举STREAM_CLASS中的所有流式输入模块,取得MIME对应的流式输入模块
Input = GetStream(URL,Silent);
p->Input = Input;
//获取播放介质的显示标题
if (p->PlayList[p->Current].Title[0])
tcscpy_s(p->Title,TSIZEOF(p->Title),p->PlayList[p->Current].Title);
else
URLToTitle(p->Title,TSIZEOF(p->Title),URL);
//为流式输入模块设置参数comment,silent,url等。
//网络流式输入模块在设置url后一般就会启动网络流程,获取介质基本信息。
Comment.No = PLAYER_COMMENT;
Comment.Node = (node*)p;
p->Input->Set(p->Input,STREAM_COMMENT,&Comment,sizeof(pin));
p->Input->Set(p->Input,STREAM_SILENT,&Silent,sizeof(Silent));
p->Input->Set(p->Input,STREAM_URL,URL,TSIZEOF(p->PlayList[p->Current].URL));
//获取该介质的长度
if (p->Input->Get(p->Input,STREAM_LENGTH,&Length,sizeof(int))!=ERR_NONE)
Length = -1;
//分配4KB数据空间,先从流式模块中尝试读取4KB数据作Probe之用。
if (!AllocBlock(PROBE_SIZE,&Probe,0,HEAP_ANY))
{
Unload(p,Silent,0,1);
return ERR_OUT_OF_MEMORY;
}
ProbeSize = p->Input->ReadBlock(p->Input,&Probe,0,PROBE_SIZE);
//获取介质的Content-Type
p->Input->Get(p->Input,STREAM_CONTENTTYPE,ContentType,sizeof(ContentType));
//根据介质的后缀名,Content-Type以及Probe数据枚举STREAMPROCESS_CLASS分支下的所有模块,一般情况下该分支下并没有模块。
NodeEnumClassEx(&List,STREAMPROCESS_CLASS,ContentType,URL,Probe.Ptr,ProbeSize);
//根据介质的后缀名,Content-Type以及Probe数据枚举FORMAT_CLASS分支下的所有模块,找到对应的容器格式解析模块。
NodeEnumClassEx(&List,FORMAT_CLASS,ContentType,URL,Probe.Ptr,ProbeSize);
//其中NodeEnumClassEx函数枚举的匹配顺序为Content-Type,Extensions,Probe
if ((ContentType || URL || Data) &&
(!ContentType || !CheckContentType(ContentType,LangStrDef(Id,NODE_CONTENTTYPE))) &&
(!URL || !CheckExts(URL,LangStrDef(Id,NODE_EXTS))) &&
(!Data || !DataProbe(Data,Length,LangStrDef(Id,NODE_PROBE))))
continue;
//设置匹配出的解析模块的输入为之前的流媒体输入模块
if (p->Format->Set(p->Format,FORMAT_INPUT,&p->Input,sizeof(stream*)) == ERR_NONE)
break;
//如果FORMAT_CLASS分支没有合适的解析模块,则播放串可能指向一个播放列表文件,尝试枚举PLAYLIST_CLASS中的所有模块。
if (!p->Format && !Nested) NodeEnumClassEx(&List,PLAYLIST_CLASS,ContentType,URL,Probe.Ptr,ProbeSize);
Flush(p);
StopLoadMode(p);
UpdatePlay(p);
UpdateRunInput(p);
UpdateRunProcess(p);
UpdateTimeouts(p);
TCPMP中的Stream节点是用于处理媒体数据输入的模块,其中common工程包含文件输入(FILE)以及内存输入(MEM)。Network工程则以插件形式实现了基于HTTP和MMS协议的流媒体插件模块。下面以HTTP模块为例进行分析。
static const nodedef HTTP =
{
sizeof(http),
HTTP_ID,
STREAM_CLASS,
PRI_MINIMUM,
(nodecreate)Create,
(nodedelete)Delete,
};
其中http是一个自定义结构体,里面有一个stream结构体类型的变量Stream,记录当前的数据流及其接口函数。http模块的WinCE平台实现主要依赖WinInet API函数完成。
继承自STREAM的模块需要实现的接口主要包括:Set,Get,DataAvailable,Read,ReadBlock,Seek,EnumDir等函数。
static int Set(http* p, int No, const void* Data, int Size)
提供设置URL,Comment,Silent等参数。当设置播放URL时,就会调用Open函数启动网络进行HTTP下载。
static int Get(http* p, int No, void* Data, int Size)
提供获取URL,Comment,Silent,Length等参数
static int DataAvailable(http* p)
检查当前还有多少可读的数据。WinCE平台通过调用InternetQueryDataAvailable实现。
Format_base模块在读取数据(ReadBlock)之前会先调用DataAvailable,以便设定读取的数据量上限。以下为format_base.c相关代码:
int Available = Reader->Input->DataAvailable(Reader->Input);
if (Left > Available) Left = Available;
Length = Reader->Input->ReadBlock(Reader->Input,&Buffer->Block,Buffer->Length,Left);
static int Read(http* p,void* Data,int Size)
static int ReadBlock(http* p,block* Block,int Ofs,int Size)
ReadBlock函数实际内部调用Read函数,而Read函数则调用InternetReadFile函数从网络读取数据。这两个函数都实现为阻塞刑读取,设定读操作超时时间为180秒。读操作成功后立即更新http结构内部变量Pos。
static filepos_t Seek(http* p,filepos_t Pos,int SeekMode)
Seek函数为支持上层Seek操作所用,目前支持两种模式:
当SeekMode为SEEK_CUR时,则从当前播放点平移Pos值。(Pos正负不限)
当SeekMode为SEEK_SET时,则从介质文件开始处开始平移Pos值。(Pos>=0)
当SeekMode为SEEK_END时,则从介质文件末尾处开始平移Pos值。(Pos<=0)
static int EnumDir(http* p,const tchar_t* URL,const tchar_t* Exts,bool_t ExtFilter,streamdir* Item)
EnumDir支持枚举一个URL指向的HTML网页中所有的介质文件的地址串。