這裡以YP上的JOKV-FM(TEST)為例
當點擊YP上的一個頻道時,其訪問地址為peercast://pls/25838B9F1EAE27079B793C9FBA0E4156?
tip=222.148.187.176:7144
peercast://指的是peercast協議,由於peercast註冊了此協議,所以在IE中輸入這個地址時會自動啟動
peercast並把這個地址傳送給peercast
25838B9F1EAE27079B793C9FBA0E4156指的是廣播端的ChanID,每廣播一個電台peercast會根據相應演算法生
成一個ID,這個ID可以唯一標識一個頻道。
tip=222.148.187.176:7144表示廣播主機的地址和連接埠號碼。這樣的話本地的peercast會先去和這個主機建
立串連,然後根據這個主機去找8個轉播相同頻道的主機,選擇其中最好的一個作為傳輸者。
建立串連後,產生播放清單play.pls如下
[playlist]
NumberOfEntries=1
File1=http://localhost:7144/stream/25838B9F1EAE27079B793C9FBA0E4156.ogg
Title1=JOKV-FM(TEST)
Length1=-1
Version=2
然後調用播放器(例如winamp)去播放這個列表,這時winamp訪問的url地址為
http://localhost:7144/stream/74F7ECF0508E50E62FD3BEB0624921E4.ogg。即winamp通過http方式從
peercast獲得媒體資料並播放,訪問主機為本機localhost,連接埠為7144。
winamp發送的HTTP請求類似如下:
GET /stream/74F7ECF0508E50E62FD3BEB0624921E4.ogg HTTP/1.1
Host: localhost:7144
Peercast有一個servent監聽7144連接埠,並處理髮來的HTTP請求。
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());
}
}
下面主要分析Peercast如何把資料送往播放器
// 觸發頻道,調用processStream傳送媒體資料給播放器
void Servent::triggerChannel(char *str, ChanInfo::PROTOCOL proto,bool relay)
{
outputProtocol = proto;
processStream(false,info);
}
// outputProtocol為HTTP協議,調用sendRawChannel發送資料
void Servent::processStream(bool doneHandshake,ChanInfo &chanInfo)
{
if (outputProtocol == ChanInfo::SP_HTTP)
{
sendRawChannel(true,true);
}
}
// 發送頻道資料給播放器
void Servent::sendRawChannel(bool sendHead, bool sendData)
{
try
{
sock->setWriteTimeout(DIRECT_WRITE_TIMEOUT*1000);
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);
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);
}
}