/*************************************************************************************** * * 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 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; }