//based on libRTMP function bool rtmp_supplement::WriteN(RTMP * r, const char *buffer, int n) { const char *ptr = buffer; while (n > 0) { #ifdef _DEBUG //fwrite(ptr, 1, n, _netstackdump); #endif int nBytes = send(r->m_socket, ptr, n, 0); //Log(LOGDEBUG, "%s: %d\n", __FUNCTION__, nBytes); if (nBytes < 0) { int sockerr = GetSockError(); _log_ptr->Log(LOGERROR, "%s, RTMP send error %d (%d bytes)", __FUNCTION__, sockerr, n); if (sockerr == EINTR && !RTMP_ctrlC) continue; _rtmp_ptr->RTMP_Close(r); n = 1; break; } if (nBytes == 0) break; n -= nBytes; ptr += nBytes; } return n == 0; }
void Send( qsrAcceptor * pAcceptor, unsigned int indice ) { int errcode = 0; char * ptr; int offset; PSO_PRE_CONDITION( pAcceptor != NULL ); PSO_PRE_CONDITION( indice < FD_SETSIZE ); ptr = (char*) &pAcceptor->answer; offset = sizeof pAcceptor->answer - pAcceptor->dispatch[indice].dataToBeWritten; do { errcode = send( pAcceptor->dispatch[indice].socketId, &ptr[offset], pAcceptor->dispatch[indice].dataToBeWritten, MSG_NOSIGNAL ); } while ( errcode == -1 && errno == EINTR ); if ( errcode == -1 ) { qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "In function send(), error = %d", GetSockError() ); /* * The following attempt at recovering from a send() failure could * well be a bit problematic. We should probably check the exact error * that cause the problem and decide which cleanup is needed based * on that error... * Since the chance of an error is very small when using the loopback * network interface, the work on this will wait!!! * * \todo Make sure that the code for the cleanup of the sockets (when * in error) is appropriate for the specific error encountered. */ #if defined (WIN32) shutdown( pAcceptor->dispatch[indice].socketId, SD_BOTH ); closesocket( pAcceptor->dispatch[indice].socketId ); #else if ( errno != EPIPE ) { shutdown( pAcceptor->dispatch[indice].socketId, 2 ); close( pAcceptor->dispatch[indice].socketId ); } #endif pAcceptor->dispatch[indice].socketId = PSO_INVALID_SOCKET; qsrSendMessage( &pAcceptor->pQuasar->log, QSR_WARNING, "Connection terminated abnormally %s%d", "for process ", pAcceptor->dispatch[indice].pid ); return; } pAcceptor->dispatch[indice].dataToBeWritten -= errcode; return; }
void stopStreaming(STREAMING_SERVER * server) { assert(server); if (server->state != STREAMING_STOPPED) { if (server->state == STREAMING_IN_PROGRESS) { server->state = STREAMING_STOPPING; // wait for streaming threads to exit while (server->state != STREAMING_STOPPED) msleep(1); } if (closesocket(server->socket)) RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d", GetSockError()); server->state = STREAMING_STOPPED; } }
void processTCPrequest(STREAMING_SERVER * server, // server socket and state (our listening socket) int sockfd // client connection socket ) { char buf[512] = { 0 }; // answer buffer char header[2048] = { 0 }; // request header char *filename = NULL; // GET request: file name //512 not enuf char *buffer = NULL; // stream buffer char *ptr = NULL; // header pointer int len; size_t nRead = 0; RTMP rtmp = { 0 }; uint32_t dSeek = 0; // can be used to start from a later point in the stream // reset RTMP options to defaults specified upon invokation of streams RTMP_REQUEST req; char srvhead[] = "\r\nServer: HTTP-RTMP Stream Server " RTMPDUMP_VERSION "\r\n"; // timeout for http requests fd_set fds; struct timeval tv; char *status = "404 Not Found"; server->state = STREAMING_IN_PROGRESS; memcpy(&req, &defaultRTMPRequest, sizeof(RTMP_REQUEST)); memset(&tv, 0, sizeof(struct timeval)); tv.tv_sec = 5; // go through request lines //do { FD_ZERO(&fds); FD_SET(sockfd, &fds); if (select(sockfd + 1, &fds, NULL, NULL, &tv) <= 0) { RTMP_Log(RTMP_LOGERROR, "Request timeout/select failed, ignoring request"); goto quit; } else { nRead = recv(sockfd, header, 2047, 0); header[2047] = '\0'; RTMP_Log(RTMP_LOGDEBUG, "%s: header: %s", __FUNCTION__, header); if (strstr(header, "Range: bytes=") != 0) { // TODO check range starts from 0 and asking till the end. RTMP_LogPrintf("%s, Range request not supported\n", __FUNCTION__); len = sprintf(buf, "HTTP/1.0 416 Requested Range Not Satisfiable%s\r\n", srvhead); send(sockfd, buf, len, 0); goto quit; } if (strncmp(header, "GET", 3) == 0 && nRead > 4) { char *p = filename; filename = header + 4; // filter " HTTP/..." from end of request while (*p != '\0') { if (*p == ' ') { *p = '\0'; break; } p++; } } } //} while(!isHTTPRequestEOF(header, nRead)); // if we got a filename from the GET method if (filename != NULL) { RTMP_Log(RTMP_LOGDEBUG, "%s: Request header: %s", __FUNCTION__, filename); if (filename[0] == '/') { // if its not empty, is it /? ptr = filename + 1; // parse parameters if (*ptr == '?') { int len; ptr++; len = strlen(ptr); while (len >= 2) { // get position of the next '&' char *temp; char ich = *ptr; unsigned int nArgLen; char *arg; ptr++; if (*ptr != '=') goto filenotfound; // long parameters not (yet) supported ptr++; len -= 2; nArgLen = len; if ((temp = strstr(ptr, "&")) != 0) { nArgLen = temp - ptr; } arg = (char *) malloc((nArgLen + 1) * sizeof(char)); memcpy(arg, ptr, nArgLen * sizeof(char)); arg[nArgLen] = '\0'; //RTMP_Log(RTMP_LOGDEBUG, "%s: unescaping parameter: %s", __FUNCTION__, arg); http_unescape(arg); RTMP_Log(RTMP_LOGDEBUG, "%s: parameter: %c, arg: %s", __FUNCTION__, ich, arg); ptr += nArgLen + 1; len -= nArgLen + 1; if (!ParseOption(ich, arg, &req)) { status = "400 unknown option"; goto filenotfound; } } } } else { goto filenotfound; } } else { RTMP_LogPrintf("%s: No request header received/unsupported method\n", __FUNCTION__); } // do necessary checks right here to make sure the combined request of default values and GET parameters is correct if (!req.hostname.av_len) { RTMP_Log(RTMP_LOGERROR, "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname"); status = "400 Missing Hostname"; goto filenotfound; } if (req.playpath.av_len == 0) { RTMP_Log(RTMP_LOGERROR, "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath"); status = "400 Missing Playpath"; goto filenotfound;; } if (req.protocol == RTMP_PROTOCOL_UNDEFINED) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP"); req.protocol = RTMP_PROTOCOL_RTMP; } if (req.rtmpport == -1) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a port (--port) or rtmp url (-r), using default port"); req.rtmpport = 0; } if (req.rtmpport == 0) { if (req.protocol & RTMP_FEATURE_SSL) req.rtmpport = 443; else if (req.protocol & RTMP_FEATURE_HTTP) req.rtmpport = 80; else req.rtmpport = 1935; } if (req.tcUrl.av_len == 0) { char str[512] = { 0 }; req.tcUrl.av_len = snprintf(str, 511, "%s://%.*s:%d/%.*s", RTMPProtocolStringsLower[req.protocol], req.hostname.av_len, req.hostname.av_val, req.rtmpport, req.app.av_len, req.app.av_val); req.tcUrl.av_val = (char *) malloc(req.tcUrl.av_len + 1); strcpy(req.tcUrl.av_val, str); } if (req.swfVfy) { #ifdef CRYPTO if (RTMP_HashSWF(req.swfUrl.av_val, &req.swfSize, req.hash, req.swfAge) == 0) { req.swfHash.av_val = (char *)req.hash; req.swfHash.av_len = RTMP_SWF_HASHLEN; } #endif } // after validation of the http request send response header len = sprintf(buf, "HTTP/1.0 200 OK%sContent-Type: video/flv\r\n\r\n", srvhead); send(sockfd, buf, len, 0); // send the packets buffer = (char *) calloc(PACKET_SIZE, 1); // User defined seek offset if (req.dStartOffset > 0) { if (req.bLiveStream) RTMP_Log(RTMP_LOGWARNING, "Can't seek in a live stream, ignoring --seek option"); else dSeek += req.dStartOffset; } if (dSeek != 0) { RTMP_LogPrintf("Starting at TS: %d ms\n", dSeek); } RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", req.bufferTime); RTMP_Init(&rtmp); RTMP_SetBufferMS(&rtmp, req.bufferTime); RTMP_SetupStream(&rtmp, req.protocol, &req.hostname, req.rtmpport, &req.sockshost, &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, dSeek, req.dStopOffset, req.bLiveStream, req.timeout); /* backward compatibility, we always sent this as true before */ if (req.auth.av_len) rtmp.Link.lFlags |= RTMP_LF_AUTH; rtmp.Link.extras = req.extras; rtmp.Link.token = req.token; rtmp.m_read.timestamp = dSeek; RTMP_LogPrintf("Connecting ... port: %d, app: %s\n", req.rtmpport, req.app); if (!RTMP_Connect(&rtmp, NULL)) { RTMP_LogPrintf("%s, failed to connect!\n", __FUNCTION__); } else { unsigned long size = 0; double percent = 0; double duration = 0.0; int nWritten = 0; int nRead = 0; do { nRead = RTMP_Read(&rtmp, buffer, PACKET_SIZE); if (nRead > 0) { if ((nWritten = send(sockfd, buffer, nRead, 0)) < 0) { RTMP_Log(RTMP_LOGERROR, "%s, sending failed, error: %d", __FUNCTION__, GetSockError()); goto cleanup; // we are in STREAMING_IN_PROGRESS, so we'll go to STREAMING_ACCEPTING } size += nRead; //RTMP_LogPrintf("write %dbytes (%.1f KB)\n", nRead, nRead/1024.0); if (duration <= 0) // if duration unknown try to get it from the stream (onMetaData) duration = RTMP_GetDuration(&rtmp); if (duration > 0) { percent = ((double) (dSeek + rtmp.m_read.timestamp)) / (duration * 1000.0) * 100.0; percent = ((double) (int) (percent * 10.0)) / 10.0; RTMP_LogStatus("\r%.3f KB / %.2f sec (%.1f%%)", (double) size / 1024.0, (double) (rtmp.m_read.timestamp) / 1000.0, percent); } else { RTMP_LogStatus("\r%.3f KB / %.2f sec", (double) size / 1024.0, (double) (rtmp.m_read.timestamp) / 1000.0); } } #ifdef _DEBUG else { RTMP_Log(RTMP_LOGDEBUG, "zero read!"); } #endif } while (server->state == STREAMING_IN_PROGRESS && nRead > -1 && RTMP_IsConnected(&rtmp) && nWritten >= 0); } cleanup: RTMP_LogPrintf("Closing connection... "); RTMP_Close(&rtmp); RTMP_LogPrintf("done!\n\n"); quit: if (buffer) { free(buffer); buffer = NULL; } if (sockfd) closesocket(sockfd); if (server->state == STREAMING_IN_PROGRESS) server->state = STREAMING_ACCEPTING; return; filenotfound: RTMP_LogPrintf("%s, %s, %s\n", __FUNCTION__, status, filename); len = sprintf(buf, "HTTP/1.0 %s%s\r\n", status, srvhead); send(sockfd, buf, len, 0); goto quit; }
bool Accept( qsrAcceptor * pAcceptor ) { PSO_SOCKET newSock = PSO_INVALID_SOCKET; int errcode, i; #if defined (WIN32) unsigned long mode = 1; #endif #if ! defined (WIN32) do { #endif newSock = accept( pAcceptor->socketFD, NULL, 0 ); #if ! defined (WIN32) } while ( newSock == PSO_INVALID_SOCKET && errno == EINTR ); #endif if ( newSock == PSO_INVALID_SOCKET ) { errcode = GetSockError(); #if defined (WIN32) if ( errcode == WSAEWOULDBLOCK ) return true; #else if ( errcode == EWOULDBLOCK || errcode == EAGAIN ) return true; #endif // The error is more serious... qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "In function accept(), error = %d", GetSockError() ); return false; } /* * Set the new socket in non-blocking mode */ #if defined (WIN32) errcode = ioctlsocket( newSock, FIONBIO, &mode ); if ( errcode == SOCKET_ERROR ) { qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "In function ioctlsocket(), error = %d", GetSockError() ); return false; } #else errcode = fcntl( newSock, F_SETFL, O_NONBLOCK); if ( errcode < 0 ) { qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "In function fcntl(), error = %d", GetSockError() ); return false; } #endif for ( i = 1; i < FD_SETSIZE; ++i ) { if ( pAcceptor->dispatch[i].socketId == PSO_INVALID_SOCKET ) { pAcceptor->dispatch[i].socketId = newSock; break; } } return true; }
void qsrWaitForConnections( qsrAcceptor * pAcceptor ) { int errcode = 0; fd_set readSet, writeSet; int maxFD, fired; struct timeval timeout; unsigned int i; bool rc; PSO_PRE_CONDITION( pAcceptor != NULL ); /* * NOTE: since socket handles, on Windows, are not integers, we will * not used the handles as indices to the pAcceptor->dispatch array */ pAcceptor->dispatch[0].socketId = pAcceptor->socketFD; pAcceptor->dispatch[0].pid = 0; pAcceptor->dispatch[0].dataToBeWritten = false; for ( i = 1; i < FD_SETSIZE; ++i ) { pAcceptor->dispatch[i].socketId = PSO_INVALID_SOCKET; pAcceptor->dispatch[i].pid = -1; pAcceptor->dispatch[i].dataToBeWritten = false; } while ( true ) { int zzz = 0; if ( g_pQSR->controlWord & QSR_SHUTDOWN_REQUEST ) { break; } timeout.tv_sec = 1; timeout.tv_usec = 0; FD_ZERO( &readSet ); FD_ZERO( &writeSet ); maxFD = 0; for ( i = 0; i < FD_SETSIZE; ++i ) { if ( pAcceptor->dispatch[i].socketId != PSO_INVALID_SOCKET) { if ( pAcceptor->dispatch[i].dataToBeWritten == 0 ) { FD_SET( pAcceptor->dispatch[i].socketId, &readSet); zzz++; } else { FD_SET( pAcceptor->dispatch[i].socketId, &writeSet); zzz++; } #if ! defined (WIN32) if ( pAcceptor->dispatch[i].socketId+1 > maxFD ) { maxFD = pAcceptor->dispatch[i].socketId+1; } #endif } } do { fired = select( maxFD, &readSet, &writeSet, NULL, &timeout ); } while ( fired == -1 && errno == EINTR ); if ( fired == -1 ) { qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "In function select(), error = %d", GetSockError() ); errcode = -1; break; } if ( fired == 0 ) continue; /* * Start with the socket listening for new connection requests */ if ( FD_ISSET( pAcceptor->socketFD, &readSet ) ) { rc = Accept( pAcceptor ); PSO_POST_CONDITION( rc == true || rc == false ); if ( ! rc ) break; fired--; } if ( fired == 0 ) continue; /* * Process all open sockets */ for ( i = 1; i < FD_SETSIZE; ++i ) { if ( pAcceptor->dispatch[i].socketId != PSO_INVALID_SOCKET ) { if ( FD_ISSET( pAcceptor->dispatch[i].socketId, &writeSet ) ) { Send( pAcceptor, i ); fired--; } else if ( FD_ISSET( pAcceptor->dispatch[i].socketId, &readSet ) ) { Receive( pAcceptor, i ); fired--; } } if ( fired == 0 ) break; } } // Cleanup (close all sockets) for ( i = 0; i < FD_SETSIZE; ++i ) { if ( pAcceptor->dispatch[i].socketId != PSO_INVALID_SOCKET ) { #if defined (WIN32) shutdown( pAcceptor->dispatch[i].socketId, SD_BOTH ); closesocket( pAcceptor->dispatch[i].socketId ); #else shutdown( pAcceptor->dispatch[i].socketId, 2 ); close( pAcceptor->dispatch[i].socketId ); #endif } } pAcceptor->socketFD = PSO_INVALID_SOCKET; }
bool qsrPrepareConnection( qsrAcceptor * pAcceptor, qsrQuasar * pQuasar ) { int errcode = 0; int one = 1; unsigned short port; long int dummy = 0; #if defined (WIN32) WORD versionRequested; WSADATA wsaData; unsigned long mode = 1; #endif struct sockaddr_in addr; PSO_PRE_CONDITION( pAcceptor != NULL ); PSO_PRE_CONDITION( pQuasar != NULL ); pAcceptor->pQuasar = pQuasar; memset( &pAcceptor->answer, 0, sizeof pAcceptor->answer ); strcpy( pAcceptor->answer.pathname, pAcceptor->pQuasar->params.memLocation ); pAcceptor->answer.memorySizekb = pAcceptor->pQuasar->params.memorySizekb; dummy = strtol( pAcceptor->pQuasar->params.qsrAddress, NULL, 10 ); if ( dummy <= 0 || dummy > 65535 ) { qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "Error getting port number" ); return false; } port = (unsigned short) dummy; #if defined (WIN32) versionRequested = MAKEWORD( 2, 2 ); errcode = WSAStartup( versionRequested, &wsaData ); if ( errcode != 0 ) { qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "In function WSAStartup(), error = %d", errcode ); return false; } pAcceptor->cleanupNeeded = true; #endif pAcceptor->socketFD = socket( PF_INET, SOCK_STREAM, 0 ); if ( pAcceptor->socketFD == PSO_INVALID_SOCKET ) { qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "In function socket(), error = %d", GetSockError() ); return false; } errcode = setsockopt( pAcceptor->socketFD, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof (one) ); if ( errcode != 0 ) { qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "In function setsockopt(), error = %d", GetSockError() ); return false; } // Set the socket in non-blocking mode. #if defined (WIN32) errcode = ioctlsocket( pAcceptor->socketFD, FIONBIO, &mode ); if ( errcode == SOCKET_ERROR ) { qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "In function ioctlsocket(), error = %d", GetSockError() ); return false; } #else errcode = fcntl( pAcceptor->socketFD, F_SETFL, O_NONBLOCK); if ( errcode < 0 ) { qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "In function fcntl(), error = %d", GetSockError() ); return false; } #endif memset( &addr, 0, sizeof(struct sockaddr_in) ); addr.sin_family = PF_INET; addr.sin_port = htons( port ); addr.sin_addr.s_addr = inet_addr( "127.0.0.1" ); errcode = bind( pAcceptor->socketFD, (struct sockaddr *) &addr, sizeof(struct sockaddr_in) ); if ( errcode != 0 ) { qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "In function bind(), error = %d", GetSockError() ); return false; } errcode = listen( pAcceptor->socketFD, 5 ); if ( errcode != 0 ) { qsrSendMessage( &pAcceptor->pQuasar->log, QSR_ERROR, "In function listen(), error = %d", GetSockError() ); return false; } return true; }