Files
ANSCORE/MediaClient/http/http_flv_cln.cpp

1450 lines
34 KiB
C++

/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "http_flv_cln.h"
#include "h264.h"
#include "h265.h"
#include "media_format.h"
#include "http_cln.h"
#include "http_parse.h"
void * http_flv_rx_thread(void * argv)
{
CHttpFlvClient * pRtmpClient = (CHttpFlvClient *) argv;
pRtmpClient->rx_thread();
return NULL;
}
CHttpFlvClient::CHttpFlvClient()
: m_bRunning(FALSE)
, m_bVideoReady(FALSE)
, m_bAudioReady(FALSE)
, m_bChunked(FALSE)
, m_bFlvHeader(FALSE)
, m_tidRx(0)
, m_nVpsLen(0)
, m_nSpsLen(0)
, m_nPpsLen(0)
, m_nAudioConfigLen(0)
, m_nVideoCodec(VIDEO_CODEC_NONE)
, m_nWidth(0)
, m_nHeight(0)
, m_nFrameRate(0)
, m_nAudioCodec(AUDIO_CODEC_NONE)
, m_nSamplerate(44100)
, m_nChannels(2)
, m_nVideoInitTS(0)
, m_nAudioInitTS(0)
, m_pNotify(NULL)
, m_pUserdata(NULL)
, m_pVideoCB(NULL)
, m_pAudioCB(NULL)
, m_nRxTimeout(10)
{
memset(m_url, 0, sizeof(m_url));
memset(m_user, 0, sizeof(m_user));
memset(m_pass, 0, sizeof(m_pass));
memset(&m_pVps, 0, sizeof(m_pVps));
memset(&m_pSps, 0, sizeof(m_pVps));
memset(&m_pPps, 0, sizeof(m_pVps));
memset(&m_pAudioConfig, 0, sizeof(m_pAudioConfig));
memset(&m_http, 0, sizeof(HTTPREQ));
m_pMutex = sys_os_create_mutex();
}
CHttpFlvClient::~CHttpFlvClient()
{
http_flv_close();
if (m_pMutex)
{
sys_os_destroy_sig_mutex(m_pMutex);
m_pMutex = NULL;
}
}
BOOL CHttpFlvClient::http_flv_start(const char * url, const char * user, const char * pass)
{
if (url && url[0] != '\0')
{
strncpy(m_url, url, sizeof(m_url)-1);
}
if (user && user[0] != '\0')
{
strncpy(m_user, user, sizeof(m_user)-1);
}
if (pass && pass[0] != '\0')
{
strncpy(m_pass, pass, sizeof(m_pass)-1);
}
m_bRunning = TRUE;
m_tidRx = sys_os_create_thread((void *)http_flv_rx_thread, this);
return TRUE;
}
BOOL CHttpFlvClient::http_flv_play()
{
return FALSE;
}
BOOL CHttpFlvClient::http_flv_stop()
{
return http_flv_close();
}
BOOL CHttpFlvClient::http_flv_pause()
{
return FALSE;
}
BOOL CHttpFlvClient::http_flv_close()
{
sys_os_mutex_enter(m_pMutex);
m_pAudioCB = NULL;
m_pVideoCB = NULL;
m_pNotify = NULL;
m_pUserdata = NULL;
sys_os_mutex_leave(m_pMutex);
#ifdef HTTPS
if (m_http.https && m_http.ssl)
{
SSL_shutdown(m_http.ssl);
}
#endif
if (m_http.cfd > 0)
{
closesocket(m_http.cfd);
m_http.cfd = 0;
}
m_bRunning = FALSE;
while (m_tidRx)
{
usleep(10*1000);
}
http_cln_free_req(&m_http);
m_bVideoReady = FALSE;
m_bAudioReady = FALSE;
return TRUE;
}
BOOL CHttpFlvClient::http_flv_req(HTTPREQ * p_http)
{
int slen;
int offset = 0;
char bufs[2048] = {'\0'};
int buflen = sizeof(bufs);
offset += snprintf(bufs+offset, buflen-offset, "GET %s HTTP/1.1\r\n", p_http->url);
offset += snprintf(bufs+offset, buflen-offset, "Host: %s:%u\r\n", p_http->host, p_http->port);
offset += snprintf(bufs+offset, buflen-offset, "Accept: */*\r\n");
offset += snprintf(bufs+offset, buflen-offset, "User-Agent: Happytime http-flv 1.0\r\n");
if (p_http->need_auth)
{
offset += http_build_auth_msg(p_http, "GET", bufs+offset, buflen-offset);
}
offset += snprintf(bufs+offset, buflen-offset, "Connection: keep-alive\r\n\r\n");
log_print(HT_LOG_DBG, "TX >> %s\r\n\r\n", bufs);
slen = http_cln_tx(p_http, bufs, offset);
return (slen == offset) ? TRUE : FALSE;
}
BOOL CHttpFlvClient::http_flv_connect()
{
http_flv_send_notify(HTTP_FLV_EVE_CONNECTING);
memset(&m_http, 0, sizeof(HTTPREQ));
int port;
char proto[32], username[100], password[100], host[100], path[200];
url_split(m_url, proto, sizeof(proto), username, sizeof(username),
password, sizeof(password), host, sizeof(host), &port, path, sizeof(path));
if (strcasecmp(proto, "https") == 0)
{
m_http.https = 1;
}
else if (strcasecmp(proto, "http") != 0)
{
log_print(HT_LOG_ERR, "%s, invalid url %s\r\n", __FUNCTION__, m_url);
return FALSE;
}
if (host[0] == '\0')
{
log_print(HT_LOG_ERR, "%s, invalid url %s\r\n", __FUNCTION__, m_url);
return FALSE;
}
if (port == -1)
{
if (m_http.https)
{
port = 443;
}
else
{
port = 80;
}
}
strcpy(m_http.host, host);
m_http.port = port;
strcpy(m_http.url, path);
int timeout = 10*1000;
m_http.cfd = tcp_connect_timeout(get_address_by_name(host), port, timeout);
if (m_http.cfd <= 0)
{
log_print(HT_LOG_ERR, "%s, connect failed. %s\r\n", __FUNCTION__, m_url);
return FALSE;
}
if (m_http.https)
{
#ifdef HTTPS
if (!http_cln_ssl_conn(&m_http, timeout))
{
http_cln_free_req(&m_http);
return FALSE;
}
#else
http_cln_free_req(&m_http);
log_print(HT_LOG_ERR, "%s, the server require ssl connection, unsupport!\r\n", __FUNCTION__);
return FALSE;
#endif
}
if (!http_flv_req(&m_http))
{
http_cln_free_req(&m_http);
log_print(HT_LOG_ERR, "%s, send request failed.\r\n", __FUNCTION__);
return FALSE;
}
return TRUE;
}
int CHttpFlvClient::http_flv_h264_rx(uint8 * data, uint32 size, uint32 ts)
{
if (data[1] == 0x00) // AVC sequence header
{
if (size < 15)
{
log_print(HT_LOG_WARN, "%s, dataLen=%d\r\n", __FUNCTION__, size);
return 0;
}
uint32 offset = 10;
int spsnum = (data[offset++] & 0x1f);
int i = 0;
while (i < spsnum)
{
uint32 spslen = (data[offset] & 0x000000FF) << 8 | (data[offset + 1] & 0x000000FF);
offset += 2;
if (offset + spslen > size)
{
return -1;
}
if (0 == m_nSpsLen && spslen <= sizeof(m_pSps) + 4)
{
m_pSps[0] = 0;
m_pSps[1] = 0;
m_pSps[2] = 0;
m_pSps[3] = 1;
m_nSpsLen = spslen + 4;
memcpy(m_pSps + 4, data + offset, spslen);
}
offset += spslen;
i++;
}
int ppsnum = (data[offset++] & 0x1f);
i = 0;
while (i < ppsnum)
{
uint32 ppslen = (data[offset] & 0x000000FF) << 8 | (data[offset + 1] & 0x000000FF);
offset += 2;
if (offset + ppslen > size)
{
return -1;
}
if (0 == m_nPpsLen && ppslen <= sizeof(m_pPps) + 4)
{
m_pPps[0] = 0;
m_pPps[1] = 0;
m_pPps[2] = 0;
m_pPps[3] = 1;
m_nPpsLen = ppslen + 4;
memcpy(m_pPps + 4, data + offset, ppslen);
}
offset += ppslen;
i++;
}
if (!m_bVideoReady)
{
m_bVideoReady = TRUE;
http_flv_send_notify(HTTP_FLV_EVE_VIDEOREADY);
}
if (m_nSpsLen > 0 && m_nPpsLen > 0)
{
http_flv_video_data_cb(m_pSps, m_nSpsLen, 0);
http_flv_video_data_cb(m_pPps, m_nPpsLen, 0);
}
}
else if (data[1] == 0x01) // AVC NALU
{
if (size < 10)
{
log_print(HT_LOG_WARN, "%s, dataLen=%d\r\n", __FUNCTION__, size);
return 0;
}
int len = 0;
int num = 5;
while (num < (int)size)
{
len = (data[num] & 0x000000FF) << 24 | (data[num + 1] & 0x000000FF) << 16 |
(data[num + 2] & 0x000000FF) << 8 | (data[num + 3] & 0x000000FF);
num = num + 4;
data[num-4]= 0;
data[num-3]= 0;
data[num-2]= 0;
data[num-1]= 1;
http_flv_video_data_cb(data + num - 4, len + 4, ts);
num = num + len;
}
}
else
{
log_print(HT_LOG_WARN, "%s, tag type [%02x]!!!\r\n", __FUNCTION__, data[1]);
}
return 0;
}
int CHttpFlvClient::http_flv_h265_rx(uint8 * data, uint32 size, uint32 ts)
{
if (data[1] == 0x00) // AVC sequence header
{
if (size < 32)
{
log_print(HT_LOG_WARN, "%s, dataLen=%d\r\n", __FUNCTION__, size);
return 0;
}
uint32 offset = 5 + 22;
int numOfArrays = data[offset++];
for (int i=0; i<numOfArrays; i++)
{
int naluType = data[offset++];
int numNalus = (data[offset] & 0x000000FF) << 8 | (data[offset + 1] & 0x000000FF);
offset += 2;
for (int j = 0; j < numNalus; j++)
{
uint32 naluLen = (data[offset] & 0x000000FF) << 8 | (data[offset + 1] & 0x000000FF);
offset += 2;
if (offset + naluLen > size)
{
return -1;
}
if (naluType == HEVC_NAL_VPS && 0 == m_nVpsLen && naluLen <= sizeof(m_pVps) + 4)
{
m_pVps[0] = 0;
m_pVps[1] = 0;
m_pVps[2] = 0;
m_pVps[3] = 1;
memcpy(m_pVps+4, data+offset, naluLen);
m_nVpsLen = naluLen + 4;
}
else if (naluType == HEVC_NAL_SPS && 0 == m_nSpsLen && naluLen <= sizeof(m_pSps) + 4)
{
m_pSps[0] = 0;
m_pSps[1] = 0;
m_pSps[2] = 0;
m_pSps[3] = 1;
memcpy(m_pSps+4, data+offset, naluLen);
m_nSpsLen = naluLen + 4;
}
else if (naluType == HEVC_NAL_PPS && 0 == m_nPpsLen && naluLen <= sizeof(m_pPps) + 4)
{
m_pPps[0] = 0;
m_pPps[1] = 0;
m_pPps[2] = 0;
m_pPps[3] = 1;
memcpy(m_pPps+4, data+offset, naluLen);
m_nPpsLen = naluLen + 4;
}
offset += naluLen;
}
}
if (!m_bVideoReady)
{
m_bVideoReady = TRUE;
http_flv_send_notify(HTTP_FLV_EVE_VIDEOREADY);
}
if (m_nVpsLen > 0 && m_nSpsLen > 0 && m_nPpsLen > 0)
{
http_flv_video_data_cb(m_pVps, m_nVpsLen, 0);
http_flv_video_data_cb(m_pSps, m_nSpsLen, 0);
http_flv_video_data_cb(m_pPps, m_nPpsLen, 0);
}
}
else if (data[1] == 0x01) // AVC NALU
{
if (size < 10)
{
log_print(HT_LOG_WARN, "%s, dataLen=%d\r\n", __FUNCTION__, size);
return 0;
}
int len = 0;
int num = 5;
while (num < (int)size)
{
len = (data[num] & 0x000000FF) << 24 | (data[num + 1] & 0x000000FF) << 16 |
(data[num + 2] & 0x000000FF) << 8 | (data[num + 3] & 0x000000FF);
num = num + 4;
data[num-4]= 0;
data[num-3]= 0;
data[num-2]= 0;
data[num-1]= 1;
http_flv_video_data_cb(data + num - 4, len + 4, ts);
num = num + len;
}
}
else
{
log_print(HT_LOG_WARN, "%s, tag type [%02x]!!!\r\n", __FUNCTION__, data[1]);
}
return 0;
}
int CHttpFlvClient::http_flv_video_rx(uint8 * data, uint32 size, uint32 ts)
{
int ret = 0;
uint8 v_type = data[0];
v_type = (v_type & 0x0f);
if (v_type == 2) // Sorenson H.263
{
}
else if (v_type == 3) // Screen video
{
}
else if (v_type == 4) // On2 VP6
{
}
else if (v_type == 5) // On2 VP6 with alpha channel
{
}
else if (v_type == 6) // Screen video version 2
{
}
else if (v_type == 7) // AVC
{
m_nVideoCodec = VIDEO_CODEC_H264;
ret = http_flv_h264_rx(data, size, ts);
}
else if (v_type == 12) // H265
{
m_nVideoCodec = VIDEO_CODEC_H265;
ret = http_flv_h265_rx(data, size, ts);
}
return ret;
}
int CHttpFlvClient::http_flv_g711_rx(uint8 * data, uint32 size, uint32 ts)
{
if (!m_bAudioReady)
{
m_nChannels = ((data[0] & 0x01) ? 2 : 1);
m_nSamplerate = 8000;
m_bAudioReady = TRUE;
http_flv_send_notify(HTTP_FLV_EVE_AUDIOREADY);
}
http_flv_audio_data_cb(data + 1, size - 1, ts);
return 0;
}
int CHttpFlvClient::http_flv_aac_rx(uint8 * data, uint32 size, uint32 ts)
{
if (size < 4)
{
return -1;
}
// AAC Packet Type: 0x00 - AAC Sequence Header; 0x01 - AAC Raw
if (data[1] == 0x00)
{
int nAudioSampleType;
log_print(HT_LOG_DBG, "%s, len[%u] data[%02X %02X %02X %02X]\r\n",
__FUNCTION__, size, data[0], data[1], data[2], data[3]);
memcpy(m_pAudioConfig, data+2, 2);
m_nAudioConfigLen = 2;
m_nChannels = ((data[0] & 0x01) ? 2 : 1); // Whether stereo (0: Mono, 1: Stereo)
nAudioSampleType = (data[0] & 0x0c) >> 2; // Audio sample rate (0: 5.5kHz, 1:11KHz, 2:22 kHz, 3:44 kHz)
switch (nAudioSampleType)
{
case 0:
m_nSamplerate = 5500;
break;
case 1:
m_nSamplerate = 11025;
break;
case 2:
m_nSamplerate = 22050;
break;
case 3:
m_nSamplerate = 44100;
break;
default:
m_nSamplerate = 44100;
break;
}
uint16 audioSpecificConfig = 0;
audioSpecificConfig = (data[2] & 0xff) << 8;
audioSpecificConfig += 0x00ff & data[3];
uint8 nSampleFrequencyIndex = (audioSpecificConfig & 0x0780) >> 7;
uint8 nChannels = (audioSpecificConfig & 0x78) >> 3;
m_nChannels = nChannels;
switch (nSampleFrequencyIndex)
{
case 0:
m_nSamplerate = 96000;
break;
case 1:
m_nSamplerate = 88200;
break;
case 2:
m_nSamplerate = 64000;
break;
case 3:
m_nSamplerate = 48000;
break;
case 4:
m_nSamplerate = 44100;
break;
case 5:
m_nSamplerate = 32000;
break;
case 6:
m_nSamplerate = 24000;
break;
case 7:
m_nSamplerate = 22050;
break;
case 8:
m_nSamplerate = 16000;
break;
case 9:
m_nSamplerate = 12000;
break;
case 10:
m_nSamplerate = 11025;
break;
case 11:
m_nSamplerate = 8000;
break;
case 12:
m_nSamplerate = 7350;
break;
default:
m_nSamplerate = 44100;
break;
}
if (!m_bAudioReady)
{
m_bAudioReady = TRUE;
http_flv_send_notify(HTTP_FLV_EVE_AUDIOREADY);
}
}
else if (data[1] == 0x01)
{
http_flv_audio_data_cb(data + 2, size - 2, ts);
}
return 0;
}
int CHttpFlvClient::http_flv_audio_rx(uint8 * data, uint32 size, uint32 ts)
{
int ret = 0;
uint8 a_type = data[0];
a_type = a_type >> 4;
if (a_type == 0) // Linear PCM, platform endian
{
}
else if (a_type == 1) // ADPCM
{
}
else if (a_type == 2) // MP3
{
}
else if (a_type == 3) // Linear PCM, little endian
{
}
else if (a_type == 7) // G711A
{
m_nAudioCodec = AUDIO_CODEC_G711A;
ret = http_flv_g711_rx(data, size, ts);
}
else if (a_type == 8) // G711U
{
m_nAudioCodec = AUDIO_CODEC_G711U;
ret = http_flv_g711_rx(data, size, ts);
}
else if (a_type == 10) // AAC
{
m_nAudioCodec = AUDIO_CODEC_AAC;
ret = http_flv_aac_rx(data, size, ts);
}
else if (a_type == 11) // Speex
{
}
else if (a_type == 14) // MP3 8-Khz
{
}
return ret;
}
int CHttpFlvClient::http_flv_metadata_rx(uint8 * data, uint32 size)
{
AMFObject obj;
AVal val;
AMFObjectProperty * property;
AMFObject subObject;
int nRes = AMF_Decode(&obj, (char*)data, size, FALSE);
if (nRes < 0)
{
log_print(HT_LOG_ERR, "%s, error decoding invoke packet\r\n", __FUNCTION__);
return -1;
}
AMF_Dump(&obj);
for (int n = 0; n < obj.o_num; n++)
{
property = AMF_GetProp(&obj, NULL, n);
if (NULL == property)
{
continue;
}
if (property->p_type == AMF_OBJECT || property->p_type == AMF_ECMA_ARRAY)
{
AMFProp_GetObject(property, &subObject);
for (int m = 0; m < subObject.o_num; m++)
{
property = AMF_GetProp(&subObject, NULL, m);
if (NULL == property)
{
continue;
}
if (property->p_type == AMF_OBJECT)
{
}
else if (property->p_type == AMF_BOOLEAN)
{
int bVal = AMFProp_GetBoolean(property);
if (strncmp("stereo", property->p_name.av_val, property->p_name.av_len) == 0)
{
m_nChannels = bVal > 0 ? 2 : 1;
}
}
else if (property->p_type == AMF_NUMBER)
{
double dVal = AMFProp_GetNumber(property);
if (strncmp("width", property->p_name.av_val, property->p_name.av_len) == 0)
{
m_nWidth = (int)dVal;
}
else if (strcasecmp("height", property->p_name.av_val) == 0)
{
m_nHeight = (int)dVal;
}
else if (strcasecmp("framerate", property->p_name.av_val) == 0)
{
m_nFrameRate = dVal;
}
else if (strcasecmp("videocodecid", property->p_name.av_val) == 0)
{
switch ((int)dVal)
{
case 7:
m_nVideoCodec = VIDEO_CODEC_H264;
break;
case 12:
m_nVideoCodec = VIDEO_CODEC_H265;
break;
}
}
else if (strcasecmp("audiosamplerate", property->p_name.av_val) == 0)
{
m_nSamplerate = (int)dVal;
}
else if (strcasecmp("audiosamplesize", property->p_name.av_val) == 0)
{
}
else if (strcasecmp("audiocodecid", property->p_name.av_val) == 0)
{
switch ((int)dVal)
{
case 7:
m_nAudioCodec = AUDIO_CODEC_G711A;
break;
case 8:
m_nAudioCodec = AUDIO_CODEC_G711U;
break;
case 10:
m_nAudioCodec = AUDIO_CODEC_AAC;
break;
}
}
else if (strcasecmp("filesize", property->p_name.av_val) == 0)
{
}
}
else if (property->p_type == AMF_STRING)
{
AMFProp_GetString(property, &val);
}
}
}
else
{
AMFProp_GetString(property, &val);
}
}
AMF_Reset(&obj);
if (m_nVideoCodec != VIDEO_CODEC_NONE && !m_bVideoReady)
{
m_bVideoReady = TRUE;
http_flv_send_notify(HTTP_FLV_EVE_VIDEOREADY);
}
if (m_nAudioCodec != AUDIO_CODEC_NONE && !m_bAudioReady)
{
m_bAudioReady = TRUE;
http_flv_send_notify(HTTP_FLV_EVE_AUDIOREADY);
}
return 0;
}
BOOL CHttpFlvClient::http_flv_res_rx(HTTPREQ * p_http)
{
if (http_is_http_msg(p_http->rbuf) == FALSE)
{
log_print(HT_LOG_ERR, "%s, invalid http msg\r\n", __FUNCTION__);
return FALSE;
}
int http_pkt_len = http_pkt_find_end(p_http->rbuf);
if (http_pkt_len == 0)
{
return TRUE;
}
p_http->hdr_len = http_pkt_len;
HTTPMSG * rx_msg = http_get_msg_buf(http_pkt_len + 1);
if (rx_msg == NULL)
{
log_print(HT_LOG_ERR, "%s, get_msg_buf ret null!!!\r\n", __FUNCTION__);
return FALSE;
}
memcpy(rx_msg->msg_buf, p_http->rbuf, http_pkt_len);
rx_msg->msg_buf[http_pkt_len] = '\0';
log_print(HT_LOG_DBG, "RX from %s << %s\r\n", p_http->host, rx_msg->msg_buf);
int parse_len = http_msg_parse_part1(rx_msg->msg_buf, http_pkt_len, rx_msg);
if (parse_len != http_pkt_len)
{
log_print(HT_LOG_ERR, "%s, http_msg_parse_part1=%d, http_pkt_len=%d!!!\r\n",
__FUNCTION__, parse_len, http_pkt_len);
http_free_msg(rx_msg);
return FALSE;
}
if (rx_msg->msg_sub_type != 200)
{
http_free_msg(rx_msg);
return FALSE;
}
if (rx_msg->ctt_type != CTT_FLV)
{
http_free_msg(rx_msg);
return FALSE;
}
char * p_encoding = http_get_headline(rx_msg, "Transfer-Encoding");
if (p_encoding)
{
if (strcasecmp(p_encoding, "chunked") == 0)
{
m_bChunked = TRUE;
}
}
http_free_msg(rx_msg);
p_http->rcv_dlen -= p_http->hdr_len;
if (p_http->rcv_dlen > 0)
{
memmove(p_http->rbuf, p_http->rbuf+p_http->hdr_len, p_http->rcv_dlen);
}
else
{
if (p_http->dyn_recv_buf)
{
free(p_http->dyn_recv_buf);
p_http->dyn_recv_buf = NULL;
}
p_http->rbuf = NULL;
p_http->rcv_dlen = 0;
p_http->mlen = 0;
}
return TRUE;
}
BOOL CHttpFlvClient::http_flv_data_handler(HTTPREQ * p_http)
{
// At least received FLV TAG header length
if (p_http->rcv_dlen < 11)
{
return TRUE;
}
uint8 * p = (uint8 *)p_http->rbuf;
uint32 size = (uint32)((p[1] << 16) | (p[2] << 8) | p[3]);
// Tag header length + data size + PreviousTagSize + (chunked '\r\n')
int tlen = 11 + size + 4 + (m_bChunked ? 2 : 0);
if (p_http->rcv_dlen < tlen)
{
// The receive buffer is not enough, allocate memory
if (p_http->mlen < tlen)
{
char * old_recv_buf = p_http->dyn_recv_buf;
p_http->dyn_recv_buf = (char *)malloc(tlen);
if (NULL == p_http->dyn_recv_buf)
{
if (old_recv_buf)
{
free(old_recv_buf);
}
return FALSE;
}
else
{
memmove(p_http->dyn_recv_buf, p_http->rbuf, p_http->rcv_dlen);
p_http->mlen = tlen;
p_http->rbuf = p_http->dyn_recv_buf;
// Free the old receive buffer
if (old_recv_buf)
{
free(old_recv_buf);
}
}
}
}
else
{
int ret = 0;
int type = p[0];
uint32 ts = (uint32)((p[4] << 16) | (p[5] << 8) | p[6] | (p[7] << 24));
if (8 == type)
{
ret = http_flv_audio_rx((uint8 *)p+11, size, ts);
}
else if (9 == type)
{
ret = http_flv_video_rx((uint8 *)p+11, size, ts);
}
else if (18 == type)
{
ret = http_flv_metadata_rx((uint8 *)p+11, size);
}
p_http->rcv_dlen -= tlen;
if (p_http->rcv_dlen > 0)
{
memmove(p_http->rbuf, p_http->rbuf+tlen, p_http->rcv_dlen);
}
else
{
if (p_http->dyn_recv_buf)
{
free(p_http->dyn_recv_buf);
p_http->dyn_recv_buf = NULL;
}
p_http->rbuf = NULL;
p_http->rcv_dlen = 0;
p_http->mlen = 0;
}
if (ret < 0)
{
return FALSE;
}
}
return TRUE;
}
BOOL CHttpFlvClient::http_flv_header_rx(HTTPREQ * p_http)
{
if (p_http->rbuf[0] != 'F' || p_http->rbuf[1] != 'L' || p_http->rbuf[2] != 'V')
{
return FALSE;
}
m_bFlvHeader = TRUE;
return TRUE;
}
BOOL CHttpFlvClient::http_flv_chunked(HTTPREQ * p_http)
{
int i = 0;
char buff[32] = {'\0'};
char * p = p_http->rbuf;
// Get the data length of the hexadecimal string
while (i < p_http->rcv_dlen - 1)
{
if (p[i] == '\r' && p[i+1] == '\n')
{
i += 2;
break;
}
else if (i < 31)
{
buff[i] = p[i];
}
++i;
}
// Need more data
if (i >= p_http->rcv_dlen - 1)
{
return FALSE;
}
int len = strtol(buff, NULL, 16); // data length
int tlen = len + i + 2; // Data length + length itself +'\r\n'
if (p_http->rcv_dlen < tlen)
{
// The receive buffer is not enough, allocate memory
if (p_http->mlen < tlen)
{
char * old_recv_buf = p_http->dyn_recv_buf;
p_http->dyn_recv_buf = (char *)malloc(tlen);
if (NULL == p_http->dyn_recv_buf)
{
// What should do if the allocation fails?
// Free the old receive buffer
if (old_recv_buf)
{
free(old_recv_buf);
}
return FALSE;
}
else
{
memmove(p_http->dyn_recv_buf, p_http->rbuf, p_http->rcv_dlen);
p_http->mlen = tlen;
p_http->rbuf = p_http->dyn_recv_buf;
// Free the old receive buffer
if (old_recv_buf)
{
free(old_recv_buf);
}
}
}
// Need more data
return FALSE;
}
// skip length
p_http->mlen -= i;
p_http->rcv_dlen -= i;
p_http->rbuf += i;
return TRUE;
}
BOOL CHttpFlvClient::http_flv_data_rx(HTTPREQ * p_http)
{
if (m_bChunked)
{
if (!http_flv_chunked(p_http))
{
return TRUE;
}
}
if (!m_bFlvHeader)
{
// Receive FLV header
int headsize = 13 + (m_bChunked ? 2 : 0);
if (p_http->rcv_dlen >= headsize)
{
if (!http_flv_header_rx(p_http))
{
return FALSE;
}
else
{
// skip flv header
p_http->mlen -= headsize;
p_http->rcv_dlen -= headsize;
p_http->rbuf += headsize;
if (m_bChunked)
{
if (!http_flv_chunked(p_http))
{
return TRUE;
}
}
}
}
else
{
// Need more data
return TRUE;
}
}
if (m_bFlvHeader)
{
return http_flv_data_handler(p_http);
}
return TRUE;
}
int CHttpFlvClient::http_flv_rx(HTTPREQ * p_http)
{
int rlen;
int sret;
fd_set fdread;
struct timeval tv = {1, 0};
FD_ZERO(&fdread);
FD_SET(p_http->cfd, &fdread);
#ifdef HTTPS
if (p_http->https && SSL_pending(p_http->ssl) > 0)
{
// There is data to read
}
else
#endif
{
sret = select((int)(p_http->cfd+1), &fdread, NULL, NULL, &tv);
if (sret == 0) // Time expired
{
return HTTP_FLV_RX_TIMEOUT;
}
else if (!FD_ISSET(p_http->cfd, &fdread))
{
return HTTP_FLV_RX_TIMEOUT;
}
}
if (p_http->rbuf == NULL)
{
p_http->rbuf = p_http->rcv_buf;
p_http->mlen = sizeof(p_http->rcv_buf)-4;
p_http->rcv_dlen = 0;
}
#ifdef HTTPS
if (p_http->https)
{
rlen = SSL_read(p_http->ssl, p_http->rbuf+p_http->rcv_dlen, p_http->mlen-p_http->rcv_dlen);
}
else
#endif
rlen = recv(p_http->cfd, p_http->rbuf+p_http->rcv_dlen, p_http->mlen-p_http->rcv_dlen, 0);
if (rlen <= 0)
{
log_print(HT_LOG_INFO, "%s, recv return = %d, dlen[%d], mlen[%d]\r\n",
__FUNCTION__, rlen, p_http->rcv_dlen, p_http->mlen);
return HTTP_FLV_RX_FAIL;
}
p_http->rcv_dlen += rlen;
if (p_http->rcv_dlen < 16)
{
return HTTP_FLV_RX_SUCC;
}
if (p_http->hdr_len == 0)
{
// Receive GET request response
if (!http_flv_res_rx(p_http))
{
return HTTP_FLV_RX_FAIL;
}
else if (p_http->hdr_len > 0 && p_http->rcv_dlen > 0)
{
// There is still data that has not been processed
if (!http_flv_data_rx(p_http))
{
return HTTP_FLV_RX_FAIL;
}
}
}
else if (!http_flv_data_rx(p_http))
{
return HTTP_FLV_RX_FAIL;
}
// Need to receive more data
return HTTP_FLV_RX_SUCC;
}
void CHttpFlvClient::rx_thread()
{
int ret = 0;
int tm_count = 0;
BOOL nodata_notify = FALSE;
if (!http_flv_connect())
{
m_tidRx = 0;
http_flv_send_notify(HTTP_FLV_EVE_CONNFAIL);
return;
}
while (m_bRunning)
{
ret = http_flv_rx(&m_http);
if (ret == HTTP_FLV_RX_FAIL)
{
break;
}
else
{
if (ret == HTTP_FLV_RX_TIMEOUT)
{
tm_count++;
if (tm_count >= m_nRxTimeout && !nodata_notify) // in 10s without data
{
nodata_notify = TRUE;
http_flv_send_notify(HTTP_FLV_EVE_NODATA);
}
}
else // should be HTTP_FLV_RX_SUCC
{
if (nodata_notify)
{
nodata_notify = FALSE;
http_flv_send_notify(HTTP_FLV_EVE_RESUME);
}
tm_count = 0;
}
}
}
m_tidRx = 0;
http_flv_send_notify(HTTP_FLV_EVE_STOPPED);
log_print(HT_LOG_INFO, "%s, exit\r\n", __FUNCTION__);
}
void CHttpFlvClient::http_flv_send_notify(int event)
{
sys_os_mutex_enter(m_pMutex);
if (m_pNotify)
{
m_pNotify(event, m_pUserdata);
}
sys_os_mutex_leave(m_pMutex);
}
void CHttpFlvClient::http_flv_video_data_cb(uint8 * p_data, int len, uint32 ts)
{
if (m_nVideoInitTS == 0 && ts != 0)
{
m_nVideoInitTS = ts;
}
sys_os_mutex_enter(m_pMutex);
if (m_pVideoCB)
{
m_pVideoCB(p_data, len, ts, m_pUserdata);
}
sys_os_mutex_leave(m_pMutex);
}
void CHttpFlvClient::http_flv_audio_data_cb(uint8 * p_data, int len, uint32 ts)
{
if (m_nAudioInitTS == 0 && ts != 0)
{
m_nAudioInitTS = ts;
}
sys_os_mutex_enter(m_pMutex);
if (m_pAudioCB)
{
m_pAudioCB(p_data, len, ts, m_pUserdata);
}
sys_os_mutex_leave(m_pMutex);
}
void CHttpFlvClient::get_h264_params()
{
if (m_nSpsLen > 0)
{
http_flv_video_data_cb(m_pSps, m_nSpsLen, 0);
}
if (m_nPpsLen > 0)
{
http_flv_video_data_cb(m_pPps, m_nPpsLen, 0);
}
}
BOOL CHttpFlvClient::get_h264_params(uint8 * p_sps, int * sps_len, uint8 * p_pps, int * pps_len)
{
if (m_nSpsLen > 0)
{
*sps_len = m_nSpsLen;
memcpy(p_sps, m_pSps, m_nSpsLen);
}
if (m_nPpsLen > 0)
{
*pps_len = m_nPpsLen;
memcpy(p_pps, m_pPps, m_nPpsLen);
}
return TRUE;
}
void CHttpFlvClient::get_h265_params()
{
if (m_nVpsLen > 0)
{
http_flv_video_data_cb(m_pVps, m_nVpsLen, 0);
}
if (m_nSpsLen > 0)
{
http_flv_video_data_cb(m_pSps, m_nSpsLen, 0);
}
if (m_nPpsLen > 0)
{
http_flv_video_data_cb(m_pPps, m_nPpsLen, 0);
}
}
BOOL CHttpFlvClient::get_h265_params(uint8 * p_sps, int * sps_len, uint8 * p_pps, int * pps_len, uint8 * p_vps, int * vps_len)
{
if (m_nVpsLen > 0)
{
*vps_len = m_nVpsLen;
memcpy(p_vps, m_pVps, m_nVpsLen);
}
if (m_nSpsLen > 0)
{
*sps_len = m_nSpsLen;
memcpy(p_sps, m_pSps, m_nSpsLen);
}
if (m_nPpsLen > 0)
{
*pps_len = m_nPpsLen;
memcpy(p_pps, m_pPps, m_nPpsLen);
}
return TRUE;
}
void CHttpFlvClient::set_notify_cb(http_flv_notify_cb notify, void * userdata)
{
sys_os_mutex_enter(m_pMutex);
m_pNotify = notify;
m_pUserdata = userdata;
sys_os_mutex_leave(m_pMutex);
}
void CHttpFlvClient::set_video_cb(http_flv_video_cb cb)
{
sys_os_mutex_enter(m_pMutex);
m_pVideoCB = cb;
sys_os_mutex_leave(m_pMutex);
}
void CHttpFlvClient::set_audio_cb(http_flv_audio_cb cb)
{
sys_os_mutex_enter(m_pMutex);
m_pAudioCB = cb;
sys_os_mutex_leave(m_pMutex);
}
void CHttpFlvClient::set_rx_timeout(int timeout)
{
m_nRxTimeout = timeout;
}