Example #1
0
RTMPPublisher::~RTMPPublisher()
{
    //OSDebugOut (TEXT("*** ~RTMPPublisher (%d queued, %d buffered)\n"), queuedPackets.Num(), bufferedPackets.Num());
    bStopping = true;

    //we're in the middle of connecting! wait for that to happen to avoid all manner of race conditions
    if (hConnectionThread)
    {
        WaitForSingleObject(hConnectionThread, INFINITE);
        OSCloseThread(hConnectionThread);
    }

    FlushBufferedPackets ();

    //OSDebugOut (TEXT("%d queued after flush\n"), queuedPackets.Num());

    if(hSendThread)
    {
        //this marks the thread to exit after current work is done
        SetEvent(hSendLoopExit);

        //this wakes up the thread
        ReleaseSemaphore(hSendSempahore, 1, NULL);

        //wait 60 sec for it to exit
        OSTerminateThread(hSendThread, 60000);
    }

    if(hSendSempahore)
        CloseHandle(hSendSempahore);

    //OSDebugOut (TEXT("*** ~RTMPPublisher hSendThread terminated (%d queued, %d buffered, %d data)\n"), queuedPackets.Num(), bufferedPackets.Num(), curDataBufferLen);

    if (hSocketThread)
    {
        //mark the socket loop to shut down after the buffer is empty
        SetEvent(hSocketLoopExit);

        //wait 60 sec for it to exit
        OSTerminateThread(hSocketThread, 60000);
    }

    //OSDebugOut (TEXT("*** ~RTMPPublisher hSocketThread terminated (%d queued, %d buffered, %d data)\n"), queuedPackets.Num(), bufferedPackets.Num(), curDataBufferLen);

    if(rtmp)
    {
        //at this point nothing should be in the buffer, flush out what remains and make it blocking
        FlushDataBuffer();

        //disable the buffered send, so RTMP_Close writes directly to the net
        rtmp->m_bCustomSend = 0;

        //ideally we need some kind of delay here, since we just dumped several seconds worth of timestamps to the network
        //at once, and Twitch at shows the offline screen as soon as the connection is severed even if there are
        //pending video frames.

        if (RTMP_IsConnected(rtmp))
            Sleep (500);    //for now
        RTMP_Close(rtmp);
    }

    if(hDataMutex)
        OSCloseMutex(hDataMutex);

    while (bufferedPackets.Num())
    {
        //this should not happen any more...
        bufferedPackets[0].data.Clear();
        bufferedPackets.Remove(0);
    }

    if (dataBuffer)
        Free(dataBuffer);

    if (hDataBufferMutex)
        OSCloseMutex(hDataBufferMutex);

    if (hBufferEvent)
        CloseHandle(hBufferEvent);

    if (hSendLoopExit)
        CloseHandle(hSendLoopExit);

    if (hSocketLoopExit)
        CloseHandle(hSocketLoopExit);

    if (hSendBacklogEvent)
        CloseHandle(hSendBacklogEvent);

    if (hBufferSpaceAvailableEvent)
        CloseHandle(hBufferSpaceAvailableEvent);

    if (hWriteEvent)
        CloseHandle(hWriteEvent);

    if(rtmp)
        RTMP_Free(rtmp);

    //--------------------------

    for(UINT i=0; i<queuedPackets.Num(); i++)
        queuedPackets[i].data.Clear();
    queuedPackets.Clear();

    double dBFrameDropPercentage = double(numBFramesDumped)/NumTotalVideoFrames()*100.0;
    double dPFrameDropPercentage = double(numPFramesDumped)/NumTotalVideoFrames()*100.0;

    Log(TEXT("Number of times waited to send: %d, Waited for a total of %d bytes"), totalTimesWaited, totalBytesWaited);

    Log(TEXT("Number of b-frames dropped: %u (%0.2g%%), Number of p-frames dropped: %u (%0.2g%%), Total %u (%0.2g%%)"),
        numBFramesDumped, dBFrameDropPercentage,
        numPFramesDumped, dPFrameDropPercentage,
        numBFramesDumped+numPFramesDumped, dBFrameDropPercentage+dPFrameDropPercentage);

    /*if(totalCalls)
        Log(TEXT("average send time: %u"), totalTime/totalCalls);*/

    strRTMPErrors.Clear();

    //--------------------------
}
Example #2
0
DWORD WINAPI RTMPPublisher::CreateConnectionThread(RTMPPublisher *publisher)
{
    //------------------------------------------------------
    // set up URL

    bool bRetry = false;
    bool bSuccess = false;
    bool bCanRetry = false;

    String failReason;
    String strBindIP;

    int    serviceID    = AppConfig->GetInt   (TEXT("Publish"), TEXT("Service"));
    String strURL       = AppConfig->GetString(TEXT("Publish"), TEXT("URL"));
    String strPlayPath  = AppConfig->GetString(TEXT("Publish"), TEXT("PlayPath"));

    strURL.KillSpaces();
    strPlayPath.KillSpaces();

    LPSTR lpAnsiURL = NULL, lpAnsiPlaypath = NULL;
    RTMP *rtmp = NULL;

    //--------------------------------
    // unbelievably disgusting hack for elgato devices

    String strOldDirectory;
    UINT dirSize = GetCurrentDirectory(0, 0);
    strOldDirectory.SetLength(dirSize);
    GetCurrentDirectory(dirSize, strOldDirectory.Array());

    OSSetCurrentDirectory(API->GetAppPath());

    //--------------------------------

    if(!strURL.IsValid())
    {
        failReason = TEXT("No server specified to connect to");
        goto end;
    }

    if(serviceID != 0)
    {
        XConfig serverData;
        if(!serverData.Open(TEXT("services.xconfig")))
        {
            failReason = TEXT("Could not open services.xconfig");
            goto end;
        }

        XElement *services = serverData.GetElement(TEXT("services"));
        if(!services)
        {
            failReason = TEXT("Could not any services in services.xconfig");
            goto end;
        }

        XElement *service = NULL;
        DWORD numServices = services->NumElements();
        for(UINT i=0; i<numServices; i++)
        {
            XElement *curService = services->GetElementByID(i);
            if(curService->GetInt(TEXT("id")) == serviceID)
            {
                service = curService;
                break;
            }
        }

        if(!service)
        {
            failReason = TEXT("Could not find the service specified in services.xconfig");
            goto end;
        }

        XElement *servers = service->GetElement(TEXT("servers"));
        if(!servers)
        {
            failReason = TEXT("Could not find any servers for the service specified in services.xconfig");
            goto end;
        }

        XDataItem *item = servers->GetDataItem(strURL);
        if(!item)
            item = servers->GetDataItemByID(0);

        strURL = item->GetData();

        Log(TEXT("Using RTMP service: %s"), service->GetName());
        Log(TEXT("  Server selection: %s"), strURL.Array());
    }

    //------------------------------------------------------
    // now back to the elgato directory if it needs the directory changed still to function *sigh*

    OSSetCurrentDirectory(strOldDirectory);

    //------------------------------------------------------

    rtmp = RTMP_Alloc();
    RTMP_Init(rtmp);

    RTMP_LogSetCallback(librtmpErrorCallback);

    //RTMP_LogSetLevel(RTMP_LOGERROR);

    lpAnsiURL = strURL.CreateUTF8String();
    lpAnsiPlaypath = strPlayPath.CreateUTF8String();

    if(!RTMP_SetupURL2(rtmp, lpAnsiURL, lpAnsiPlaypath))
    {
        failReason = Str("Connection.CouldNotParseURL");
        goto end;
    }

    RTMP_EnableWrite(rtmp); //set it to publish

    /*rtmp->Link.swfUrl.av_len = rtmp->Link.tcUrl.av_len;
    rtmp->Link.swfUrl.av_val = rtmp->Link.tcUrl.av_val;
    rtmp->Link.pageUrl.av_len = rtmp->Link.tcUrl.av_len;
    rtmp->Link.pageUrl.av_val = rtmp->Link.tcUrl.av_val;*/
    rtmp->Link.flashVer.av_val = "FMLE/3.0 (compatible; FMSc/1.0)";
    rtmp->Link.flashVer.av_len = (int)strlen(rtmp->Link.flashVer.av_val);

    //-----------------------------------------

    UINT tcpBufferSize = AppConfig->GetInt(TEXT("Publish"), TEXT("TCPBufferSize"), 64*1024);

    if(tcpBufferSize < 8192)
        tcpBufferSize = 8192;
    else if(tcpBufferSize > 1024*1024)
        tcpBufferSize = 1024*1024;

    rtmp->m_outChunkSize = 4096;//RTMP_DEFAULT_CHUNKSIZE;//
    rtmp->m_bSendChunkSizeInfo = TRUE;

    rtmp->m_bUseNagle = TRUE;

    strBindIP = AppConfig->GetString(TEXT("Publish"), TEXT("BindToIP"), TEXT("Default"));
    if (scmp(strBindIP, TEXT("Default")))
    {
        rtmp->m_bindIP.addr.sin_family = AF_INET;
        rtmp->m_bindIP.addrLen = sizeof(rtmp->m_bindIP.addr);
        if (WSAStringToAddress(strBindIP.Array(), AF_INET, NULL, (LPSOCKADDR)&rtmp->m_bindIP.addr, &rtmp->m_bindIP.addrLen) == SOCKET_ERROR)
        {
            // no localization since this should rarely/never happen
            failReason = TEXT("WSAStringToAddress: Could not parse address");
            goto end;
        }
    }

    LogInterfaceType(rtmp);

    //-----------------------------------------

    if(!RTMP_Connect(rtmp, NULL))
    {
        failReason = Str("Connection.CouldNotConnect");
        failReason << TEXT("\r\n\r\n") << RTMPPublisher::GetRTMPErrors();
        bCanRetry = true;
        goto end;
    }

    if(!RTMP_ConnectStream(rtmp, 0))
    {
        failReason = Str("Connection.InvalidStream");
        failReason << TEXT("\r\n\r\n") << RTMPPublisher::GetRTMPErrors();
        bCanRetry = true;
        goto end;
    }

    //-----------------------------------------

    OSDebugOut(TEXT("Connected: %u\r\n"), OSGetTime());

    publisher->RequestKeyframe(1000);

    //-----------------------------------------

    bSuccess = true;

end:

    if (lpAnsiURL)
        Free(lpAnsiURL);

    if (lpAnsiPlaypath)
        Free(lpAnsiPlaypath);

    if(!bSuccess)
    {
        if(rtmp)
        {
            RTMP_Close(rtmp);
            RTMP_Free(rtmp);
        }

        if(failReason.IsValid())
            App->SetStreamReport(failReason);

        if(!publisher->bStopping)
            PostMessage(hwndMain, OBS_REQUESTSTOP, bCanRetry ? 0 : 1, 0);

        Log(TEXT("Connection to %s failed: %s"), strURL.Array(), failReason.Array());

        publisher->bStopping = true;
    }
    else
    {
        publisher->Init(rtmp, tcpBufferSize);
        publisher->bConnected = true;
        publisher->bConnecting = false;
    }

    return 0;
}
Example #3
0
static event_t *
rtmp_loop(rtmp_t *r, media_pipe_t *mp, char *url, char *errbuf, size_t errlen)
{
  RTMPPacket p = {0};
  int pos = -1, ret;
  uint32_t dts;
  event_t *e = NULL;

  r->mp = mp;
  r->hold = 0;
  r->lost_focus = 0;
  r->epoch = 1;
  r->seekbase = AV_NOPTS_VALUE;
  r->seekpos = AV_NOPTS_VALUE;
  r->subpts = AV_NOPTS_VALUE;
  r->lastsubpts = AV_NOPTS_VALUE;

  mp->mp_video.mq_seektarget = AV_NOPTS_VALUE;
  mp->mp_audio.mq_seektarget = AV_NOPTS_VALUE;
  mp_set_playstatus_by_hold(mp, 0, NULL);

  while(1) {


    if(pos == -1) {

      ret = RTMP_GetNextMediaPacket(r->r, &p);

      if(ret == 2) {
	/* Wait for queues to drain */
	e = mp_wait_for_empty_queues(mp, 0);
	mp_set_playstatus_stop(mp);

	if(e == NULL)
	  e = event_create_type(EVENT_EOF);
	break;
      }

      if(ret == 0) {
	RTMP_Close(r->r);
	  
	RTMP_Init(r->r);

	memset(&p, 0, sizeof(p));

	TRACE(TRACE_DEBUG, "RTMP", "Reconnecting stream at pos %d", 
	      r->seekbase);

	if(!RTMP_SetupURL(r->r, url)) {
	  snprintf(errbuf, errlen, "Unable to setup RTMP session");
	  e = NULL;
	  break;
	}

	if(!RTMP_Connect(r->r, NULL)) {
	  snprintf(errbuf, errlen, "Unable to connect RTMP session");
	  e = NULL;
	  break;
	}

	if(!RTMP_ConnectStream(r->r, r->seekbase / 1000)) {
	  snprintf(errbuf, errlen, "Unable to stream RTMP session");
	  return NULL;
	}
	r->epoch++;


	r->lastdts = 0;
	r->seekbase = AV_NOPTS_VALUE;
	mp_flush(mp, 0);
	continue;
      }

      dts = p.m_nTimeStamp;

      switch(p.m_packetType) {
      case RTMP_PACKET_TYPE_INFO:
	if(handle_metadata(r, p.m_body, p.m_nBodySize, mp, errbuf, errlen)) {
	  RTMPPacket_Free(&p);
	  return NULL;
	}
	break;

      case RTMP_PACKET_TYPE_VIDEO:
	e = get_packet_v(r, (void *)p.m_body, p.m_nBodySize, dts, mp);
	break;

      case RTMP_PACKET_TYPE_AUDIO:
	e = get_packet_a(r, (void *)p.m_body, p.m_nBodySize, dts, mp);
	break;
	
      case 0x16:
	pos = 0;
	break;
      default:
	TRACE(TRACE_DEBUG, "RTMP", 
	      "Got unknown packet type %d\n", p.m_packetType);
	break;
      }
      if(pos == -1)
	RTMPPacket_Free(&p);
    }

    if(pos != -1) {
      if(pos + 11 < p.m_nBodySize) {
	uint32_t ds = AMF_DecodeInt24(p.m_body + pos + 1);
	  
	if(pos + 11 + ds + 4 > p.m_nBodySize) {
	  snprintf(errbuf, errlen, "Corrupt stream");
	  RTMPPacket_Free(&p);
	  return NULL;
	}

	dts = AMF_DecodeInt24(p.m_body + pos + 4);
	dts |= (p.m_body[pos + 7] << 24);

	if(p.m_body[pos] == RTMP_PACKET_TYPE_INFO) {
	  if(handle_metadata(r, p.m_body, p.m_nBodySize, mp, errbuf, errlen)) {
	    RTMPPacket_Free(&p);
	    return NULL;
	  }
	} else if(p.m_body[pos] == RTMP_PACKET_TYPE_VIDEO) {
	  e = get_packet_v(r, (void *)p.m_body + pos + 11, ds, dts, mp);
	} else if(p.m_body[pos] == RTMP_PACKET_TYPE_AUDIO) {
	  e = get_packet_a(r, (void *)p.m_body + pos + 11, ds, dts, mp);
	} else {
	  TRACE(TRACE_DEBUG, "RTMP", 
		"Got unknown packet type %d\n", p.m_body[pos]);
	}
	pos += 11 + ds + 4;
      } else {
	pos = -1;
	RTMPPacket_Free(&p);
      }
    }
    if(e != NULL)
      break;
  }
  return e;
}
int main(int argc, char* argv[])
{
    InitSockets();

    double duration = -1;
    int nRead;
    //is live stream ?
    bool bLiveStream = TRUE;

    int bufsize = 1024 * 1024 * 10;
    char *buf = (char*)malloc(bufsize);
    memset(buf, 0, bufsize);
    long countbufsize = 0;

    FILE *fp = fopen("receive.flv", "wb");
    if (!fp)
    {
        RTMP_LogPrintf("Open File Error.\n");
        CleanupSockets();
        return -1;
    }

    /* set log level */
    RTMP_LogLevel loglvl = RTMP_LOGDEBUG;
    RTMP_LogSetLevel(loglvl);

    RTMP *rtmp = RTMP_Alloc();
    RTMP_Init(rtmp);
    //set connection timeout,default 30s
    rtmp->Link.timeout = 10;

	char* rtmpFilePath = "d:\\rtmp.raw";
	rtmp->m_pRTMPFile = fopen(rtmpFilePath,"rb");
	if (!rtmp->m_pRTMPFile)
	{
		RTMP_LogPrintf("Failed to open File :%s\n", rtmpFilePath);
		return FALSE;
	}

	// HKS's live URL
	if(!RTMP_SetupURL(rtmp, "rtmp://live.hkstv.hk.lxdns.com/live/hks"))
    {
        RTMP_Log(RTMP_LOGERROR, "SetupURL Err\n");
        RTMP_Free(rtmp);
        CleanupSockets();
        return -1;
    }
    if (bLiveStream)
    {
        rtmp->Link.lFlags |= RTMP_LF_LIVE;
    }

    //1hour
    RTMP_SetBufferMS(rtmp, 3600 * 1000);

    if(!RTMP_Connect(rtmp, NULL))
    {
        RTMP_Log(RTMP_LOGERROR, "Connect Err\n");
        RTMP_Free(rtmp);
        CleanupSockets();
        return -1;
    }

    if(!RTMP_ConnectStream(rtmp, 0))
    {
        RTMP_Log(RTMP_LOGERROR, "ConnectStream Err\n");
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
        CleanupSockets();
        return -1;
    }

    while(nRead = RTMP_Read(rtmp, buf, bufsize))
    {
        fwrite(buf, 1, nRead, fp);

        countbufsize += nRead;
        RTMP_LogPrintf("Receive: %5dByte, Total: %5.2fkB\n", nRead, countbufsize * 1.0 / 1024);
    }

    if(fp)
        fclose(fp);

    if(fpPcap)
        fclose(fpPcap);

	if (rtmp->m_pRTMPFile)
	{
		fclose(rtmp->m_pRTMPFile);
	}

    if(buf)
    {
        free(buf);
    }

    if(rtmp)
    {
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
        CleanupSockets();
        rtmp = NULL;
    }
    return 0;
}
Example #5
0
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;

    char srvhead[] = "\r\nServer: HTTP-RTMP Stream Server " RTMPDUMP_VERSION "\r\n";

    char *status = "404 Not Found";

    server->state = STREAMING_IN_PROGRESS;

    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;
    memcpy(&req, &defaultRTMPRequest, sizeof(RTMP_REQUEST));

    // timeout for http requests
    fd_set fds;
    struct timeval tv;

    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)
        {
            filename = header + 4;

            // filter " HTTP/..." from end of request
            char *p = filename;
            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 == '?')
            {
                ptr++;
                int len = strlen(ptr);

                while (len >= 2)
                {
                    char ich = *ptr;
                    ptr++;
                    if (*ptr != '=')
                        goto filenotfound;	// long parameters not (yet) supported

                    ptr++;
                    len -= 2;

                    // get position of the next '&'
                    char *temp;

                    unsigned int nArgLen = len;
                    if ((temp = strstr(ptr, "&")) != 0)
                    {
                        nArgLen = temp - ptr;
                    }

                    char *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 && !req.fullUrl.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 && !req.fullUrl.av_len)
    {
        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 && !req.fullUrl.av_len)
    {
        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 && !req.fullUrl.av_len)
    {
        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 && !req.fullUrl.av_len)
    {
        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);
    if (!req.fullUrl.av_len)
    {
        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, &req.usherToken, dSeek, req.dStopOffset,
                         req.bLiveStream, req.timeout);
    }
    else
    {
        if (RTMP_SetupURL(&rtmp, req.fullUrl.av_val) == FALSE)
        {
            RTMP_Log(RTMP_LOGERROR, "Couldn't parse URL: %s", req.fullUrl.av_val);
            return;
        }
    }
    /* 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.av_val);
    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;
}
Example #6
0
RTMPPublisher::~RTMPPublisher()
{
    //OSDebugOut (TEXT("*** ~RTMPPublisher (%d queued, %d buffered, %d data)\n"), queuedPackets.Num(), bufferedPackets.Num(), curDataBufferLen);
    bStopping = true;

    //we're in the middle of connecting! wait for that to happen to avoid all manner of race conditions
    if (hConnectionThread)
    {
        WaitForSingleObject(hConnectionThread, INFINITE);
        OSCloseThread(hConnectionThread);
    }

    //send all remaining buffered packets, this may block since it respects timestamps
    FlushBufferedPackets ();

    //OSDebugOut (TEXT("%d queued after flush\n"), queuedPackets.Num());

    if(hSendThread)
    {
        //this marks the thread to exit after current work is done
        SetEvent(hSendLoopExit);

        //these wake up the thread
        ReleaseSemaphore(hSendSempahore, 1, NULL);
        SetEvent(hBufferSpaceAvailableEvent);

        //wait 60 sec for it to exit
        OSTerminateThread(hSendThread, 60000);
    }

    if(hSendSempahore)
        CloseHandle(hSendSempahore);

    //OSDebugOut (TEXT("*** ~RTMPPublisher hSendThread terminated (%d queued, %d buffered, %d data)\n"), queuedPackets.Num(), bufferedPackets.Num(), curDataBufferLen);

    if (hSocketThread)
    {
        //mark the socket loop to shut down after the buffer is empty
        SetEvent(hSocketLoopExit);

        //wake it up in case it already is empty
        SetEvent(hBufferEvent);

        //wait 60 sec for it to exit
        OSTerminateThread(hSocketThread, 60000);
    }

    //OSDebugOut (TEXT("*** ~RTMPPublisher hSocketThread terminated (%d queued, %d buffered, %d data)\n"), queuedPackets.Num(), bufferedPackets.Num(), curDataBufferLen);

    if(rtmp)
    {
        if (RTMP_IsConnected(rtmp))
        {
            //at this point nothing should be in the buffer, flush out what remains to the net and make it blocking
            FlushDataBuffer();

            //disable the buffered send, so RTMP_* functions write directly to the net (and thus block)
            rtmp->m_bCustomSend = 0;

            //manually shut down the stream and issue a graceful socket shutdown
            RTMP_DeleteStream(rtmp);

            shutdown(rtmp->m_sb.sb_socket, SD_SEND);

            //this waits for the socket shutdown to complete gracefully
            for (;;)
            {
                char buff[1024];
                int ret;

                ret = recv(rtmp->m_sb.sb_socket, buff, sizeof(buff), 0);
                if (!ret)
                    break;
                else if (ret == -1)
                {
                    Log(TEXT("~RTMPublisher: Received error %d while waiting for graceful shutdown."), WSAGetLastError());
                    break;
                }
            }

            //OSDebugOut(TEXT("Graceful shutdown complete.\n"));
        }

        //this closes the socket if not already done
        RTMP_Close(rtmp);
    }

    if(hDataMutex)
        OSCloseMutex(hDataMutex);

    while (bufferedPackets.Num())
    {
        //this should not happen any more...
        bufferedPackets[0].data.Clear();
        bufferedPackets.Remove(0);
    }

    if (dataBuffer)
        Free(dataBuffer);

    if (hDataBufferMutex)
        OSCloseMutex(hDataBufferMutex);

    if (hBufferEvent)
        CloseHandle(hBufferEvent);

    if (hSendLoopExit)
        CloseHandle(hSendLoopExit);

    if (hSocketLoopExit)
        CloseHandle(hSocketLoopExit);

    if (hSendBacklogEvent)
        CloseHandle(hSendBacklogEvent);

    if (hBufferSpaceAvailableEvent)
        CloseHandle(hBufferSpaceAvailableEvent);

    if (hWriteEvent)
        CloseHandle(hWriteEvent);

    if(rtmp)
    {
        if (rtmp->Link.pubUser.av_val)
            Free(rtmp->Link.pubUser.av_val);
        if (rtmp->Link.pubPasswd.av_val)
            Free(rtmp->Link.pubPasswd.av_val);
        RTMP_Free(rtmp);
    }

    //--------------------------

    for(UINT i=0; i<queuedPackets.Num(); i++)
        queuedPackets[i].data.Clear();
    queuedPackets.Clear();

    double dBFrameDropPercentage = double(numBFramesDumped)/NumTotalVideoFrames()*100.0;
    double dPFrameDropPercentage = double(numPFramesDumped)/NumTotalVideoFrames()*100.0;

    Log(TEXT("Number of times waited to send: %d, Waited for a total of %d bytes"), totalTimesWaited, totalBytesWaited);

    Log(TEXT("Number of b-frames dropped: %u (%0.2g%%), Number of p-frames dropped: %u (%0.2g%%), Total %u (%0.2g%%)"),
        numBFramesDumped, dBFrameDropPercentage,
        numPFramesDumped, dPFrameDropPercentage,
        numBFramesDumped+numPFramesDumped, dBFrameDropPercentage+dPFrameDropPercentage);

    /*if(totalCalls)
        Log(TEXT("average send time: %u"), totalTime/totalCalls);*/

    strRTMPErrors.Clear();

    //--------------------------
}
Example #7
0
static void *send_thread(void *data)
{
	struct rtmp_stream *stream = data;

	os_set_thread_name("rtmp-stream: send_thread");

	while (os_sem_wait(stream->send_sem) == 0) {
		struct encoder_packet packet;

		if (stopping(stream) && stream->stop_ts == 0) {
			break;
		}

		if (!get_next_packet(stream, &packet))
			continue;

		if (stopping(stream)) {
			if (can_shutdown_stream(stream, &packet)) {
				obs_encoder_packet_release(&packet);
				break;
			}
		}

		if (!stream->sent_headers) {
			if (!send_headers(stream)) {
				os_atomic_set_bool(&stream->disconnected, true);
				break;
			}
		}

		if (send_packet(stream, &packet, false, packet.track_idx) < 0) {
			os_atomic_set_bool(&stream->disconnected, true);
			break;
		}
	}

	if (disconnected(stream)) {
		info("Disconnected from %s", stream->path.array);
	} else {
		info("User stopped the stream");
	}

	if (stream->new_socket_loop) {
		os_event_signal(stream->send_thread_signaled_exit);
		os_event_signal(stream->buffer_has_data_event);
		pthread_join(stream->socket_thread, NULL);
		stream->socket_thread_active = false;
		stream->rtmp.m_bCustomSend = false;
	}

	set_output_error(stream);
	RTMP_Close(&stream->rtmp);

	if (!stopping(stream)) {
		pthread_detach(stream->send_thread);
		obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED);
	} else {
		obs_output_end_data_capture(stream->output);
	}

	free_packets(stream);
	os_event_reset(stream->stop_event);
	os_atomic_set_bool(&stream->active, false);
	stream->sent_headers = false;
	return NULL;
}
Example #8
0
//Publish using RTMP_Write()
int publish_using_write(){
         uint32_t start_time=0;
         uint32_t now_time=0;
         uint32_t pre_frame_time=0;
         uint32_t lasttime=0;
         int bNextIsKey=0;
         char* pFileBuf=NULL;
 
         //read from tag header
         uint32_t type=0;
         uint32_t datalength=0;
         uint32_t timestamp=0;
 
         RTMP *rtmp=NULL;                           
        
         FILE*fp=NULL;
         fp=fopen("cuc_ieschool.flv","rb");
         if (!fp){
                   RTMP_LogPrintf("Open File Error.\n");
                   CleanupSockets();
                   return -1;
         }
 
         /* set log level */
         //RTMP_LogLevel loglvl=RTMP_LOGDEBUG;
         //RTMP_LogSetLevel(loglvl);
                  
         if (!InitSockets()){
                  RTMP_LogPrintf("Init Socket Err\n");
                   return -1;
         }
 
         rtmp=RTMP_Alloc();
         RTMP_Init(rtmp);
         //set connection timeout,default 30s
         rtmp->Link.timeout=5;                      
         if(!RTMP_SetupURL(rtmp,"rtmp://localhost/publishlive/livestream"))
         {
                   RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
         }
 
         RTMP_EnableWrite(rtmp);
         //1hour
         RTMP_SetBufferMS(rtmp, 3600*1000);         
         if (!RTMP_Connect(rtmp,NULL)){
                   RTMP_Log(RTMP_LOGERROR,"Connect Err\n");
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
         }
        
         if (!RTMP_ConnectStream(rtmp,0)){
                   RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n");
                   RTMP_Close(rtmp);
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
         }
 
         printf("Start to send data ...\n");
        
         //jump over FLV Header
         fseek(fp,9,SEEK_SET);     
         //jump over previousTagSizen
         fseek(fp,4,SEEK_CUR);   
         start_time=RTMP_GetTime();
         while(1)
         {
                   if((((now_time=RTMP_GetTime())-start_time)
                              <(pre_frame_time)) && bNextIsKey){        
                            //wait for 1 sec if the send process is too fast
                            //this mechanism is not very good,need some improvement
                            if(pre_frame_time>lasttime){
                                     RTMP_LogPrintf("TimeStamp:%8lu ms\n",pre_frame_time);
                                     lasttime=pre_frame_time;
                            }
                            sleep(1);
                            continue;
                   }
                  
                   //jump over type
                   fseek(fp,1,SEEK_CUR);   
                   if(!ReadU24(&datalength,fp))
                            break;
                   if(!ReadTime(&timestamp,fp))
                            break;
                   //jump back
                   fseek(fp,-8,SEEK_CUR);  
                  
                   pFileBuf=(char*)malloc(11+datalength+4);
                   memset(pFileBuf,0,11+datalength+4);
                   if(fread(pFileBuf,1,11+datalength+4,fp)!=(11+datalength+4))
                            break;
                  
                   pre_frame_time=timestamp;
                  
                   if (!RTMP_IsConnected(rtmp)){
                            RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n");
                            break;
                   }
                   if (!RTMP_Write(rtmp,pFileBuf,11+datalength+4)){
                            RTMP_Log(RTMP_LOGERROR,"Rtmp Write Error\n");
                            break;
                   }
                  
                   free(pFileBuf);
                   pFileBuf=NULL;
 
                   if(!PeekU8(&type,fp))
                            break;
                   if(type==0x09){
                            if(fseek(fp,11,SEEK_CUR)!=0)
                                     break;
                            if(!PeekU8(&type,fp)){
                                     break;
                            }
                            if(type==0x17)
                                     bNextIsKey=1;
                            else
                                     bNextIsKey=0;
                            fseek(fp,-11,SEEK_CUR);
                   }
         }
 
         RTMP_LogPrintf("\nSend Data Over\n");
        
         if(fp)
                   fclose(fp);
 
         if (rtmp!=NULL){
                   RTMP_Close(rtmp);        
                   RTMP_Free(rtmp);
                   rtmp=NULL;
         }
 
         if(pFileBuf){
                   free(pFileBuf);
                   pFileBuf=NULL;
         }
 
         CleanupSockets();
         return 0;
}
Example #9
0
/**
 * Open RTMP connection and verify that the stream can be played.
 *
 * URL syntax: rtmp://server[:port][/app][/playpath][ keyword=value]...
 *             where 'app' is first one or two directories in the path
 *             (e.g. /ondemand/, /flash/live/, etc.)
 *             and 'playpath' is a file name (the rest of the path,
 *             may be prefixed with "mp4:")
 *
 *             Additional RTMP library options may be appended as
 *             space-separated key-value pairs.
 */
static int rtmp_open(URLContext *s, const char *uri, int flags)
{
    LibRTMPContext *ctx = s->priv_data;
    RTMP *r = &ctx->rtmp;
    int rc = 0, level;
    char *filename = s->filename;

    switch (av_log_get_level()) {
    default:
    case AV_LOG_FATAL:   level = RTMP_LOGCRIT;    break;
    case AV_LOG_ERROR:   level = RTMP_LOGERROR;   break;
    case AV_LOG_WARNING: level = RTMP_LOGWARNING; break;
    case AV_LOG_INFO:    level = RTMP_LOGINFO;    break;
    case AV_LOG_VERBOSE: level = RTMP_LOGDEBUG;   break;
    case AV_LOG_DEBUG:   level = RTMP_LOGDEBUG2;  break;
    }
    RTMP_LogSetLevel(level);
    RTMP_LogSetCallback(rtmp_log);

    if (ctx->app || ctx->playpath) {
        int len = strlen(s->filename) + 1;
        if (ctx->app)      len += strlen(ctx->app)      + sizeof(" app=");
        if (ctx->playpath) len += strlen(ctx->playpath) + sizeof(" playpath=");

        if (!(ctx->temp_filename = filename = av_malloc(len)))
            return AVERROR(ENOMEM);

        av_strlcpy(filename, s->filename, len);
        if (ctx->app) {
            av_strlcat(filename, " app=", len);
            av_strlcat(filename, ctx->app, len);
        }
        if (ctx->playpath) {
            av_strlcat(filename, " playpath=", len);
            av_strlcat(filename, ctx->playpath, len);
        }
    }

    RTMP_Init(r);
    if (!RTMP_SetupURL(r, filename)) {
        rc = AVERROR_UNKNOWN;
        goto fail;
    }

    if (flags & AVIO_FLAG_WRITE)
        RTMP_EnableWrite(r);

    if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) {
        rc = AVERROR_UNKNOWN;
        goto fail;
    }

    s->is_streamed = 1;
    return 0;
fail:
    av_freep(&ctx->temp_filename);
    if (rc)
        RTMP_Close(r);

    return rc;
}
Example #10
0
RTMPPublisher::~RTMPPublisher()
{
    bStopping = true;

    //we're in the middle of connecting! wait for that to happen to avoid all manner of race conditions
    if (hConnectionThread)
    {
        WaitForSingleObject(hConnectionThread, INFINITE);
        OSCloseThread(hConnectionThread);
    }

    if(hSendThread)
    {
        ReleaseSemaphore(hSendSempahore, 1, NULL);

        //wake it up in case it's waiting for buffer space
        SetEvent(hBufferSpaceAvailableEvent);
        OSTerminateThread(hSendThread, 20000);
    }

    if(hSendSempahore)
        CloseHandle(hSendSempahore);

    if(hDataMutex)
        OSCloseMutex(hDataMutex);

    while (bufferedPackets.Num()) {
        bufferedPackets[0].data.Clear();
        bufferedPackets.Remove(0);
    }

    //wake up and shut down the buffered sender
    SetEvent(hWriteEvent);
    SetEvent(hBufferEvent);

    if (hSocketThread)
    {
        OSTerminateThread(hSocketThread, 20000);

        //at this point nothing new should be coming in to the buffer, flush out what remains
        FlushDataBuffer();
    }

    if(rtmp)
    {
        //disable the buffered send, so RTMP_Close writes directly to the net
        rtmp->m_bCustomSend = 0;
        RTMP_Close(rtmp);
    }

    if (dataBuffer)
        Free(dataBuffer);

    if (hDataBufferMutex)
        OSCloseMutex(hDataBufferMutex);

    if (hBufferEvent)
        CloseHandle(hBufferEvent);

    if (hBufferSpaceAvailableEvent)
        CloseHandle(hBufferSpaceAvailableEvent);

    if (hWriteEvent)
        CloseHandle(hWriteEvent);

    if(rtmp)
        RTMP_Free(rtmp);

    //--------------------------

    for(UINT i=0; i<queuedPackets.Num(); i++)
        queuedPackets[i].data.Clear();
    queuedPackets.Clear();

    double dBFrameDropPercentage = double(numBFramesDumped)/NumTotalVideoFrames()*100.0;
    double dPFrameDropPercentage = double(numPFramesDumped)/NumTotalVideoFrames()*100.0;

    Log(TEXT("Number of times waited to send: %d, Waited for a total of %d bytes"), totalTimesWaited, totalBytesWaited);

    Log(TEXT("Number of b-frames dropped: %u (%0.2g%%), Number of p-frames dropped: %u (%0.2g%%), Total %u (%0.2g%%)"),
        numBFramesDumped, dBFrameDropPercentage,
        numPFramesDumped, dPFrameDropPercentage,
        numBFramesDumped+numPFramesDumped, dBFrameDropPercentage+dPFrameDropPercentage);

    /*if(totalCalls)
        Log(TEXT("average send time: %u"), totalTime/totalCalls);*/

    strRTMPErrors.Clear();

    //--------------------------
}
Example #11
0
static 
void  RtmpDispatchPkt(RTMP_SESSION *pSession, RTMPPacket *pPkt)
{
	RTMP *pRtmp  =  pSession->prtmp;
	/* 处ē†ęŠ„ꖇ */	
	RTMP_Log(RTMP_LOGDEBUG, "%s, received packet type %02X, size %u bytes", __FUNCTION__,
pPkt->m_packetType, pPkt->m_nBodySize);
	switch(pPkt->m_packetType)
	{
		case RTMP_PACKET_TYPE_CHUNK_SIZE:
		{
			break;
		}

		case RTMP_PACKET_TYPE_BYTES_READ_REPORT:
		{
			break;
		}

		case RTMP_PACKET_TYPE_CONTROL:
		{
			break;
		}

		case RTMP_PACKET_TYPE_SERVER_BW:
		{
			break;
		}

		case RTMP_PACKET_TYPE_CLIENT_BW:
		{
			break;
		}
		case RTMP_PACKET_TYPE_AUDIO:
		{
			break;
		}

		case RTMP_PACKET_TYPE_VIDEO:
		{
			break;
		}
		case RTMP_PACKET_TYPE_FLEX_STREAM_SEND:
		{
    		break;
    	}

 		case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT:
 		{
    		break;
		}
  		case RTMP_PACKET_TYPE_FLEX_MESSAGE:
	  	{
		    RTMP_Log(RTMP_LOGDEBUG, "%s, flex message, size %u bytes, not fully supported",
		             __FUNCTION__, pPkt->m_nBodySize);
		    
		    if (SessionInvoke(pSession, pPkt, 1))
		      RTMP_Close(pSession->prtmp);
		    break;
	  	}
  		case RTMP_PACKET_TYPE_INFO:
  		{
    		break;
		}
  		case RTMP_PACKET_TYPE_SHARED_OBJECT:
  		{
    		break;
		}
  		case RTMP_PACKET_TYPE_INVOKE:
  		{
		    RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,
		             pPkt->m_nBodySize);
		   
		    if (SessionInvoke(pSession, pPkt, 0))
		      RTMP_Close(pRtmp);
		    break;
		}
  		case RTMP_PACKET_TYPE_FLASH_VIDEO:
  		{
   			break;
   		}
	  	default:
	  	{
	    	RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,
	            pPkt->m_packetType);
			#ifdef _DEBUG
			RTMP_LogHex(RTMP_LOGDEBUG, pPkt->m_body, pPkt->m_nBodySize);
			#endif
	  	}
	}

    RTMPPacket_Free(pPkt);
    RTMPPacket_Init(pPkt);
}
Example #12
0
void LibRtmp::Close()
{
    RTMP_Close(rtmp_);
}
Example #13
0
//Publish using RTMP_SendPacket()
int publish_using_packet(){
         RTMP *rtmp=NULL;                           
         RTMPPacket *packet=NULL;
         uint32_t start_time=0;
         uint32_t now_time=0;
         //the timestamp of the previous frame
         long pre_frame_time=0;
         long lasttime=0;
         int bNextIsKey=1;
         uint32_t preTagsize=0;
        
         //packet attributes
         uint32_t type=0;                        
         uint32_t datalength=0;           
         uint32_t timestamp=0;           
         uint32_t streamid=0;                        
 
         FILE*fp=NULL;
         fp=fopen("Wildlife.flv","rb");
         if (!fp){
                   RTMP_LogPrintf("Open File Error.\n");
                   CleanupSockets();
                   return -1;
         }
 
         /* set log level */
         //RTMP_LogLevel loglvl=RTMP_LOGDEBUG;
         //RTMP_LogSetLevel(loglvl);
                  
         if (!InitSockets()){
                   RTMP_LogPrintf("Init Socket Err\n");
                   return -1;
         }
 
         rtmp=RTMP_Alloc();
         RTMP_Init(rtmp);
         //set connection timeout,default 30s
         rtmp->Link.timeout=5;                      
         if(!RTMP_SetupURL(rtmp,"rtmp://127.0.0.1:8080/live/test"))
         {
                   RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n");
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
         }
        
         //if unable,the AMF command would be 'play' instead of 'publish'
         RTMP_EnableWrite(rtmp);     
        
         if (!RTMP_Connect(rtmp,NULL)){
                   RTMP_Log(RTMP_LOGERROR,"Connect Err\n");
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
         }
        
         if (!RTMP_ConnectStream(rtmp,0)){
                   RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n");
                   RTMP_Close(rtmp);
                   RTMP_Free(rtmp);
                   CleanupSockets();
                   return -1;
         }
 
         packet=(RTMPPacket*)malloc(sizeof(RTMPPacket));
         RTMPPacket_Alloc(packet,1024*64);
         RTMPPacket_Reset(packet);
 
         packet->m_hasAbsTimestamp = 0;        
         packet->m_nChannel = 0x04;
         packet->m_nInfoField2 = rtmp->m_stream_id;
 
         RTMP_LogPrintf("Start to send data ...\n");
        
         //jump over FLV Header
         fseek(fp,9,SEEK_SET);     
         //jump over previousTagSizen
         fseek(fp,4,SEEK_CUR);   
         start_time=RTMP_GetTime();
         while(1)
         {
                   if((((now_time=RTMP_GetTime())-start_time)
                              <(pre_frame_time)) && bNextIsKey){        
                            //wait for 1 sec if the send process is too fast
                            //this mechanism is not very good,need some improvement
                            if(pre_frame_time>lasttime){
                                     RTMP_LogPrintf("TimeStamp:%8lu ms\n",pre_frame_time);
                                     lasttime=pre_frame_time;
                            }
                            sleep(1);
                            continue;
                   }
                  
                   //not quite the same as FLV spec
                   if(!ReadU8(&type,fp))     
                            break;
                   if(!ReadU24(&datalength,fp))
                            break;
                   if(!ReadTime(&timestamp,fp))
                            break;
                   if(!ReadU24(&streamid,fp))
                            break;
 
                   if (type!=0x08&&type!=0x09){
                            //jump over non_audio and non_video frameļ¼Œ
                            //jump over next previousTagSizen at the same time
                            fseek(fp,datalength+4,SEEK_CUR);
                            continue;
                   }
                  
                   if(fread(packet->m_body,1,datalength,fp)!=datalength)
                            break;
 
                   packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
                   packet->m_nTimeStamp = timestamp;
                   packet->m_packetType = type;
                   packet->m_nBodySize  = datalength;
                   pre_frame_time=timestamp;
 
                   if (!RTMP_IsConnected(rtmp)){
                            RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n");
                            break;
                   }
                   if (!RTMP_SendPacket(rtmp,packet,0)){
                            RTMP_Log(RTMP_LOGERROR,"Send Error\n");
                            break;
                   }
 
                   if(!ReadU32(&preTagsize,fp))
                            break;
                           
                   if(!PeekU8(&type,fp))
                            break;
                   if(type==0x09){
                            if(fseek(fp,11,SEEK_CUR)!=0)
                                     break;
                            if(!PeekU8(&type,fp)){
                                     break;
                            }
                            if(type==0x17)
                                     bNextIsKey=1;
                            else
                                     bNextIsKey=0;
 
                            fseek(fp,-11,SEEK_CUR);
                   }
         }               
 
         RTMP_LogPrintf("\nSend Data Over\n");
        
         if(fp)
                   fclose(fp);
 
         if (rtmp!=NULL){
                   RTMP_Close(rtmp);        
                   RTMP_Free(rtmp); 
                   rtmp=NULL;
         }
         if (packet!=NULL){
                   RTMPPacket_Free(packet);    
                   free(packet);
                   packet=NULL;
         }
 
         CleanupSockets();
         return 0;
}
Example #14
0
static event_t *
rtmp_loop(rtmp_t *r, media_pipe_t *mp, char *url, char *errbuf, size_t errlen)
{
  RTMPPacket p = {0};
  int pos = -1, ret;
  uint32_t dts;
  event_t *e = NULL;

  mp_set_playstatus_by_hold(mp, 0, NULL);

  while(1) {


    if(pos == -1) {

      mp->mp_eof = 0;
      ret = RTMP_GetNextMediaPacket(r->r, &p);

      if(ret == 2) {
	/* Wait for queues to drain */
	mp->mp_eof = 1;
      again:
	e = mp_wait_for_empty_queues(mp);

	if(e != NULL) {
	  e = rtmp_process_event(r, e, NULL);
	  if(e == NULL)
	    goto again;
	}

	if(e == NULL)
	  e = event_create_type(EVENT_EOF);
	break;
      }

      if(ret == 0) {
	int64_t restartpos = r->seekpos_video;

	TRACE(TRACE_ERROR, "RTMP", "Disconnected");
	sleep(1);

	if(restartpos == AV_NOPTS_VALUE) {
	  snprintf(errbuf, errlen,
		   "Giving up restart since nothing was decoded");
	  return NULL;
	}


	RTMP_Close(r->r);
	
	RTMP_Init(r->r);

	memset(&p, 0, sizeof(p));

	TRACE(TRACE_DEBUG, "RTMP", "Reconnecting stream at pos %ld", 
	      restartpos);

	if(!RTMP_SetupURL(r->r, url)) {
	  snprintf(errbuf, errlen, "Unable to setup RTMP session");
	  return NULL;
	}

	if(!RTMP_Connect(r->r, NULL)) {
	  snprintf(errbuf, errlen, "Unable to connect RTMP session");
	  return NULL;
	}

	if(!RTMP_ConnectStream(r->r, 0)) {
	  snprintf(errbuf, errlen, "Unable to stream RTMP session");
	  return NULL;
	}

	if(mp->mp_flags & MP_CAN_SEEK)
	  RTMP_SendSeek(r->r, restartpos / 1000);
	continue;
      }

      dts = p.m_nTimeStamp;

      switch(p.m_packetType) {
      case RTMP_PACKET_TYPE_INFO:
	if(handle_metadata(r, p.m_body, p.m_nBodySize, mp, errbuf, errlen)) {
	  RTMPPacket_Free(&p);
	  return NULL;
	}
	break;

      case RTMP_PACKET_TYPE_VIDEO:
	e = get_packet_v(r, (void *)p.m_body, p.m_nBodySize, dts, mp);
	break;

      case RTMP_PACKET_TYPE_AUDIO:
	e = get_packet_a(r, (void *)p.m_body, p.m_nBodySize, dts, mp);
	break;
	
      case 0x16:
	pos = 0;
	break;
      default:
	TRACE(TRACE_DEBUG, "RTMP", 
	      "Got unknown packet type %d\n", p.m_packetType);
	break;
      }
      if(pos == -1)
	RTMPPacket_Free(&p);
    }

    if(pos != -1) {
      if(pos + 11 < p.m_nBodySize) {
	uint32_t ds = AMF_DecodeInt24(p.m_body + pos + 1);
	  
	if(pos + 11 + ds + 4 > p.m_nBodySize) {
	  snprintf(errbuf, errlen, "Corrupt stream");
	  RTMPPacket_Free(&p);
	  return NULL;
	}

	dts = AMF_DecodeInt24(p.m_body + pos + 4);
	dts |= (p.m_body[pos + 7] << 24);

	if(p.m_body[pos] == RTMP_PACKET_TYPE_INFO) {
	  if(handle_metadata(r, p.m_body, p.m_nBodySize, mp, errbuf, errlen)) {
	    RTMPPacket_Free(&p);
	    return NULL;
	  }
	} else if(p.m_body[pos] == RTMP_PACKET_TYPE_VIDEO) {
	  e = get_packet_v(r, (void *)p.m_body + pos + 11, ds, dts, mp);
	} else if(p.m_body[pos] == RTMP_PACKET_TYPE_AUDIO) {
	  e = get_packet_a(r, (void *)p.m_body + pos + 11, ds, dts, mp);
	} else {
	  TRACE(TRACE_DEBUG, "RTMP", 
		"Got unknown packet type %d\n", p.m_body[pos]);
	}
	pos += 11 + ds + 4;
      } else {
	pos = -1;
	RTMPPacket_Free(&p);
      }
    }
    if(e != NULL)
      break;
  }
  return e;
}
Example #15
0
static int init_send(struct rtmp_stream *stream)
{
	int ret;
	size_t idx = 0;
	bool next = true;

#if defined(_WIN32)
	adjust_sndbuf_size(stream, MIN_SENDBUF_SIZE);
#endif

	reset_semaphore(stream);

	ret = pthread_create(&stream->send_thread, NULL, send_thread, stream);
	if (ret != 0) {
		RTMP_Close(&stream->rtmp);
		warn("Failed to create send thread");
		return OBS_OUTPUT_ERROR;
	}

	if (stream->new_socket_loop) {
		int one = 1;
#ifdef _WIN32
		if (ioctlsocket(stream->rtmp.m_sb.sb_socket, FIONBIO, &one)) {
			stream->rtmp.last_error_code = WSAGetLastError();
#else
		if (ioctl(stream->rtmp.m_sb.sb_socket, FIONBIO, &one)) {
			stream->rtmp.last_error_code = errno;
#endif
			warn("Failed to set non-blocking socket");
			return OBS_OUTPUT_ERROR;
		}

		os_event_reset(stream->send_thread_signaled_exit);

		info("New socket loop enabled by user");
		if (stream->low_latency_mode)
			info("Low latency mode enabled by user");

		if (stream->write_buf)
			bfree(stream->write_buf);

		int total_bitrate = 0;
		obs_output_t  *context  = stream->output;

		obs_encoder_t *vencoder = obs_output_get_video_encoder(context);
		if (vencoder) {
			obs_data_t *params = obs_encoder_get_settings(vencoder);
			if (params) {
				int bitrate = obs_data_get_int(params, "bitrate");
				total_bitrate += bitrate;
				obs_data_release(params);
			}
		}

		obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, 0);
		if (aencoder) {
			obs_data_t *params = obs_encoder_get_settings(aencoder);
			if (params) {
				int bitrate = obs_data_get_int(params, "bitrate");
				total_bitrate += bitrate;
				obs_data_release(params);
			}
		}

		// to bytes/sec
		int ideal_buffer_size = total_bitrate * 128;

		if (ideal_buffer_size < 131072)
			ideal_buffer_size = 131072;

		stream->write_buf_size = ideal_buffer_size;
		stream->write_buf = bmalloc(ideal_buffer_size);

#ifdef _WIN32
		ret = pthread_create(&stream->socket_thread, NULL,
				socket_thread_windows, stream);
#else
		warn("New socket loop not supported on this platform");
		return OBS_OUTPUT_ERROR;
#endif

		if (ret != 0) {
			RTMP_Close(&stream->rtmp);
			warn("Failed to create socket thread");
			return OBS_OUTPUT_ERROR;
		}

		stream->socket_thread_active = true;
		stream->rtmp.m_bCustomSend = true;
		stream->rtmp.m_customSendFunc = socket_queue_data;
		stream->rtmp.m_customSendParam = stream;
	}

	os_atomic_set_bool(&stream->active, true);
	while (next) {
		if (!send_meta_data(stream, idx++, &next)) {
			warn("Disconnected while attempting to connect to "
			     "server.");
			set_output_error(stream);
			return OBS_OUTPUT_DISCONNECTED;
		}
	}
	obs_output_begin_data_capture(stream->output, 0);

	return OBS_OUTPUT_SUCCESS;
}

#ifdef _WIN32
static void win32_log_interface_type(struct rtmp_stream *stream)
{
	RTMP *rtmp = &stream->rtmp;
	MIB_IPFORWARDROW route;
	uint32_t dest_addr, source_addr;
	char hostname[256];
	HOSTENT *h;

	if (rtmp->Link.hostname.av_len >= sizeof(hostname) - 1)
		return;

	strncpy(hostname, rtmp->Link.hostname.av_val, sizeof(hostname));
	hostname[rtmp->Link.hostname.av_len] = 0;

	h = gethostbyname(hostname);
	if (!h)
		return;

	dest_addr = *(uint32_t*)h->h_addr_list[0];

	if (rtmp->m_bindIP.addrLen == 0)
		source_addr = 0;
	else if (rtmp->m_bindIP.addr.ss_family == AF_INET)
		source_addr = (*(struct sockaddr_in*)&rtmp->m_bindIP)
			.sin_addr.S_un.S_addr;
	else
		return;

	if (!GetBestRoute(dest_addr, source_addr, &route)) {
		MIB_IFROW row;
		memset(&row, 0, sizeof(row));
		row.dwIndex = route.dwForwardIfIndex;

		if (!GetIfEntry(&row)) {
			uint32_t speed =row.dwSpeed / 1000000;
			char *type;
			struct dstr other = {0};

			if (row.dwType == IF_TYPE_ETHERNET_CSMACD) {
				type = "ethernet";
			} else if (row.dwType == IF_TYPE_IEEE80211) {
				type = "802.11";
			} else {
				dstr_printf(&other, "type %lu", row.dwType);
				type = other.array;
			}

			info("Interface: %s (%s, %lu mbps)", row.bDescr, type,
					speed);

			dstr_free(&other);
		}
	}
}
Example #16
0
int main(int argc, char **argv) {
LOGV("Main()");
extern char *optarg;
extern int optind;
//reset optind counter
optind=1;

int nStatus = RD_SUCCESS;
double percent = 0;
double duration = 0.0;

int nSkipKeyFrames = DEF_SKIPFRM;// skip this number of keyframes when resuming

int bOverrideBufferTime = FALSE;// if the user specifies a buffer time override this is true
int bStdoutMode = TRUE;	// if true print the stream directly to stdout, messages go to stderr
int bResume = FALSE;		// true in resume mode
uint32_t dSeek = 0;		// seek position in resume mode, 0 otherwise
uint32_t bufferTime = DEF_BUFTIME;

// meta header and initial frame for the resume mode (they are read from the file and compared with
// the stream we are trying to continue
char *metaHeader = 0;
uint32_t nMetaHeaderSize = 0;

// video keyframe for matching
char *initialFrame = 0;
uint32_t nInitialFrameSize = 0;
int initialFrameType = 0;	// tye: audio or video

AVal hostname = { 0, 0 };
AVal playpath = { 0, 0 };
AVal subscribepath = { 0, 0 };
AVal usherToken = { 0, 0 }; //Justin.tv auth token
int port = -1;
int protocol = RTMP_PROTOCOL_UNDEFINED;
int retries = 0;
int bLiveStream = FALSE;	// is it a live stream? then we can't seek/resume
int bRealtimeStream = FALSE;  // If true, disable the BUFX hack (be patient)
int bHashes = FALSE;		// display byte counters not hashes by default

long int timeout = DEF_TIMEOUT;	// timeout connection after 120 seconds
uint32_t dStartOffset = 0;	// seek position in non-live mode
uint32_t dStopOffset = 0;
RTMP rtmp = { 0 };

AVal swfUrl = { 0, 0 };
AVal tcUrl = { 0, 0 };
AVal pageUrl = { 0, 0 };
AVal app = { 0, 0 };
AVal auth = { 0, 0 };
AVal swfHash = { 0, 0 };
uint32_t swfSize = 0;
AVal flashVer = { 0, 0 };
AVal sockshost = { 0, 0 };

#ifdef CRYPTO
int swfAge = 30; /* 30 days for SWF cache by default */
int swfVfy = 0;
unsigned char hash[RTMP_SWF_HASHLEN];
#endif

char *flvFile = 0;

signal(SIGINT, sigIntHandler);
signal(SIGTERM, sigIntHandler);
#ifndef WIN32
signal(SIGHUP, sigIntHandler);
signal(SIGPIPE, sigIntHandler);
signal(SIGQUIT, sigIntHandler);
#endif

RTMP_debuglevel = RTMP_LOGINFO;

// Check for --quiet option before printing any output
int index = 0;
while (index < argc) {
	if (strcmp(argv[index], "--quiet") == 0 || strcmp(argv[index], "-q") == 0)
		RTMP_debuglevel = RTMP_LOGCRIT;
	index++;
}

RTMP_LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION);
RTMP_LogPrintf(
		"(c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n");

if (!InitSockets()) {
	RTMP_Log(RTMP_LOGERROR,
			"Couldn't load sockets support on your platform, exiting!");
	return RD_FAILED;
}

/* sleep(30); */

RTMP_Init(&rtmp);

int opt;
struct option longopts[] = { { "help", 0, NULL, 'h' }, { "host", 1, NULL, 'n' },
		{ "port", 1, NULL, 'c' }, { "socks", 1, NULL, 'S' }, { "protocol", 1,
				NULL, 'l' }, { "playpath", 1, NULL, 'y' }, { "playlist", 0,
				NULL, 'Y' }, { "rtmp", 1, NULL, 'r' },
		{ "swfUrl", 1, NULL, 's' }, { "tcUrl", 1, NULL, 't' }, { "pageUrl", 1,
				NULL, 'p' }, { "app", 1, NULL, 'a' }, { "auth", 1, NULL, 'u' },
		{ "conn", 1, NULL, 'C' },
#ifdef CRYPTO
		{ "swfhash", 1, NULL, 'w' }, { "swfsize", 1, NULL, 'x' }, { "swfVfy", 1,
				NULL, 'W' }, { "swfAge", 1, NULL, 'X' },
#endif
		{ "flashVer", 1, NULL, 'f' }, { "live", 0, NULL, 'v' }, { "realtime", 0,
				NULL, 'R' }, { "flv", 1, NULL, 'o' },
		{ "resume", 0, NULL, 'e' }, { "timeout", 1, NULL, 'm' }, { "buffer", 1,
				NULL, 'b' }, { "skip", 1, NULL, 'k' }, { "subscribe", 1, NULL,
				'd' }, { "start", 1, NULL, 'A' }, { "stop", 1, NULL, 'B' }, {
				"token", 1, NULL, 'T' }, { "hashes", 0, NULL, '#' }, { "debug",
				0, NULL, 'z' }, { "quiet", 0, NULL, 'q' }, { "verbose", 0, NULL,
				'V' }, { "jtv", 1, NULL, 'j' }, { 0, 0, 0, 0 } };

while ((opt = getopt_long(argc, argv,
		"hVveqzRr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:",
		longopts, NULL)) != -1) {
	switch (opt) {
	case 'h':
		usage(argv[0]);
		return RD_SUCCESS;
#ifdef CRYPTO
	case 'w': {
		int res = hex2bin(optarg, &swfHash.av_val);
		if (res != RTMP_SWF_HASHLEN) {
			swfHash.av_val = NULL;
			RTMP_Log(RTMP_LOGWARNING,
					"Couldn't parse swf hash hex string, not hexstring or not %d bytes, ignoring!",
					RTMP_SWF_HASHLEN);
		}
		swfHash.av_len = RTMP_SWF_HASHLEN;
		break;
	}
	case 'x': {
		int size = atoi(optarg);
		if (size <= 0) {
			RTMP_Log(RTMP_LOGERROR, "SWF Size must be at least 1, ignoring\n");
		} else {
			swfSize = size;
		}
		break;
	}
	case 'W':
		STR2AVAL(swfUrl, optarg);
		swfVfy = 1;
		break;
	case 'X': {
		int num = atoi(optarg);
		if (num < 0) {
			RTMP_Log(RTMP_LOGERROR, "SWF Age must be non-negative, ignoring\n");
		} else {
			swfAge = num;
		}
	}
		break;
#endif
	case 'k':
		nSkipKeyFrames = atoi(optarg);
		if (nSkipKeyFrames < 0) {
			RTMP_Log(RTMP_LOGERROR,
					"Number of keyframes skipped must be greater or equal zero, using zero!");
			nSkipKeyFrames = 0;
		} else {
			RTMP_Log(RTMP_LOGDEBUG,
					"Number of skipped key frames for resume: %d",
					nSkipKeyFrames);
		}
		break;
	case 'b': {
		int32_t bt = atol(optarg);
		if (bt < 0) {
			RTMP_Log(RTMP_LOGERROR,
					"Buffer time must be greater than zero, ignoring the specified value %d!",
					bt);
		} else {
			bufferTime = bt;
			bOverrideBufferTime = TRUE;
		}
		break;
	}
	case 'v':
		bLiveStream = TRUE;	// no seeking or resuming possible!
		break;
	case 'R':
		bRealtimeStream = TRUE; // seeking and resuming is still possible
		break;
	case 'd':
		STR2AVAL(subscribepath, optarg);
		break;
	case 'n':
		STR2AVAL(hostname, optarg);
		break;
	case 'c':
		port = atoi(optarg);
		break;
	case 'l':
		protocol = atoi(optarg);
		if (protocol < RTMP_PROTOCOL_RTMP || protocol > RTMP_PROTOCOL_RTMPTS) {
			RTMP_Log(RTMP_LOGERROR, "Unknown protocol specified: %d", protocol);
			return RD_FAILED;
		}
		break;
	case 'y':
		STR2AVAL(playpath, optarg);
		break;
	case 'Y':
		RTMP_SetOpt(&rtmp, &av_playlist, (AVal *) &av_true);
		break;
	case 'r': {
		AVal parsedHost, parsedApp, parsedPlaypath;
		unsigned int parsedPort = 0;
		int parsedProtocol = RTMP_PROTOCOL_UNDEFINED;

		if (!RTMP_ParseURL(optarg, &parsedProtocol, &parsedHost, &parsedPort,
				&parsedPlaypath, &parsedApp)) {
			RTMP_Log(RTMP_LOGWARNING, "Couldn't parse the specified url (%s)!",
					optarg);
		} else {
			if (!hostname.av_len)
				hostname = parsedHost;
			if (port == -1)
				port = parsedPort;
			if (playpath.av_len == 0 && parsedPlaypath.av_len) {
				playpath = parsedPlaypath;
			}
			if (protocol == RTMP_PROTOCOL_UNDEFINED)
				protocol = parsedProtocol;
			if (app.av_len == 0 && parsedApp.av_len) {
				app = parsedApp;
			}
		}
		break;
	}
	case 's':
		STR2AVAL(swfUrl, optarg);
		break;
	case 't':
		STR2AVAL(tcUrl, optarg);
		break;
	case 'p':
		STR2AVAL(pageUrl, optarg);
		break;
	case 'a':
		STR2AVAL(app, optarg);
		break;
	case 'f':
		STR2AVAL(flashVer, optarg);
		break;
	case 'o':
		flvFile = optarg;
		if (strcmp(flvFile, "-"))
			bStdoutMode = FALSE;

		break;
	case 'e':
		bResume = TRUE;
		break;
	case 'u':
		STR2AVAL(auth, optarg);
		break;
	case 'C': {
		AVal av;
		STR2AVAL(av, optarg);
		if (!RTMP_SetOpt(&rtmp, &av_conn, &av)) {
			RTMP_Log(RTMP_LOGERROR, "Invalid AMF parameter: %s", optarg);
			return RD_FAILED;
		}
	}
		break;
	case 'm':
		timeout = atoi(optarg);
		break;
	case 'A':
		dStartOffset = (int) (atof(optarg) * 1000.0);
		break;
	case 'B':
		dStopOffset = (int) (atof(optarg) * 1000.0);
		break;
	case 'T': {
		AVal token;
		STR2AVAL(token, optarg);
		RTMP_SetOpt(&rtmp, &av_token, &token);
	}
		break;
	case '#':
		bHashes = TRUE;
		break;
	case 'q':
		RTMP_debuglevel = RTMP_LOGCRIT;
		break;
	case 'V':
		RTMP_debuglevel = RTMP_LOGDEBUG;
		break;
	case 'z':
		RTMP_debuglevel = RTMP_LOGALL;
		break;
	case 'S':
		STR2AVAL(sockshost, optarg);
		break;
	case 'j':
		STR2AVAL(usherToken, optarg);
		break;
	default:
		RTMP_LogPrintf("unknown option: %c\n", opt);
		usage(argv[0]);
		return RD_FAILED;
		break;
	}
}

if (!hostname.av_len) {
	RTMP_Log(RTMP_LOGERROR,
			"You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname");
	return RD_FAILED;
}
if (playpath.av_len == 0) {
	RTMP_Log(RTMP_LOGERROR,
			"You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath");
	return RD_FAILED;
}

if (protocol == RTMP_PROTOCOL_UNDEFINED) {
	RTMP_Log(RTMP_LOGWARNING,
			"You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP");
	protocol = RTMP_PROTOCOL_RTMP;
}
if (port == -1) {
	RTMP_Log(RTMP_LOGWARNING,
			"You haven't specified a port (--port) or rtmp url (-r), using default port 1935");
	port = 0;
}
if (port == 0) {
	if (protocol & RTMP_FEATURE_SSL)
		port = 443;
	else if (protocol & RTMP_FEATURE_HTTP)
		port = 80;
	else
		port = 1935;
}

if (flvFile == 0) {
	RTMP_Log(RTMP_LOGWARNING,
			"You haven't specified an output file (-o filename), using stdout");
	bStdoutMode = TRUE;
}

if (bStdoutMode && bResume) {
	RTMP_Log(RTMP_LOGWARNING,
			"Can't resume in stdout mode, ignoring --resume option");
	bResume = FALSE;
}

if (bLiveStream && bResume) {
	RTMP_Log(RTMP_LOGWARNING,
			"Can't resume live stream, ignoring --resume option");
	bResume = FALSE;
}

#ifdef CRYPTO
if (swfVfy) {
	if (RTMP_HashSWF(swfUrl.av_val, &swfSize, hash, swfAge) == 0) {
		swfHash.av_val = (char *) hash;
		swfHash.av_len = RTMP_SWF_HASHLEN;
	}
}

if (swfHash.av_len == 0 && swfSize > 0) {
	RTMP_Log(RTMP_LOGWARNING,
			"Ignoring SWF size, supply also the hash with --swfhash");
	swfSize = 0;
}

if (swfHash.av_len != 0 && swfSize == 0) {
	RTMP_Log(RTMP_LOGWARNING,
			"Ignoring SWF hash, supply also the swf size  with --swfsize");
	swfHash.av_len = 0;
	swfHash.av_val = NULL;
}
#endif

if (tcUrl.av_len == 0) {
	tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) + hostname.av_len
			+ app.av_len + sizeof("://:65535/");
	tcUrl.av_val = (char *) malloc(tcUrl.av_len);
	if (!tcUrl.av_val)
		return RD_FAILED;
	tcUrl.av_len = snprintf(tcUrl.av_val, tcUrl.av_len, "%s://%.*s:%d/%.*s",
			RTMPProtocolStringsLower[protocol], hostname.av_len,
			hostname.av_val, port, app.av_len, app.av_val);
}

int first = 1;

// User defined seek offset
if (dStartOffset > 0) {
	// Live stream
	if (bLiveStream) {
		RTMP_Log(RTMP_LOGWARNING,
				"Can't seek in a live stream, ignoring --start option");
		dStartOffset = 0;
	}
}

RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath,
		&tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize, &flashVer,
		&subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout);

/* Try to keep the stream moving if it pauses on us */
if (!bLiveStream && !bRealtimeStream && !(protocol & RTMP_FEATURE_HTTP))
	rtmp.Link.lFlags |= RTMP_LF_BUFX;

off_t size = 0;

// ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams)
if (bResume) {
	nStatus = OpenResumeFile(flvFile, &file, &size, &metaHeader,
			&nMetaHeaderSize, &duration);
	if (nStatus == RD_FAILED)
		goto clean;

	if (!file) {
		// file does not exist, so go back into normal mode
		bResume = FALSE; // we are back in fresh file mode (otherwise finalizing file won't be done)
	} else {
		nStatus = GetLastKeyframe(file, nSkipKeyFrames, &dSeek, &initialFrame,
				&initialFrameType, &nInitialFrameSize);
		if (nStatus == RD_FAILED) {
			RTMP_Log(RTMP_LOGDEBUG, "Failed to get last keyframe.");
			goto clean;
		}

		if (dSeek == 0) {
			RTMP_Log(RTMP_LOGDEBUG,
					"Last keyframe is first frame in stream, switching from resume to normal mode!");
			bResume = FALSE;
		}
	}
}

if (!file) {
	if (bStdoutMode) {
		file = stdout;
		SET_BINMODE(file);
	} else {
		file = fopen(flvFile, "w+b");
		if (file == 0) {
			RTMP_LogPrintf("Failed to open file! %s\n", flvFile);
			return RD_FAILED;
		}
	}
}

#ifdef _DEBUG
netstackdump = fopen("netstackdump", "wb");
netstackdump_read = fopen("netstackdump_read", "wb");
#endif

while (!RTMP_ctrlC) {
	RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", bufferTime);
	RTMP_SetBufferMS(&rtmp, bufferTime);

	if (first) {
		first = 0;
		RTMP_LogPrintf("Connecting ...\n");

		if (!RTMP_Connect(&rtmp, NULL)) {
			nStatus = RD_NO_CONNECT;
			break;
		}

		RTMP_Log(RTMP_LOGINFO, "Connected...");

		// User defined seek offset
		if (dStartOffset > 0) {
			// Don't need the start offset if resuming an existing file
			if (bResume) {
				RTMP_Log(RTMP_LOGWARNING,
						"Can't seek a resumed stream, ignoring --start option");
				dStartOffset = 0;
			} else {
				dSeek = dStartOffset;
			}
		}

		// Calculate the length of the stream to still play
		if (dStopOffset > 0) {
			// Quit if start seek is past required stop offset
			if (dStopOffset <= dSeek) {
				RTMP_LogPrintf("Already Completed\n");
				nStatus = RD_SUCCESS;
				break;
			}
		}

		if (!RTMP_ConnectStream(&rtmp, dSeek)) {
			nStatus = RD_FAILED;
			break;
		}
	} else {
		nInitialFrameSize = 0;

		if (retries) {
			RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n");
			if (!RTMP_IsTimedout(&rtmp))
				nStatus = RD_FAILED;
			else
				nStatus = RD_INCOMPLETE;
			break;
		}
		RTMP_Log(RTMP_LOGINFO, "Connection timed out, trying to resume.\n\n");
		/* Did we already try pausing, and it still didn't work? */
		if (rtmp.m_pausing == 3) {
			/* Only one try at reconnecting... */
			retries = 1;
			dSeek = rtmp.m_pauseStamp;
			if (dStopOffset > 0) {
				if (dStopOffset <= dSeek) {
					RTMP_LogPrintf("Already Completed\n");
					nStatus = RD_SUCCESS;
					break;
				}
			}
			if (!RTMP_ReconnectStream(&rtmp, dSeek)) {
				RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n");
				if (!RTMP_IsTimedout(&rtmp))
					nStatus = RD_FAILED;
				else
					nStatus = RD_INCOMPLETE;
				break;
			}
		} else if (!RTMP_ToggleStream(&rtmp)) {
			RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n");
			if (!RTMP_IsTimedout(&rtmp))
				nStatus = RD_FAILED;
			else
				nStatus = RD_INCOMPLETE;
			break;
		}
		bResume = TRUE;
	}

	nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume,
			metaHeader, nMetaHeaderSize, initialFrame, initialFrameType,
			nInitialFrameSize, nSkipKeyFrames, bStdoutMode, bLiveStream,
			bRealtimeStream, bHashes, bOverrideBufferTime, bufferTime,
			&percent);
	free(initialFrame);
	initialFrame = NULL;

	/* If we succeeded, we're done.
	 */
	if (nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(&rtmp) || bLiveStream)
		break;
}

if (nStatus == RD_SUCCESS) {
	RTMP_LogPrintf("Download complete\n");
} else if (nStatus == RD_INCOMPLETE) {
	RTMP_LogPrintf(
			"Download may be incomplete (downloaded about %.2f%%), try resuming\n",
			percent);
}

clean: RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n");
RTMP_Close(&rtmp);

if (file != 0){
	fclose(file);
	file = 0;
}

CleanupSockets();

#ifdef _DEBUG
if (netstackdump != 0)
fclose(netstackdump);
if (netstackdump_read != 0)
fclose(netstackdump_read);
#endif
return nStatus;
}
Example #17
0
int rtmp_open(LibRTMPContext *ctx , int flags)
{
    RTMP *r = &ctx->rtmp;
    int rc = 0;
    char *filename = NULL;
    if(ctx->filename==NULL)
        return -1;
    int len = strlen(ctx->filename) + 1;

    RTMP_LogSetLevel(RTMP_LOGERROR);
    RTMP_LogSetCallback(rtmp_log);

    if (ctx->app)      len += strlen(ctx->app)      + sizeof(" app=");
    if (ctx->tcurl)    len += strlen(ctx->tcurl)    + sizeof(" tcUrl=");
    if (ctx->pageurl)  len += strlen(ctx->pageurl)  + sizeof(" pageUrl=");
    if (ctx->flashver) len += strlen(ctx->flashver) + sizeof(" flashver=");

    if (ctx->conn) {
        char *sep, *p = ctx->conn;
        int options = 0;

        while (p) {
            options++;
            p += strspn(p, " ");
            if (!*p)
                break;
            sep = strchr(p, ' ');
            if (sep)
                p = sep + 1;
            else
                break;
        }
        len += options * sizeof(" conn=");
        len += strlen(ctx->conn);
    }

    if (ctx->playpath)
        len += strlen(ctx->playpath) + sizeof(" playpath=");
    if (ctx->live)
        len += sizeof(" live=1");
    if (ctx->subscribe)
        len += strlen(ctx->subscribe) + sizeof(" subscribe=");

    if (ctx->client_buffer_time)
        len += strlen(ctx->client_buffer_time) + sizeof(" buffer=");

    if (ctx->swfurl || ctx->swfverify) {
        len += sizeof(" swfUrl=");

        if (ctx->swfverify)
            len += strlen(ctx->swfverify) + sizeof(" swfVfy=1");
        else
            len += strlen(ctx->swfurl);
    }

    if (!(ctx->temp_filename = filename = malloc(len)))
        return ERROR_MEM;

    strlcpy(filename, ctx->filename, len);
    if (ctx->app) {
        strlcat(filename, " app=", len);
        strlcat(filename, ctx->app, len);
    }
    if (ctx->tcurl) {
        strlcat(filename, " tcUrl=", len);
        strlcat(filename, ctx->tcurl, len);
    }
    if (ctx->pageurl) {
        strlcat(filename, " pageUrl=", len);
        strlcat(filename, ctx->pageurl, len);
    }
    if (ctx->swfurl) {
        strlcat(filename, " swfUrl=", len);
        strlcat(filename, ctx->swfurl, len);
    }
    if (ctx->flashver) {
        strlcat(filename, " flashVer=", len);
        strlcat(filename, ctx->flashver, len);
    }
    if (ctx->conn) {
        char *sep, *p = ctx->conn;
        while (p) {
            strlcat(filename, " conn=", len);
            p += strspn(p, " ");
            if (!*p)
                break;
            sep = strchr(p, ' ');
            if (sep)
                *sep = '\0';
            strlcat(filename, p, len);

            if (sep)
                p = sep + 1;
        }
    }
    if (ctx->playpath) {
        strlcat(filename, " playpath=", len);
        strlcat(filename, ctx->playpath, len);
    }
    if (ctx->live)
        strlcat(filename, " live=1", len);
    if (ctx->subscribe) {
        strlcat(filename, " subscribe=", len);
        strlcat(filename, ctx->subscribe, len);
    }
    if (ctx->client_buffer_time) {
        strlcat(filename, " buffer=", len);
        strlcat(filename, ctx->client_buffer_time, len);
    }
    if (ctx->swfurl || ctx->swfverify) {
        strlcat(filename, " swfUrl=", len);

        if (ctx->swfverify) {
            strlcat(filename, ctx->swfverify, len);
            strlcat(filename, " swfVfy=1", len);
        } else {
            strlcat(filename, ctx->swfurl, len);
        }
    }

    RTMP_Init(r);
    if (!RTMP_SetupURL(r, filename)) {
        rc = ERROR_SETUP;
        goto fail;
    }

   // if (flags & WRITE_FLAGS)
        RTMP_EnableWrite(r);

    if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) {
        rc = ERROR_CONNECT;
        goto fail;
    }
/*
    if (ctx->buffer_size >= 0 && (flags & WRITE_FLAGS)) {
        int tmp = ctx->buffer_size;
        setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp));
    }
*/
    return 0;
fail:
    //free(ctx->temp_filename);
    if (rc)
        RTMP_Close(r);

    return rc;
}