📄 channel.cpp
字号:
// ------------------------------------------------// File : channel.cpp// Date: 4-apr-2002// Author: giles// Desc: // Channel streaming classes. These do the actual // streaming of media between clients. //// (c) 2002 peercast.org// // ------------------------------------------------// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation; either version 2 of the License, or// (at your option) any later version.// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.// ------------------------------------------------#include <string.h>#include <stdlib.h>#include "common.h"#include "socket.h"#include "channel.h"#include "gnutella.h"#include "servent.h"#include "servmgr.h"#include "sys.h"#include "xml.h"#include "http.h"#include "peercast.h"#include "asf.h"// -----------------------------------char *Channel::srcTypes[]={ "NONE", "PEERCAST", "SHOUTCAST", "ICECAST", "URL"};// -----------------------------------char *Channel::statusMsgs[]={ "NONE", "WAIT", "CONNECT", "REQUEST", "CLOSE", "RECEIVE", "BROADCAST", "ABORT", "SEARCH", "NOHOSTS", "IDLE", "ERROR"};// -----------------------------------void readXMLString(String &str, XML::Node *n, const char *arg){ char *p; p = n->findAttr(arg); if (p) { str.set(p,String::T_HTML); str.convertTo(String::T_ASCII); }}// -----------------------------------------------------------------------------// Initialise the channel to its default settings of unallocated and reset.// -----------------------------------------------------------------------------void Channel::init(){ reset();}// -----------------------------------------------------------------------------// Close this channel and stop thread// -----------------------------------------------------------------------------void Channel::close(){ thread.active = false; setStatus(S_CLOSING);}// -----------------------------------------------------------------------------void Channel::endThread(){ close(); thread.unlock(); init();}// -----------------------------------------------------------------------------void Channel::setStatus(STATUS s){ status = s; if (isPlaying()) { info.status = ChanInfo::S_PLAY; info.lastPlay = sys->getTime(); }else{ info.status = ChanInfo::S_UNKNOWN; info.lastPlay = 0; }} // -----------------------------------------------------------------------------// Reset channel and make it available // -----------------------------------------------------------------------------void Channel::reset(){ currSource.init(); srcType = SRC_NONE; lastIdleTime = 0; prefetchCnt=0; numRelays = 0; numListeners = 0; info.init(); index = 0; mount.clear(); bump = false; stayConnected = false; icyMetaInterval = 0; syncPos = 0; headMeta.init(); insertMeta.init(); chanData.init(); setStatus(S_NONE); type = T_NONE; readDelay = false; sock = NULL; sourceURL.clear();}// -----------------------------------bool Channel::checkIdle(){ if ((numListeners > 0) || (stayConnected || (status == S_BROADCASTING))) { prefetchCnt=0; return false; } if (prefetchCnt) prefetchCnt--; if (prefetchCnt) LOG_CHANNEL("prefetch %d",prefetchCnt); return prefetchCnt==0;}// -----------------------------------bool Channel::isFull(){ return chanMgr->maxStreamsPerChannel ? numRelays >= chanMgr->maxStreamsPerChannel : false;}// -----------------------------------void Channel::startMP3File(char *fn){ type = T_BROADCAST; FileStream *fs = new FileStream(); fs->openReadOnly(fn); input = fs; thread.data = this; thread.func = streamMP3File; if (!sys->startThread(&thread)) init();}// -----------------------------------int Channel::streamMP3File(ThreadInfo *thread){ thread->lock(); Channel *ch = (Channel *)thread->data; LOG_CHANNEL("Channel started: %s",ch->getName()); try { while (thread->active) { ch->input->read(&ch->mp3Head,sizeof(MP3Header)); ch->readMP3(); ch->input->rewind(); LOG_CHANNEL("%s end",ch->getName()); } }catch(StreamException &e) { LOG_ERROR("Unable to read file: %s",e.msg); } ch->input->close(); delete ch->input; LOG_CHANNEL("Channel stopped: %s",ch->getName()); ch->endThread(); return 0;}// -----------------------------------void Channel::startGet(){ srcType = SRC_PEERCAST; type = T_RELAY; input = NULL; info.srcProtocol = ChanInfo::SP_PEERCAST; thread.data = this; thread.func = streamGet; if (!sys->startThread(&thread)) init();}// -----------------------------------void Channel::startURL(const char *u){ sourceURL.set(u); srcType = SRC_URL; type = T_BROADCAST; stayConnected = true; // source type should be set before here. //info.srcType = ChanInfo::T_UNKNOWN; thread.data = this; thread.func = streamURL; if (!sys->startThread(&thread)) init();}// -----------------------------------int Channel::findProc(ThreadInfo *thread){ thread->lock(); Channel *ch = (Channel *)thread->data; ch->setStatus(S_SEARCHING); int findCnt=0; while (thread->active) { ChanHitList *chl = chanMgr->findHitListByID(ch->info.id); if (chl && chl->numHits()) { // update chaninfo with latest ch->info = chl->info; ch->setStatus(S_IDLE); thread->unlock(); ch->startGet(); return 0; }else { if ((findCnt%60) == 0) servMgr->findChannel(ch->info); if (findCnt++ > 300) // give up eventually break; } sys->sleep(1000); } ch->endThread(); return 0;}// -----------------------------------String Channel::streamURL(const char *url){ String nextURL; String urlTmp; urlTmp.set(url); char *fileName = urlTmp.cstr(); Stream *file = NULL; PlayList *pls=NULL; LOG_CHANNEL("Fetch URL=%s",fileName); try { // get the source protocol if (strnicmp(fileName,"http://",7)==0) { info.srcProtocol = ChanInfo::SP_HTTP; fileName += 7; } else if (strnicmp(fileName,"mms://",6)==0) { info.srcProtocol = ChanInfo::SP_MMS; fileName += 6; } else if (strnicmp(fileName,"file://",7)==0) { info.srcProtocol = ChanInfo::SP_FILE; fileName += 7; } else { info.srcProtocol = ChanInfo::SP_FILE; } if ((info.srcProtocol == ChanInfo::SP_HTTP) || (info.srcProtocol == ChanInfo::SP_MMS)) { if ((info.contentType == ChanInfo::T_WMA) || (info.contentType == ChanInfo::T_WMV)) info.srcProtocol = ChanInfo::SP_MMS; LOG_CHANNEL("Channel source is HTTP"); ClientSocket *sock = sys->createSocket(); if (!sock) throw StreamException("Ch.%d cannot create socket",index); file = sock; char *dir = strstr(fileName,"/"); if (dir) *dir++=0; LOG_CHANNEL("Fetch Host=%s",fileName); if (dir) LOG_CHANNEL("Fetch Dir=%s",dir); setStatus(S_CONNECTING); Host host; host.fromStrName(fileName,80); sock->open(host); sock->connect(); HTTP http(*sock); http.writeLine("GET /%s HTTP/1.1",dir?dir:""); http.writeLine("%s %s",HTTP_HS_HOST,fileName); http.writeLine("%s %s",HTTP_HS_CONNECTION,"close"); http.writeLine("%s %s",HTTP_HS_ACCEPT,"*/*"); if (info.srcProtocol == ChanInfo::SP_MMS) { http.writeLine("%s %s",HTTP_HS_AGENT,"NSPlayer/4.1.0.3856"); http.writeLine("Pragma: no-cache,rate=1.000000,request-context=2"); http.writeLine("Pragma: xPlayStrm=1"); //http.writeLine("Pragma: stream-switch-count=1"); //http.writeLine("Pragma: stream-switch-entry=ffff:1:0"); }else { http.writeLine("%s %s",HTTP_HS_AGENT,PCX_AGENT); http.writeLine("icy-metadata:1"); } http.writeLine(""); int res = http.readResponse(); if ((res!=200) && (res!=302)) { LOG_ERROR("HTTP response: %s",http.cmdLine); throw StreamException("Bad HTTP connect"); } String name = info.name; while (http.nextHeader()) { LOG_CHANNEL("Fetch HTTP: %s",http.cmdLine); ChanInfo tmpInfo = info; Servent::readICYHeader(http,info,NULL); if (!tmpInfo.name.isEmpty()) info.name = tmpInfo.name; if (!tmpInfo.genre.isEmpty()) info.genre = tmpInfo.genre; if (!tmpInfo.url.isEmpty()) info.url = tmpInfo.url; if (http.isHeader("icy-metaint")) icyMetaInterval = http.getArgInt(); else if (http.isHeader("Location:")) nextURL.set(http.getArgStr()); char *arg = http.getArgStr(); if (arg) { if (http.isHeader("content-type")) { if (stristr(arg,MIME_XSCPLS)) pls = new PlayList(PlayList::T_SCPLS, 1000); else if (stristr(arg,MIME_PLS)) pls = new PlayList(PlayList::T_PLS, 1000); else if (stristr(arg,MIME_XPLS)) pls = new PlayList(PlayList::T_PLS, 1000); else if (stristr(arg,MIME_M3U)) pls = new PlayList(PlayList::T_PLS, 1000); else if (stristr(arg,MIME_TEXT)) pls = new PlayList(PlayList::T_PLS, 1000); else if (stristr(arg,MIME_ASX)) pls = new PlayList(PlayList::T_ASX, 1000); else if (stristr(arg,MIME_MMS)) info.srcProtocol = ChanInfo::SP_MMS; } } } if ((!nextURL.isEmpty()) && (res==302)) { LOG_CHANNEL("Ch.%d redirect: %s",index,nextURL.cstr()); sock->close(); delete sock; sock = NULL; return nextURL; } }else if (info.srcProtocol == ChanInfo::SP_FILE) { LOG_CHANNEL("Channel source is FILE"); FileStream *fs = new FileStream(); fs->openReadOnly(fileName); file = fs; ChanInfo::TYPE fileType = ChanInfo::T_UNKNOWN; // if filetype is unknown, try and figure it out from file extension. //if ((info.srcType == ChanInfo::T_UNKNOWN) || (info.srcType == ChanInfo::T_PLAYLIST)) { const char *ext = fileName+strlen(fileName); while (*--ext) if (*ext=='.') { ext++; break; } fileType = ChanInfo::getTypeFromStr(ext); } if (info.bitrate) readDelay = true; if (fileType == ChanInfo::T_PLS) pls = new PlayList(PlayList::T_PLS, 1000); else if (fileType == ChanInfo::T_ASX) pls = new PlayList(PlayList::T_ASX, 1000); else info.contentType = fileType; }else { throw StreamException("Unsupported URL"); } if (pls) { LOG_CHANNEL("Ch.%d is Playlist",index); pls->read(*file); file->close(); delete file; file = NULL; int urlNum=0; String url; LOG_CHANNEL("Playlist: %d URLs",pls->numURLs); while ((thread.active) && (pls->numURLs)) { if (url.isEmpty()) { url = pls->urls[urlNum%pls->numURLs]; urlNum++; } try { url = streamURL(url.cstr()); }catch(StreamException &) {} } delete pls; }else { // if we didn`t get a channel id from the source, then create our own (its an original broadcast) if (!info.id.isSet()) { info.id = chanMgr->broadcastID; info.id.encode(&servMgr->serverHost,info.name.cstr(),NULL,info.bitrate); } input = file; setStatus(S_BROADCASTING); readStream(); file->close(); } }catch(StreamException &e) { setStatus(S_ERROR); LOG_ERROR("Ch.%d error: %s",index,e.msg); sys->sleep(1000); } setStatus(S_CLOSING); if (file) { file->close(); delete file; } return nextURL;}// -----------------------------------void Channel::checkReadDelay(unsigned int len){ if (readDelay) { unsigned int time = (len*1000)/((info.bitrate*1024)/8); //LOG_CHANNEL("sleeping for %d\n",time); sys->sleep(time); }}// -----------------------------------int Channel::streamURL(ThreadInfo *thread){ thread->lock(); Channel *ch = (Channel *)thread->data; ClientSocket *sock = NULL; LOG_CHANNEL("Ch.%d started: %s",ch->index,ch->sourceURL.cstr()); String url; while (thread->active) { if (url.isEmpty()) url = ch->sourceURL; url = ch->streamURL(url.cstr()); } ch->endThread(); return 0;} // -----------------------------------int Channel::streamGet(ThreadInfo *thread){ thread->lock(); GnuPacket pack; Channel *ch = (Channel *)thread->data; chanMgr->lockHitList(ch->info.id,true); LOG_CHANNEL("Ch.%d started: %s",ch->index,ch->getName()); while (thread->active) { ch->lastIdleTime = sys->getTime(); ch->setStatus(S_IDLE); while ((ch->checkIdle()) && (thread->active)) sys->sleepIdle(); if (!thread->active) break; //ch->info.title.set("Please wait.",String::T_ASCII); bool doneSearch=false; do { Host sh = servMgr->serverHost; ChanHitList *chl = chanMgr->findHitListByID(ch->info.id); if (chl) { if (servMgr->getFirewall() == ServMgr::FW_OFF) { // we are non-firewalled so try non push hosts, then push hosts ch->currSource = chl->getHit(false); if (!ch->currSource.host.ip) ch->currSource = chl->getHit(true); }else{ // we are firewalled so try non push hosts only ch->currSource = chl->getHit(false); } } if (sh.isSame(ch->currSource.host)) ch->currSource.host.ip = 0; if (!ch->currSource.host.ip) { ch->setStatus(S_SEARCHING); if (!doneSearch) { LOG_CHANNEL("Ch.%d search..",ch->index); if (servMgr->findChannel(ch->info)) doneSearch = true; } sys->sleepIdle(); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -