网络播放器

原创 2002年01月05日 18:22:00

作者: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

Qt实现网络播放器

写了这么多的博客,关于网络的还不算多,经常有人询问一些关于网络传输、制作在线试听及下载音乐、构造及解析数据等的一些问题,今天就在这里一并讲解。 网络操作:     主要涉及:Q...
  • u010002704
  • u010002704
  • 2014年10月24日 09:26
  • 977

Android实现网络音乐播放器

本文是一个简单的音乐播放器布局代码
  • whuhan2013
  • whuhan2013
  • 2016年04月14日 21:38
  • 5758

Android简单网络音乐播放器

音乐播放需要调用service,在此,只是简单梳理播放流程。public class PlayMusicService extends Service { //绑定服务 调用服务的方法。 ...
  • confusing_awakening
  • confusing_awakening
  • 2017年05月02日 21:09
  • 285

简单几步实现网络音乐播放器(Qt版百度FM)

简单几步实现 百度FM音乐 Qt版本       很久之前写的软件了,一直没有总结,也懒得继续开发了,这里简单总结一下,也算是对自己的努力一个交代吧。        起源:不大习惯打开一个浏览器听歌,...
  • spygg
  • spygg
  • 2016年12月28日 03:35
  • 2586

kX3552 + HiFi级播放器 + wifi无线保真= 实战HiFi音乐娱乐欣赏

特别说明:此贴仅仅适合网络即时聊天平台乐一乐,不是技术贴,仅供参考   HiFi级别音乐欣赏 和 网络娱乐学习   研玩单位:第一位:(虚位以待)     第二位:电脑学习室(51vv --- 615...
  • bamboo2983
  • bamboo2983
  • 2015年10月27日 07:01
  • 1227

Qt/C++ 音乐播放器源码

Qt5音乐播放器 这是本人的第一条博客,排版什么的就将就看吧~ 一,我还在学生,学Qt来收获很大,至少是明白了Qt这些大大小小的问题 1.播放器做的很累人,网上虽有源码,但都过分的简单且不美观。 2....
  • what951006
  • what951006
  • 2016年04月27日 19:06
  • 8011

android 视频播放 -- 调用系统播放器

android 视频播放,视频来源有两种:一种是本地视频,一种是网络视频,对android播放来说都统一用uri来表示。 播放视频有三种处理方式: (1) 使用webview 播放 (2) 使用...
  • lue2009
  • lue2009
  • 2015年05月08日 16:48
  • 3050

【Android 多媒体开发】 MediaPlayer 网络视频播放器

.....
  • han1202012
  • han1202012
  • 2014年09月11日 11:19
  • 6055

Qt+VLC 实现的网络串流媒体播放

缘起 由于项目需要,监控相机需要在客户端显示,但是这个baslar相机BIP2-1300c-dn只支持网页查看,并没有传统工业相机一样的c++ demo。没办法,还需要这个功能,就自己写一个网络媒体...
  • liumlau
  • liumlau
  • 2016年06月22日 22:14
  • 3410

FFmpeg 视频播放器类,可复用

音频播放类可以点这里:音频播放类 原理介绍:视频播放类都是基于音频播放类改变过来的,加上了音频视频同步,基本做出了效果.h头文件#ifndef FFMPEGPLAYER_H #define FFMP...
  • what951006
  • what951006
  • 2016年12月22日 21:10
  • 604
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:网络播放器
举报原因:
原因补充:

(最多只允许输入30个字)