分享
 
 
 

网络播放器

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

作者:afterain

本人最近刚刚把它做完。鉴于现在很多 人在向这方面发展,所以决定把自己 在此期间的一些经验写出来。让后来的同志们少走些弯路。

我的这个事例是通过directshow的例子memfile改写的。如果用于网络的时时播放,会有一些延时问题。具体会在后面说明。我已经把它作成了DLL(实际也是工作的需要 :) ),大家可以在www.feelby.net下载。包括演示例子的源代码。至于DLL中的其他代码,可以参考我原来的文章,可在CSDN的开发文档中找到(关键字用“direct”),说明了一些directshow的基本知识和对他的操作。

先说说memfile例子的整体框架。实际上,directshow已经封装好了几个类,CasyncReader和CasyncStream是我们最关心的,CasyncReader已经是个source filter了,而我们只需通过CasyncStream类就可以控制数据了。CasyncStream类很简单,都是一些纯虚函数。我们是继承它,把它的函数完善就行了。

现在把工作的重点放在CasyncStream类。Memfile是继承了它得到自己的类CmemStream。因为这个类有了一些函数的总体框架,所以我就用它做为父类了(当然,完全可以直接从CasyncStream继承)。有三个重要的函数:SetPointer(LONGLONG llPos),Read(PBYTE pbBuffer,DWORD dwBytesToRead,BOOL bAlign,LPDWORD pdwBytesRead)和Size(LONGLONG *pSizeAvailable)。其中重中之重是Read函数,(实际上我已经弃用SetPointer函数了)。所以的数据操作都是在这里完成的。下面通过具体的代码来说明。

参数的说明:

m_pbData 读写的内存数据指针

m_llLength 数据的总长度

m_llPosition 实际读写的内存数据位置指针

m_dwKBPerSec 播放的的速率

由于初期时,操作内存数据指针m_pbData总是出错,所以改用自己的指针。本来是打算用m_llPosition来虚拟个无限大的内存空间(实际就是循环0---max,读前面的数据,刷新后面的,接着读后面的,刷新前面的来达到这效果),可是要烦琐一些,有些临界条件难以判断。所以实际上是我只用了它的参数m_llLength。大家可以通过memfile的源代码来学习m_llPosition+ m_pbData的用途。

m_llLength是个非常重要的参数。如果你要做网络的实时监控,当然不希望播放了几个小时就停了。通过修改它可以达到你们需要的长度。它是LONGLONG型的,就是说2的64次方。足够播放n年了 : ) 。

NOTE:如果你把它改的小,不论你怎么添加内存数据都不能持续的播放。directshow播放完这个长度的数据后就自动的停止了。

首先,初始化参数:

m_PlayBuf = new BYTE[32768*10];//我自己定义的数据缓冲

m_Buf Size = 0;//缓冲区未播放的数据大小,开始没数据,当然为0

m_llLength = 4000000000000;//这个大小足够播放了,4T的数据

hMutex = CreateMutex(NULL,TRUE,"protect buf");//这是个HANDLE型的,用于播放和添加数据时,保护数据的完整性

接着就是Read函数了,它是自动调用的,而且是个work thread,参数pbBuffer是输出变量,就是要播放的数据,pdwBytesRead也是输出变量,表示读了的数据长度,其余是输入变量:

HRESULT Read(PBYTE pbBuffer,

DWORD dwBytesToRead,

BOOL bAlign,

LPDWORD pdwBytesRead)

{

CAutoLock lck(&m_csLock);

DWORD dwReadLength;

dwReadLength = dwBytesToRead;//只有在最后的数据包改写该参数(因为不一定会符合32768的大小,我默认不修改)

///////////////////////////handle buf

while (32768>m_Buf Size);//wait for add new data

WaitForSingleObject(hMutex, 1L); file://这一小段是关键

CopyMemory((PVOID)pbBuffer, (PVOID)m_PlayBuf,dwReadLength);//从我们的缓冲中得到要播放的数据

ReleaseMutex(hMutex);

m_Buf Size -= dwReadLength;//未播放的数据大小减去刚刚播放的数据量dwReadLength

CopyMemory((PVOID)m_PlayBuf, (PVOID)(m_PlayBuf+dwReadLength),m_Buf Size);//把未播放的数据移动到m_PlayBuf的开头,这样我们就不需要位置指针m_llPosition。这样有个好处,就是,m_llPosition实际上播放是会有一次从229376返回0,所以在播放位置的判断很麻烦,这就是为什么我弃用这个参数的原因。

m_llPosition += dwReadLength;//这是memfile的代码,我没有去掉

*pdwBytesRead = dwReadLength;

return S_OK;

}

最后就是我们怎么样才能更新我们的数据呢?在这里建立个线程比较合理。下面的函数是我自己添加到CmemStream类的。然后在主程序中建立线程来调用该函数。

LONGLONG AddBuf(PBYTE buf)

{

if ((m_Buf Size +32768)>32768*10){//当添加的数据超过了我们的缓冲大小,则返回-1告诉调用程序,具体的处理由调用程序决定,是丢弃还是重发

return -1;

}

WaitForSingleObject(hMutex, 1L);

CopyMemory((PVOID)(m_PlayBuf+m_Buf Size),(PVOID)buf,32768);//把新的数据添加到我们自己的缓冲中

ReleaseMutex(hMutex);

m_Buf Size += 32768;//未播放的数据增加 了32768个字节

return m_Buf Size;

}

NOTE:上面多次有32768这个数字。这是它默认的数据大小,(修改可不容易,还不如自己写个新的source filter)。这就是我最开始提到的“延时问题”的问题的关键。因为一定需要32768字节的数据才能播放,所以32768就是我们延时的数据大小,你一定要等到数据增加到32768才能给出播放。如果32768对你的压缩数据来说,是一秒的数据,那么就延时一秒,如果是3秒的数据量,那么就延时3秒。这是这个类的限制。要想真正的实时,还是自己写source filter吧。我看过一个实时的产品,它好象有自己的compress 和uncompress filter等。

在网络的实时播放时,最重要的是数据的同步(得到新数据和播放之间),也就是Read()函数这个线程和AddBuf()函数这个线程之间的同步。如果AddBuf过快,数据就会丢失,过慢,则造成播放速度缓慢。我对多线程不是很有研究,所以我的播放事例只是简单的重发。当然你也可以丢包,不过,可以看到播放的效果就不行了。我也有代码同步,只是简单的Sleep()一段时间。是实际测试出来的(本地文件播放,只是演示。实际本地文件播放,只需重发包就行了,不会造成数据包的丢失)。也用与网络的播放,实测是1-2秒的时间延时。补充一点,数据包的丢失并不会造成播放的中断,只是画面上的停顿。

演示例子的说明

只是个简单的事例。很多的代码没有整理。

其中最主要的的是建立一个线程AddBufThreadProc。不断的添加数据。

DWORD AddBufThreadProc(LPVOID p)

{

CFile f;

PBYTE buf = new BYTE[32768];

int eof = 0;

f.Open("e:\\R-161936-0600.mpg",CFile::modeRead);

while(1)//add buf

{

eof = f.Read(buf,32768);//get new data,you can change it,example used socket

if(eof!=32768)//if data finish

break;

// Sleep(175);//this way,if add too quick,maybe can lost data

while(STREAM_SendBuf(buf)==-1);//this way,not lost data

}

f.Close();

return 0;

}

NOTE:

要注意的是,这个类本来是异步读文件的,受到天生的限制,用于实时播放是不太适合的(特别是当你的数据量相对32768来说,如果是5秒的话,是不能接受的。我的项目就是能够调节数据量,当最大时,延时小,只有1秒,数据量小时超过5秒)。如果用于实时的话,它只适合局域网或是宽带网。

当然,如果只是作为网络播放文件还是比较好的。如果网络速度低的话,还是传输MPEG-4 的好。默认是MPEG-1格式的,可以通过memfile的例子看到如何修改播放格式的,支持的挺多的:)。

有什么好的想法或是建议可以联系:afterain@263.net

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有