diff options
author | phintuka <phintuka> | 2007-01-07 06:20:35 +0000 |
---|---|---|
committer | phintuka <phintuka> | 2007-01-07 06:20:35 +0000 |
commit | 12d2753a27bb11505eaa95ef266f533b983f24f8 (patch) | |
tree | 96bec0506e07a3fb26a9d70d3f9cda840fb71a93 | |
parent | e506080907ba4f4ffa42e0027f163aedcf4a514b (diff) | |
download | xineliboutput-12d2753a27bb11505eaa95ef266f533b983f24f8.tar.gz xineliboutput-12d2753a27bb11505eaa95ef266f533b983f24f8.tar.bz2 |
Removed unused Xine_Sync
Fixes to polling
cBackgroundWriter replaced with dedicated classes
Reduced HTTP logging
Simple RTSP (RFC 2326) server implementation to switch multicasting on/off
-rw-r--r-- | frontend_svr.c | 337 |
1 files changed, 270 insertions, 67 deletions
diff --git a/frontend_svr.c b/frontend_svr.c index 4bcfa799..6e7bb121 100644 --- a/frontend_svr.c +++ b/frontend_svr.c @@ -4,7 +4,7 @@ * See the main source file 'xineliboutput.c' for copyright information and * how to reach the author. * - * $Id: frontend_svr.c,v 1.33 2007-01-01 08:50:53 phintuka Exp $ + * $Id: frontend_svr.c,v 1.34 2007-01-07 06:20:35 phintuka Exp $ * */ @@ -40,10 +40,13 @@ #include "tools/udp_pes_scheduler.h" #include "tools/http.h" #include "tools/vdrdiscovery.h" +#include "tools/sdp.h" #include "frontend_svr.h" #include "device.h" +//#define HTTP_OSD + #define LOG_OSD_BANDWIDTH (128*1024) /* log messages if OSD bandwidth > 1 Mbit/s */ typedef struct { @@ -56,17 +59,16 @@ class cReplyFuture : public cFuture<int>, public cListObject {}; class cGrabReplyFuture : public cFuture<grab_result_t>, public cListObject {}; class cCmdFutures : public cHash<cReplyFuture> {}; + //----------------------------- cXinelibServer -------------------------------- // (control) connection types enum { - ctNone = -1, - ctDetecting = 0, - ctControl, - ctHttp, - //ctHttpPlay, // client can't access file that is just played -> stream over http - ctRtsp, // TCP/RTSP + UDP/RTP - ctRtspMux // TCP: multiplexed RTSP control + RTP/RTCP data/control + ctDetecting = 0x00, + ctControl = 0x01, + ctHttp = 0x02, + ctRtsp = 0x04, // TCP/RTSP + UDP/RTP + ctRtspMux = 0x08 // TCP: multiplexed RTSP control + RTP/RTCP data/control }; // (data) connection types @@ -97,7 +99,7 @@ cXinelibServer::cXinelibServer(int listen_port) : m_bMulticast[i] = 0; m_bConfigOk[i] = false; m_bUdp[i] = 0; - m_ConnType[i] = ctNone; + m_ConnType[i] = ctDetecting; } m_Port = listen_port; @@ -283,7 +285,8 @@ void cXinelibServer::OsdCmd(void *cmd_gen) if(m_Writer[i]) { if(!spudata) spudata = dvdspu_encode(cmd, &spulen); - //m_Writer[i]->Queue(-1, spudata, spulen); + if(spudata) + m_Writer[i]->Put(-1, spudata, spulen); } } #endif @@ -317,7 +320,6 @@ void cXinelibServer::OsdCmd(void *cmd_gen) } } - int64_t cXinelibServer::GetSTC(void) { Lock(); @@ -355,14 +357,14 @@ int cXinelibServer::Play_PES(const uchar *data, int len) for(int i=0; i<MAXCLIENTS; i++) { if(fd_control[i] >= 0) { if((m_bConfigOk[i] && fd_data[i] >= 0) || - m_ConnType[i] == ctHttp || - m_ConnType[i] == ctRtsp) { + m_ConnType[i] & (ctHttp|ctRtsp)) { if(m_bUdp[i]) { UdpClients++; } else if(m_Writer[i]) { + int result = m_Writer[i]->Put(m_StreamPos, data, len); if(!result) { LOGMSG("cXinelibServer::Play_PES Write/Queue error (TCP/PIPE)"); @@ -406,57 +408,46 @@ void cXinelibServer::SetHDMode(bool On) #endif } -void cXinelibServer::Xine_Sync(void) -{ -#if 0 - TRACEF("cXinelibServer::Xine_Sync"); - - bool foundReceivers=false; - SyncLock.Lock(); - cXinelibThread::Xine_Control((const char *)"SYNC",m_StreamPos); - for(int i=0; i<MAXCLIENTS; i++) - if(fd_control[i] >= 0) { - wait_sync[i]=true; - foundReceivers=true; - } - Unlock(); - if(foundReceivers) { - TRACE("cXinelibServer::Xine_Sync --> WAIT..."); - SyncDone.Wait(SyncLock); - TRACE("cXinelibServer::Xine_Sync --> WAIT DONE."); - } - SyncLock.Unlock(); - Lock(); -#endif -} - - bool cXinelibServer::Poll(cPoller &Poller, int TimeoutMs) { // in live mode transponder clock is the master ... - if((*xc.local_frontend && strncmp(xc.local_frontend, "none", 4)) || m_bLiveMode) - return m_Scheduler->Poll(TimeoutMs, m_Master=false); + // in replay mode local frontend (if present) is master + if(m_bLiveMode || (*xc.local_frontend && strncmp(xc.local_frontend, "none", 4))) { + if(m_Scheduler->Clients()) + return m_Scheduler->Poll(TimeoutMs, m_Master=false); + return true; + } // replay mode: do { Lock(); m_Master = true; - int Free = 0xffff, Clients = 0, Udp = 0; + int Free = 0xffff, FreeHttp = 0xffff; + int Clients = 0, Http = 0, Udp = 0; for(int i=0; i<MAXCLIENTS; i++) { - if(fd_control[i]>=0 && m_bConfigOk[i]) { - if(fd_data[i]>=0 || m_bMulticast[i]) { + if(fd_control[i]>=0) { + if(m_bConfigOk[i]) { if(m_Writer[i]) Free = min(Free, m_Writer[i]->Free()); else if(m_bUdp[i]) Udp++; Clients++; + } else if(m_ConnType[i] & (ctHttp|ctRtspMux)) { + if(m_Writer[i]) { + FreeHttp = min(FreeHttp, m_Writer[i]->Free()); + Http++; + } } } } + if(m_iMulticastMask) { + Clients++; + Udp++; + } /* select master timing source for replay mode */ int master = -1; - if(Clients && !m_iMulticastMask && !Udp) { + if(Clients && !Udp) { for(int i=0; i<MAXCLIENTS; i++) if(fd_control[i]>=0 && m_bConfigOk[i] && m_Writer[i]) { master = i; @@ -467,24 +458,30 @@ bool cXinelibServer::Poll(cPoller &Poller, int TimeoutMs) if(m_MasterCli >= 0) Xine_Control("MASTER 0"); if(master >= 0) - write_cmd(fd_control[master], "MASTER 1\r\n"); + write_cmd(fd_control[master], "MASTER 1\r\n"); m_MasterCli = master; } Unlock(); - // replay is paused when no clients - if(!Clients && !xc.remote_rtp_always_on) { + if(!Clients && !Http) { + if(m_bLiveMode) + return true; + // replay is paused when no clients if(TimeoutMs>0) cCondWait::SleepMs(TimeoutMs); return false; } // in replay mode cUdpScheduler is master timing source - if(Free < 8128 || !m_Scheduler->Poll(TimeoutMs, true)) { + if(Free < 8128 || + !m_Scheduler->Poll(TimeoutMs, true) || + (!Clients && FreeHttp < 8128)) { + if(TimeoutMs > 0) cCondWait::SleepMs(min(TimeoutMs, 5)); TimeoutMs -= 5; + } else { return true; } @@ -813,7 +810,7 @@ uchar *cXinelibServer::GrabImage(int &Size, bool Jpeg, #define CREATE_NEW_WRITER \ if(m_Writer[cli]) \ delete m_Writer[cli]; \ - m_Writer[cli] = new cBackgroundWriter(fd); + m_Writer[cli] = new cTcpWriter(fd); void cXinelibServer::Handle_Control_PIPE(int cli, const char *arg) { @@ -1044,6 +1041,8 @@ void cXinelibServer::Handle_Control_CONFIG(int cli) xc.AutocropOptions()); ConfigurePostprocessing("pp", xc.ffmpeg_pp ? true : false, xc.FfmpegPpOptions()); + write_cmd(fd_control[cli], "CLEAR\r\n"); + #ifdef ENABLE_TEST_POSTPLUGINS ConfigurePostprocessing("headphone", xc.headphone ? true : false, NULL); #endif @@ -1137,8 +1136,7 @@ void cXinelibServer::Handle_Control_CONTROL(int cli, const char *arg) void cXinelibServer::Handle_Control_HTTP(int cli, const char *arg) { - LOGDBG("HTTP(%d): %s", cli, arg); - + // Parse request if(m_ConnType[cli] == ctDetecting || !m_State[cli]) { LOGDBG("HTTP request: %s", arg); @@ -1147,19 +1145,21 @@ void cXinelibServer::Handle_Control_HTTP(int cli, const char *arg) m_State[cli] = new cConnState; if( !m_State[cli]->SetCommand(arg) || + strncmp(m_State[cli]->Version(), "HTTP/1.", 7) || strcmp(m_State[cli]->Name(), "GET")) { LOGMSG("invalid HTTP request: %s", arg); CloseConnection(cli); return; } - m_ConnType[cli] = ctHttp; return; } + // Handle request else if(m_ConnType[cli] == ctHttp) { LOGDBG("HTTP(%d): %s", cli, arg); + // Collect headers if(*arg) { m_State[cli]->AddHeader(arg); return; @@ -1167,36 +1167,64 @@ void cXinelibServer::Handle_Control_HTTP(int cli, const char *arg) LOGMSG("HTTP Request complete"); - // primary device output + // + // primary device output (PES) + // if(!strcmp(m_State[cli]->Uri(), "/")) { LOGMSG("HTTP streaming primary device feed"); write_cmd(fd_control[cli], HTTP_REPLY_200_PRIMARY); -#if 1 + // pack header (scr 0, mux rate 0x6270) write(fd_control[cli], - "\x00\x00\x01\xba" "\x44\x00\x04\x00" - "\x04\x01\x01\x89" "\xc3\xf8", 14); -#endif -#if 1 - // system header - write(fd_control[cli], + "\x00\x00\x01\xba" + "\x44\x00\x04\x00" "\x04\x01\x01\x89" "\xc3\xf8", 14); + // system header (streams C0, E0, BD, BF) + write(fd_control[cli], "\x00\x00\x01\xbb" "\x00\x12" "\x80\xc4\xe1" "\x00\xe1" "\x7f" - "\xb9\xe0\xe8" "\xb8\xc0\x20" - "\xbd\xe0\x3a" "\xbf\xe0\x02", 24); -#endif - m_Writer[cli] = new cBackgroundWriter(fd_control[cli], KILOBYTE(1024), true); + "\xb9\xe0\xe8" "\xb8\xc0\x20" "\xbd\xe0\x3a" "\xbf\xe0\x02", 24); + + m_Writer[cli] = new cRawWriter(fd_control[cli], KILOBYTE(1024)); + + DELETENULL(m_State[cli]); + return; + } + +#if 0 + // + // primary device output (TS) + // + if(!strcmp(m_State[cli]->Uri(), "/TS")) { + LOGMSG("HTTP streaming primary device feed (TS)"); + write_cmd(fd_control[cli], HTTP_REPLY_200_PRIMARY_TS); + m_Writer[cli] = new cTsWriter(fd_control[cli], KILOBYTE(1024)); DELETENULL(m_State[cli]); return; } +#endif + +#if 0 /* for testing */ + else if(!strcmp(m_State[cli]->Uri(), "/test.avi")) { + LOGMSG("HTTP streaming test file"); + new cHttpStreamer(fd_control[cli], "/tmp/test.avi", + m_State[cli]->Header("Range")->Value()); + // detach socket + fd_control[cli] = -1; + CloseConnection(cli); + return; + } +#endif + // // currently playing media file + // else if(!strncmp(m_State[cli]->Uri(), "/PLAYFILE", 9)) { if( m_FileName && m_bPlayingFile) { LOGMSG("HTTP streaming media file"); - new cHttpStreamer(fd_control[cli], m_FileName, m_State[cli]->Header("Range")->Value()); + new cHttpStreamer(fd_control[cli], m_FileName, + m_State[cli]->Header("Range")->Value()); // detach socket fd_control[cli] = -1; CloseConnection(cli); @@ -1206,7 +1234,9 @@ void cXinelibServer::Handle_Control_HTTP(int cli, const char *arg) LOGDBG("No currently playing file"); } + // // nothing else will be served ... + // LOGMSG("Rejected HTTP request for \'%s\'", *m_State[cli]->Uri()); write_cmd(fd_control[cli], HTTP_REPLY_401); LOGDBG("HTTP Reply: HTTP/1.1 401 Unauthorized"); @@ -1214,8 +1244,183 @@ void cXinelibServer::Handle_Control_HTTP(int cli, const char *arg) } } +#define RTSP_200_OK "RTSP/1.0 200 OK\r\n" \ + "CSeq: %d\r\n" +#define RTSP_415 "RTSP/1.0 415 Unsupported media type\r\n" \ + "CSeq: %d\r\n" RTSP_FIN +#define RTSP_461 "RTSP/1.0 461 Unsupported transport\r\n" \ + "CSeq: %d\r\n" RTSP_FIN +#define RTSP_501 "RTSP/1.0 501 Not implemented\r\n" \ + "CSeq: %d\r\n" RTSP_FIN +#define RTSP_FIN "\r\n", CSeq + +#define RTSPOUT(x...) do { printf_cmd(fd_control[cli], x); LOGMSG("RTSP TX:" x); } while(0) +//#define RTSPOUT(x...) printf_cmd(fd_control[cli], x) + void cXinelibServer::Handle_Control_RTSP(int cli, const char *arg) { + // + // Minimal RTSP (RFC 2326) server implementation + // + + + // + // collect request and headers + // + if(m_ConnType[cli] == ctDetecting || !m_State[cli]) { + LOGDBG("RTSP request: %s", arg); + + DELETENULL(m_State[cli]); + m_State[cli] = new cConnState; + + if( !m_State[cli]->SetCommand(arg) || + strcmp(m_State[cli]->Version(), "RTSP/1.0")) { + LOGMSG("invalid RTSP request: %s", arg); + CloseConnection(cli); + return; + } + m_ConnType[cli] = ctRtsp; + return; + } + + // + // Process complete request + // + else if(m_ConnType[cli] == ctRtsp) { + LOGDBG("RTSP(%d): %s", cli, arg); + + if(*arg) { + m_State[cli]->AddHeader(arg); + return; + } + + cHeader *cseq = m_State[cli]->Header("CSeq"); + int CSeq = cseq ? cseq->IntValue() : -1; + LOGMSG("RTSP Request complete (cseq %d)", CSeq); + + // + // OPTIONS rtsp://127.0.0.1:37890 RTSP/1.0 + // CSeq: 1 + // + if(!strcmp(m_State[cli]->Name(), "OPTIONS")) { + RTSPOUT(RTSP_200_OK + "Public: DESCRIBE, SETUP, TEARDOWN, PLAY\r\n" + RTSP_FIN); + } // OPTIONS + + // + // DESCRIBE rtsp://127.0.0.1:37890 RTSP/1.0 + // CSeq: 2 + // Accept: application/sdp + // + else if(!strcmp(m_State[cli]->Name(), "DESCRIBE")) { + cHeader *accept = m_State[cli]->Header("Accept"); + if(accept && strstr(accept->Value(), "application/sdp")) { + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + char buf[64]; + getsockname(fd_control[cli], (struct sockaddr *)&sin, &len); + const char *sdp_descr = vdr_sdp_description(cxSocket::ip2txt(sin.sin_addr.s_addr, + sin.sin_port, buf), + 2001, + xc.listen_port, + xc.remote_rtp_addr, + /*m_ssrc*/0x4df73452, + xc.remote_rtp_port, + xc.remote_rtp_ttl); + int sdplen = strlen(sdp_descr); + RTSPOUT(RTSP_200_OK + "Content-Type: application/sdp\r\n" + "Content-Length: %d\r\n" + "\r\n", + CSeq, sdplen); + write_cmd(fd_control[cli], sdp_descr, sdplen); + } else { + RTSPOUT(RTSP_415 /*UNSUPPORTED_MEDIATYPE*/); + } + } // DESCRIBE + + // + // SETUP rtsp://127.0.0.1:37890/ RTSP/1.0 + // CSeq: 15 + // Transport: RTP/AVP;unicast;client_port=37890-37891 + // User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10) + // + else if(!strcmp(m_State[cli]->Name(), "SETUP")) { + cHeader *transport = m_State[cli]->Header("Transport"); + int urtp=0, mrtp=0, tcp=0; + if(transport && + (strstr(transport->Value(), "RTP/AVP;multicast") && (mrtp=1)) || + (strstr(transport->Value(), "RTP/AVP;unicast") && (urtp=1)) || + (strstr(transport->Value(), "RTP/AVP;interleaved") && (tcp=1))) { + //if(!mrtp) + // sprintf(buf, "RTSP/1.0 461 Unsupported transport\r\n" RTSP_H_CSEQ RTSP_OK_FIN); + //else + RTSPOUT(RTSP_200_OK + "Session: %u\r\n" + "Transport: RTP/AVP;multicast;destination=224.8.4.9;server_port=37890-37891\r\n" + RTSP_FIN, + cli); + } else { + RTSPOUT(RTSP_461 /*UNSUPPORTED_TRANSPORT*/ ); + } + } // SETUP + + // + // PLAY rtsp://127.0.0.1:37890 RTSP/1.0 + // CSeq: 13 + // Session: 0 + // Range: npt=0.000- + // User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10) + // + else if(!strcmp(m_State[cli]->Name(), "PLAY")) { + RTSPOUT(RTSP_200_OK + RTSP_FIN); + + if(!m_iMulticastMask && !xc.remote_rtp_always_on) + m_Scheduler->AddRtp(); + + m_bMulticast[cli] = true; + m_iMulticastMask |= (1<<cli); +#if 0 + //udp + int fd = sock_connect(fd_control[cli], atoi(arg), SOCK_DGRAM); + if(fd < 0) { + LOGERR("socket() for UDP failed"); + write_cmd(fd_control[cli], "UDP: Socked failed.\r\n"); + return; + } + m_bUdp[cli] = true; + fd_data[cli] = fd; + m_Scheduler->AddHandle(fd); +#endif + } // PLAY + + // + // TEARDOWN rtsp://127.0.0.1:37890 RTSP/1.0 + // CSeq: 39 + // Session: 1 + // User-Agent: VLC media player (LIVE555 Streaming Media v2005.11.10) + // + else if(!strcmp(m_State[cli]->Name(), "TEARDOWN")) { + RTSPOUT(RTSP_200_OK + RTSP_FIN); + CloseConnection(cli); + } // TEARDOWN + + // + // unknown/unsupported request + // + else { + LOGMSG("Unsupported RTSP request: %s", *m_State[cli]->Name()); + RTSPOUT(RTSP_501 /*NOT_IMPLEMENTED*/); + } // unsupported request + + + // dispose buffer + DELETENULL(m_State[cli]); + + } // ConnectionType == ctRtsp } void cXinelibServer::Handle_Control(int cli, const char *cmd) @@ -1417,8 +1622,6 @@ void cXinelibServer::Action(void) /* higher priority */ SetPriority(-1); - SetPriority(-2); - SetPriority(-3); sched_param temp; temp.sched_priority = 2; |