PeerCast分析要点一(PeerCast和媒体播放器的通信原理)
来源:互联网 发布:js 二维数组赋值 编辑:程序博客网 时间:2024/05/23 01:18
PeerCast和媒体播放器的通信原理
这里分析的仅以MP3为例,PeerCast和播放器的数据通信是通过HTTP协议来实现
首先播放器通过向本地PeerCast发送http请求(注意媒体数据是从PeerCast本地的缓冲区内读取到播放器的,通过以下的请求地址实现)通过向PeerCast的主服务线程7144端口发送HTTP请求http://localhost:7144/stream/4DA260ACBCD97D2251E59CEC3A3F73D3.mp3
播放器发送的请求类似:
GET /stream/4DA260ACBCD97D2251E59CEC3A3F73D3.mp3 HTTP/1.1
Cache-Control: no-cache
Connection: Keep-Alive
Pragma: foFileURI.dlna.org
Accept: */*
User-Agent: NSPlayer/12 .00 .7601 .17514 WMFSDK/12.00 .7601 .17514
Icy-Metadata: 1
Accept-Encoding: gzip,deflate
Host: localhost:7144
紧接着PeerCast开启了7144的线程一直做监听工作,当接收到以上的HTTP请求时
PeerCast调用handshakeHTTP来处理该请求
void Servent::handshakeHTTP(HTTP &http, bool isHTTP)
{
char *in = http.cmdLine;
if (http.isRequest("GET /"))
{
char *fn = in+4;
if (strncmp(fn,"/stream/",8)==0)
triggerChannel(fn+8,ChanInfo::SP_HTTP,isPrivate());
}
}
过后便通过triggerChannel来触发频道,调用processStream传输媒体数据给播放器
void Servent::triggerChannel(char *str, ChanInfo::PROTOCOL proto,bool relay)
{
outputProtocol = proto;
processStream(false,info);
}
在processStream函数中,首先通过handshakeStream对播放器的Http请求进行处理
void Servent::processStream(bool doneHandshake,ChanInfo &chanInfo)
{
if (!doneHandshake)
{
setStatus(S_HANDSHAKE);
if (!handshakeStream(chanInfo))
return;
}
if (outputProtocol == ChanInfo::SP_HTTP)
{
if ((addMetadata) && (chanMgr->icyMetaInterval))
sendRawMetaChannel(chanMgr->icyMetaInterval);
else
sendRawChannel(true,true);
}
在handshakeStream函数中,从http的请求中读取相关数据,如若频道源还未找到,则在该方法中继续寻找频道源,同时发送以下的http回复给播放器,过后就是真正的数据传送了
回复播放器的请求
HTTP/1.1 200 OK
Server: nginx
Date: Mon, 22 Aug 2011 09:13:30 GMT
Content-Type: audio/mpeg
Content-Length: 4073817
Last-Modified: Fri, 08 Apr 2011 09:22:53 GMT
Connection: keep-Alive
Accept-Ranges: bytes
ID3---(表示该音乐的相关歌曲信息)
// outputProtocol为HTTP协议,调用sendRawChannel发送数据
void Servent::processStream(bool doneHandshake,ChanInfo &chanInfo)
{
if (outputProtocol == ChanInfo::SP_HTTP)
{
sendRawChannel(true,true);
}
用sendRawChannel来发送频道数据给播放器(PS:这一块详细分析如何用http来传送流媒体数据的过程)
void Servent::sendRawChannel(bool sendHead, bool sendData)
{
try
{
//设置direct直连方式的超时时间
sock->setWriteTimeout(DIRECT_WRITE_TIMEOUT*1000);
//在此对频道进行查找,确定频道是否已经被cut掉了
Channel *ch = chanMgr->findChannelByID(chanID);
if (!ch)
throw StreamException("Channel not found");
setStatus(S_CONNECTED);
//这里进行最重要的数据传输,请特别注意
LOG_DEBUG("Starting Raw stream of %s at %d",ch->info.name.cstr(),streamPos);
//说明几个重要的参数,
//streamPos:当前频道流所在缓冲的为止pos
//rawData为频道流缓冲区(专门用来存放数据包的地方)
//syncPos则是用来标志该媒体数据应当放在缓冲区当中的顺位,以数量为单位。用来保证节点间
//同步缓存位置的参数
if (sendHead)
{
ch->headPack.writeRaw(*sock);
streamPos = ch->headPack.pos + ch->headPack.len;
LOG_DEBUG("Sent %d bytes header ",ch->headPack.len);
}
if (sendData)
{
unsigned int streamIndex = ch->streamIndex;
unsigned int connectTime = sys->getTime();
unsigned int lastWriteTime = connectTime;
while ((thread.active) && sock->active())
{
ch = chanMgr->findChannelByID(chanID);
if (ch)
{
if (streamIndex != ch->streamIndex)
{
streamIndex = ch->streamIndex;
streamPos = ch->headPack.pos;
LOG_DEBUG("sendRaw got new stream index");
}
ChanPacket rawPack;
if (ch->rawData.findPacket(streamPos,rawPack))
{
if (syncPos != rawPack.sync)
LOG_ERROR("Send skip: %d",rawPack.sync-syncPos);
syncPos = rawPack.sync+1;
if ((rawPack.type == ChanPacket::T_DATA) || (rawPack.type==ChanPacket::T_HEAD))
{
rawPack.writeRaw(*sock);
lastWriteTime = sys->getTime();
}
if (rawPack.pos < streamPos)
LOG_DEBUG("raw: skip back %d",rawPack.pos-streamPos);
streamPos = rawPack.pos+rawPack.len;
}
}
if ((sys->getTime()-lastWriteTime) > DIRECT_WRITE_TIMEOUT)
throw TimeoutException();
sys->sleepIdle();
}
}
}catch(StreamException &e)
{
LOG_ERROR("Stream channel: %s",e.msg);
}
}
改进方案:底层传进来的sock可以改用RTP进行封装,通过RTP来传输流媒体给播放器,可以改进传输的数据,如何操作:
需要在底层创建一个WSARTPClientSocket,实现的功能类似WSAClientSocket,只是我们需要的是在WSARTPClientSocket下实现的是基于UDP的RTP套接字,改装底层套接字的实现,这样子当我们穿进来一个rtpSock时,rawPack.writeRaw(*rtpSock);将会执行调用rtp来发送数据的底层实现,这样子我们可以改进数据传输的效率,不过在播放器端需要改用RTP来进行接受,那么如何实现改用RTP传输时和播放器通信的接口呢:
首先,数据通信方式上,第一步还是采用播放器想PeerCast进行http请求,请求方式如上
第二,PeerCast同样对该请求进行http回复,紧接着,就开始了RTP数据的传送阶段
第三,我们需要知道原先sock套接字的创建在何处,哪里边kill掉,以及哪里执行了何种方法,只有这样子我们才能创建我们的rtpSock,为rtpSock的各种实现添加同样的操作代码。
第四,播放器要做的处理主要有两方面,首先要能够进行http请求,能够处理http回复的http包,其次要有能够接受rtp数据包的相关实现机制,这是播放器需要做的两种处理
PeerCast在启动时,就开启了7144线程在监听进来的连接,这个地方就是sock创建的时候,我们可以将sock里面的相关数据拷贝到rtpSock中
----------------------》ClientSocket *cs = sv->sock->accept(); //创建一个新的socket以接受连接
在kill方法中我们要做rtpSock的相应处理,在reset方法也要有相应的处理
- PeerCast分析要点一(PeerCast和媒体播放器的通信原理)
- Peercast的改进分析
- PeerCast.
- peercast
- 关于Peercast源代码的分析
- 关于Peercast源代码的分析
- 分析PeerCast的博客集
- Peercast简介、分析及常见问题处理 (一)
- Peercast的http.h源文件分析
- Peercast收听电台的源代码流程分析
- Peercast中“流”概念的分析
- Peercast整体架构分析
- PeerCast 分析报告
- Peercast播放模块分析
- Peercast源代码分析
- PeerCast 分析报告
- PeerCast 分析报告
- Peercast整体架构分析
- ArcEngine开发中Label无法显示小数点前0的问题解决方法
- JOJ 2190: Mondriaan's Dream (状态压缩DP +DFS)
- Ruby基本概念的解读
- 在美做开发多年,写给国内iPhone、Android开发新手
- Func和Action委托
- PeerCast分析要点一(PeerCast和媒体播放器的通信原理)
- poj之2981大整数相加
- .net 实现 URL重写,伪静态
- Action委托
- 0.VC(ui)-hook-Combox滚动条
- 最长公共子序列 LCS
- Java引用对象SoftReference WeakReference PhantomReference
- ABAP编程需要注意的小问题
- jquery随记(效果)----处理多组元素并发(不用特意处理 就是并发)