织梦CMS - 轻松建站从此开始!

罗索

DirectShow中网络播放器的源Source Filter

落鹤生 发布于 2011-11-03 22:17 点击:次 
本人最近刚刚把它做完。鉴于现在很多 人在向这方面发展,所以决定把自己 在此期间的一些经验写出来。让后来的同志们少走些弯路。我的这个事例是通过directshow的例子memfile改写的。如果用于网络的时时播放,会有一些延时问题。具体会在后面说明。
TAG:

作者: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播放完这个长度的数据后就自动的停止了。
首先,初始化参数:

  1. m_PlayBuf = new BYTE[32768*10];//我自己定义的数据缓冲 
  2. m_Buf Size = 0;//缓冲区未播放的数据大小,开始没数据,当然为0 
  3. m_llLength = 4000000000000;//这个大小足够播放了,4T的数据 
  4. hMutex   = CreateMutex(NULL,TRUE,"protect buf"); 
  5. //这是个HANDLE型的,用于播放和添加数据时,保护数据的完整性 

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

  1. HRESULT Read(PBYTE pbBuffer, 
  2. DWORD dwBytesToRead, 
  3. BOOL bAlign, 
  4. LPDWORD pdwBytesRead) 
  5.    CAutoLock lck(&m_csLock); 
  6.    DWORD dwReadLength; 
  7.  
  8.    dwReadLength = dwBytesToRead;
  9. //只有在最后的数据包改写该参数(因为不一定会符合32768的大小,我默认不修改) 
  10.    ///////////////////////////handle buf 
  11.    while (32768>m_Buf Size);//wait for add  new data 
  12.  
  13.    WaitForSingleObject(hMutex, 1L); file://这一小段是关键 
  14.    CopyMemory((PVOID)pbBuffer, (PVOID)m_PlayBuf,dwReadLength);
  15. //从我们的缓冲中得到要播放的数据 
  16.    ReleaseMutex(hMutex); 
  17.    m_Buf Size -= dwReadLength;//未播放的数据大小减去刚刚播放的数据量dwReadLength 
  18.    CopyMemory((PVOID)m_PlayBuf, (PVOID)(m_PlayBuf+dwReadLength),m_Buf Size);
  19. //把未播放的数据移动到m_PlayBuf的开头,这样我们就不需要位置指针m_llPosition。这样有个
  20. //好处,就 是,m_llPosition实际上播放是会有一次从229376返回0,所以在播放位置的判断很麻
  21. //烦,这就是为什么我弃用这个参数的原因。 
  22.  
  23.    m_llPosition += dwReadLength;//这是memfile的代码,我没有去掉 
  24.    *pdwBytesRead = dwReadLength; 
  25.    return S_OK; 
  26.  } 
  27.  
  28. //最后就是我们怎么样才能更新我们的数据呢?在这里建立个线程比较合理。下面的函数是我自己添
  29. //加到CmemStream类的。然后在主程序中建立线程来调用该函数。 
  30. LONGLONG AddBuf(PBYTE buf) 
  31.    if ((m_Buf Size +32768)>32768*10){//当添加的数据超过了我们的缓冲大小,则返回
  32. //-1告诉调用程序,具体的处理由调用程序决定,是丢弃还是重发 
  33.     return -1; 
  34.    } 
  35.    WaitForSingleObject(hMutex, 1L); 
  36.    CopyMemory((PVOID)(m_PlayBuf+m_Buf Size),(PVOID)buf,32768);//把新的数据添加到我们自己的缓冲中 
  37.    ReleaseMutex(hMutex); 
  38.    m_Buf Size += 32768;//未播放的数据增加 了32768个字节 
  39.    return m_Buf Size; 
  40.   } 

NOTE:上面多次有32768这个数字。这是它默认的数据大小,(修改可不容易,还不如自己写个新的source filter)。这就是我最开始提到的“延时问题”的问题的关键。因为一定需要32768字节的数据才能播放,所以32768就是我们延时的数据大小,你 一定要等到数据增加到32768才能给出播放。如果32768对你的压缩数据来说,是一秒的数据,那么就延时一秒,如果是3秒的数据量,那么就延时3秒。 这是这个类的限制。要想真正的实时,还是自己写source filter吧。我看过一个实时的产品,它好象有自己的compress 和uncompress filter等。
在网络的实时播放时,最重要的是数据的同步(得到新数据和播放之间),也就是Read()函数这个线程 和AddBuf()函数这个线程之间的同步。如果AddBuf过快,数据就会丢失,过慢,则造成播放速度缓慢。我对多线程不是很有研究,所以我的播放事例 只是简单的重发。当然你也可以丢包,不过,可以看到播放的效果就不行了。我也有代码同步,只是简单的Sleep()一段时间。是实际测试出来的(本地文件 播放,只是演示。实际本地文件播放,只需重发包就行了,不会造成数据包的丢失)。也用与网络的播放,实测是1-2秒的时间延时。补充一点,数据包的丢失并 不会造成播放的中断,只是画面上的停顿。

演示例子的说明
只是个简单的事例。很多的代码没有整理。
其中最主要的的是建立一个线程AddBufThreadProc。不断的添加数据。

  1. DWORD  AddBufThreadProc(LPVOID p) 
  2.  { 
  3.  CFile f; 
  4.  PBYTE buf = new BYTE[32768]; 
  5.  int   eof = 0; 
  6.  f.Open("e://R-161936-0600.mpg",CFile::modeRead); 
  7.    while(1)//add buf 
  8.    { 
  9.     eof = f.Read(buf,32768);//get new data,you can change it,example used socket 
  10.     if(eof!=32768)//if data finish 
  11.      break
  12. //    Sleep(175);//this way,if add too quick,maybe can lost data 
  13.     while(STREAM_SendBuf(buf)==-1);//this way,not lost data 
  14.    } 
  15.    f.Close(); 
  16.    return 0; 
  17.   } 

NOTE:
要注意的是,这个类本来是异步读文件的,受到天生的限制,用于实时播放是不太适合的(特别是当你的数据量相对32768来说,如 果是5秒的话,是不能接受的。我的项目就是能够调节数据量,当最大时,延时小,只有1秒,数据量小时超过5秒)。如果用于实时的话,它只适合局域网或是宽 带网。
当然,如果只是作为网络播放文件还是比较好的。如果网络速度低的话,还是传输MPEG-4 的好。默认是MPEG-1格式的,可以通过memfile的例子看到如何修改播放格式的,支持的挺多的:)。
有什么好的想法或是建议可以联系:afterain@263.net

(afterain)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201111/15267.html]
本文出处:blog.csdn.net/afterain 作者:afterain
顶一下
(3)
100%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容