librtmp 최적화

      librtmp는 RTMP 의 오픈 소스 라이브러리 로 많은 곳에서 스트림을 푸시하고 풀하는 데 사용됩니다. RTMPDump 오픈 소스 소프트웨어의 일부이며 다운로드 주소는 librtmp: RTMPDump 이며 최신 버전은 V2.3입니다. 이 문서는 librtmp 최적화에 중점을 둡니다.

1. 네트워크 출력 블록 크기를 조정합니다.

RTMP_Connect0 함수에서 LibRTMP는 TCP 옵션인 Nagle 알고리즘을 해제한다.이는 실시간 성능에는 좋지만 LibRTMP의 구조체 RTMP의 멤버는 m_outChunkSize를 가지며 기본 값인 128은 RTMP_Init 함수 그러면 전체 LibRTMP 코드는 m_outChunkSize의 인터페이스 함수를 변경하지 않으며 내부적으로 m_outChunkSize의 구현 논리를 변경하지 않으며 블록 크기를 변경하라는 메시지를 스트리밍 미디어 서버의 코드 논리로 보내지 않습니다. Nagle을 끄고 이렇게 작은 블록 크기를 추가하면 작은 패킷이 많이 생기고, Ethernet의 MTU는 1500이므로 주로 미디어 스트림을 받기 때문에 재생 클라이언트에서 사용한다면 괜찮지만, 미디어 스트림을 릴리스하는 푸시 스트림 클라이언트, 네트워크 효율성이 너무 낮고 작은 IP 패킷이 너무 많아 스트리밍 미디어의 서버 소프트 인터럽트가 증가하여 커널의 CPU 사용량이 높아집니다. m_outChunkSize는 메시지가 스트리밍 미디어 서버로 전송될 때 청크에 사용되므로 이 측면에서 LibRTMP는 청크 크기 변경을 부분적으로 지원하며 논리 구현의 이 부분은 변경할 필요가 없습니다.

출력 청크 크기 조정 기능

static int
ChangeChunkSize(RTMP *r,int outChunkSize)
{
RTMPPacket packet;
char pbuf[RTMP_MAX_HEADER_SIZE + 4];

packet.m_nBytesRead = 0;
packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;

packet.m_packetType = RTMP_PACKET_TYPE_CHUNK_SIZE;
packet.m_nChannel = 0x04; 
packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
packet.m_nTimeStamp = 0;
packet.m_nInfoField2 = 0;
packet.m_hasAbsTimestamp = 0;
packet.m_nBodySize = 4;
r->m_outChunkSize = outChunkSize;


r->m_outChunkSize = htonl(r->m_outChunkSize);

memcpy(packet.m_body, &r->m_outChunkSize, 4);

r->m_outChunkSize = ntohl(r->m_outChunkSize);

return RTMP_SendPacket(r, &packet, TRUE);
}

참고 1: RTMP 프로토콜의 메시지 유형 01(RTMP_PACKET_TYPE_CHUNK_SIZE 매크로의 값)은 MTU와 결합된 출력 블록 크기를 변경하는 데 사용되는 메시지 유형입니다.

참고 2: outChunkSize의 크기는 1500-20(IP 헤더)-20(TCP 헤더)=1460으로 선택 가능하며, IP 헤더와 TCP 헤더는 확장 옵션이 있고 PPPoE의 경우 1360으로 선택 가능 보장을 위해 또는 MTU의 다른 값보다 크게 설정할 수 있지만 이 경우 IP 조각화가 나타나므로 좋은 습관이 아닙니다.

참고 3: 이 함수가 호출될 때마다 RTMP의 멤버 변수 m_outChunkSize는 서버에서 수신한 것과 일관성을 유지하는 방식으로 수정됩니다.

네트워크 출력 블록 크기를 조정하는 기능의 타이밍

언제든지 조정할 수 있지만 ChangeChunkSize 함수를 호출한 후에는 RTMP의 멤버 변수 m_outChunkSize가 이 함수 내에서 변경되었음을 알 수 있으므로 이 함수 호출 후 스트리밍 미디어 서버로 전송되는 모든 메시지는 다음과 같이 분할됩니다. 이 청크 크기에 따라 청크는 TCP의 질서로 인해 서버가 블록 크기 변경 메시지를 수신한 후 이 블록 크기로 모든 후속 메시지를 구문 분석합니다.재생 클라이언트는 주로 스트리밍을 위한 것이므로 재생 end는 서버에 데이터를 전달해야 하는 경우가 많으므로 수정할 필요가 없습니다. HandleInvoke 함수):

if (r->Link.protocol & RTMP_FEATURE_WRITE)
{
ChangeChunkSize(r, 1360);//若不改拉流时的输出块大小在这里调用ChangeChunkSize
SendReleaseStream(r);
SendFCPublish(r);
}
else
{
RTMP_SendServerBW(r);
RTMP_SendCtrl(r, 3, 0, 300);
}

2. AMF_GetProp 함수 버그

librtmp\amf.c 파일에 함수가 있습니다(약 1124행).

AMFObjectProperty *
AMF_GetProp(AMFObject *obj, const AVal *name, int nIndex)
{
  if (nIndex >= 0)
    {
      if (nIndex <= obj->o_num) //这里有问题
	return &obj->o_props[nIndex];
    }
  else
    {
      int n;
      for (n = 0; n < obj->o_num; n++)
	{
	  if (AVMATCH(&obj->o_props[n].p_name, name))
	    return &obj->o_props[n];
	}
    }
 
  return (AMFObjectProperty *)&AMFProp_Invalid;
}

3. librtmp 전송 차단

librtmp의 RTMP_Connect0( )에 전송 시간 초과 설정을 추가했습니다.

/* set timeout */
{
    struct timeval timeout;
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;
      
    SET_RCVTIMEO(tv, r->Link.timeout);
    if (setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)))
    {
        RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!", __FUNCTION__, r->Link.timeout);
    }
      
    if(setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)) == -1)
    {
        RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!", __FUNCTION__, timeout.tv_sec);
    }
    else
    {
        RTMP_Log(RTMP_LOGDEBUG, "%s, Setting socket timeout to %ds success!", __FUNCTION__, timeout.tv_sec);
    }
}

4. librtmp 스트리밍 네트워크의 연결 해제로 인해 전체 응용 프로그램이 충돌했고 WriteN 함수에서 충돌 스택이 나타났습니다.
해결 방법은 rtmp.c 파일에서 WriteN 함수를 수정하는 것입니다.

static int
WriteN(RTMP *r, const char *buffer, int n)
{
  const char *ptr = buffer;
#ifdef CRYPTO
  char *encrypted = 0;
  char buf[RTMP_BUFFER_CACHE_SIZE];
 
  if (r->Link.rc4keyOut)
    {
      if (n > sizeof(buf))
    encrypted = (char *)malloc(n);
      else
    encrypted = (char *)buf;
      ptr = encrypted;
      RC4_encrypt2(r->Link.rc4keyOut, n, buffer, ptr);
    }
#endif
 
  while (n > 0)
    {
      int nBytes;
 
      if (r->Link.protocol & RTMP_FEATURE_HTTP)
        nBytes = HTTP_Post(r, RTMPT_SEND, ptr, n);
      else
        nBytes = RTMPSockBuf_Send(&r->m_sb, ptr, n);
      /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d\n", __FUNCTION__, nBytes); */
 
      if (nBytes < 0)
    {
      int sockerr = GetSockError();
      RTMP_Log(RTMP_LOGERROR, "%s, RTMP send error %d (%d bytes)", __FUNCTION__,
          sockerr, n);
 
      if (sockerr == EINTR && !RTMP_ctrlC)
        continue;
 
     if(WSAECONNABORTED  == sockerr || WSAECONNRESET  == sockerr)
    {
        //连接被对方断开
       RTMP_CloseNoSendAnyData(r);
    }
    else
     {
                  RTMP_Close(r);
    }
      n = 1;
      break;
    }
 
      if (nBytes == 0)
    break;
 
      n -= nBytes;
      ptr += nBytes;
    }
 
#ifdef CRYPTO
  if (encrypted && encrypted != buf)
    free(encrypted);
#endif
 
  return n == 0;
}

//새 RTMP_CloseNoSendAnyData 함수

void
RTMP_CloseNoSendAnyData(RTMP *r)
{
  int i;

  if (RTMP_IsConnected(r))
    {
      if (r->m_stream_id > 0)
        {
      if (r->m_clientID.av_val)
        {
      HTTP_Post(r, RTMPT_CLOSE, "", 1);
      free(r->m_clientID.av_val);
      r->m_clientID.av_val = NULL;
      r->m_clientID.av_len = 0;
    }
      RTMPSockBuf_Close(&r->m_sb);
    }

  r->m_stream_id = -1;
  r->m_sb.sb_socket = -1;
  r->m_nBWCheckCounter = 0;
  r->m_nBytesIn = 0;
  r->m_nBytesInSent = 0;

  if (r->m_read.flags & RTMP_READ_HEADER) {
    free(r->m_read.buf);
    r->m_read.buf = NULL;
  }
  r->m_read.dataType = 0;
  r->m_read.flags = 0;
  r->m_read.status = 0;
  r->m_read.nResumeTS = 0;
  r->m_read.nIgnoredFrameCounter = 0;
  r->m_read.nIgnoredFlvFrameCounter = 0;

  r->m_write.m_nBytesRead = 0;
  RTMPPacket_Free(&r->m_write);

  for (i = 0; i < RTMP_CHANNELS; i++)
    {
      if (r->m_vecChannelsIn[i])
    {
      RTMPPacket_Free(r->m_vecChannelsIn[i]);
      free(r->m_vecChannelsIn[i]);
      r->m_vecChannelsIn[i] = NULL;
    }
      if (r->m_vecChannelsOut[i])
    {
      free(r->m_vecChannelsOut[i]);
      r->m_vecChannelsOut[i] = NULL;
    }
    }
  AV_clear(r->m_methodCalls, r->m_numCalls);
  r->m_methodCalls = NULL;
  r->m_numCalls = 0;
  r->m_numInvokes = 0;

  r->m_bPlaying = FALSE;
  r->m_sb.sb_size = 0;

  r->m_msgCounter = 0;
  r->m_resplen = 0;
  r->m_unackd = 0;

  free(r->Link.playpath0.av_val);
  r->Link.playpath0.av_val = NULL;

  if (r->Link.lFlags & RTMP_LF_FTCU)
    {
      free(r->Link.tcUrl.av_val);
      r->Link.tcUrl.av_val = NULL;
      r->Link.lFlags ^= RTMP_LF_FTCU;
    }

#ifdef CRYPTO
  if (r->Link.dh)
    {
      MDH_free(r->Link.dh);
      r->Link.dh = NULL;
    }
  if (r->Link.rc4keyIn)
    {
      RC4_free(r->Link.rc4keyIn);
      r->Link.rc4keyIn = NULL;
    }
  if (r->Link.rc4keyOut)
    {
      RC4_free(r->Link.rc4keyOut);
      r->Link.rc4keyOut = NULL;
    }
#endif
}

Guess you like

Origin blog.csdn.net/byxdaz/article/details/128872896