Files
gb28181/GB28181Device/media/media_proxy.cpp
2024-12-15 20:42:32 +08:00

1807 lines
41 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-2022, 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 "media_proxy.h"
#include "media_format.h"
#include "h264.h"
#include "h265.h"
#include "h264_util.h"
#include "h265_util.h"
#include "media_util.h"
#include "media_parse.h"
#include "mjpeg.h"
#include "base64.h"
#include "http_test.h"
#include <math.h>
#ifdef MEDIA_PROXY
/***************************************************************************************/
MEDIA_PRY * g_proxy = NULL;
/***************************************************************************************/
int event_notify_cb(int evt, void * puser)
{
CMediaProxy * p_proxy = (CMediaProxy *) puser;
p_proxy->onNotify(evt);
return 0;
}
int rtsp_audio_cb(uint8 * pdata, int len, uint32 ts, uint16 seq, void *puser)
{
CMediaProxy * p_proxy = (CMediaProxy *) puser;
p_proxy->onAudio(pdata, len, ts, seq);
return 0;
}
int rtsp_video_cb(uint8 * pdata, int len, uint32 ts, uint16 seq, void *puser)
{
CMediaProxy * p_proxy = (CMediaProxy *) puser;
p_proxy->onVideo(pdata, len, ts, seq);
return 0;
}
int jpeg_video_cb(uint8 * pdata, int len, void *puser)
{
CMediaProxy * p_proxy = (CMediaProxy *) puser;
p_proxy->onVideo(pdata, len, 0, 0);
return 0;
}
#ifdef RTMP_PROXY
int rtmp_video_data_cb(uint8 * pdata, int len, uint32 ts, void *puser)
{
CMediaProxy * p_proxy = (CMediaProxy *) puser;
p_proxy->onVideo(pdata, len, ts, 0);
return 0;
}
int rtmp_audio_data_cb(uint8 * pdata, int len, uint32 ts, void *puser)
{
CMediaProxy * p_proxy = (CMediaProxy *) puser;
p_proxy->onAudio(pdata, len, ts, 0);
return 0;
}
#endif // RTMP_PROXY
#ifdef SRT_PROXY
int srt_video_data_cb(uint8 * pdata, int len, uint32 ts, void *puser)
{
CMediaProxy * p_proxy = (CMediaProxy *) puser;
p_proxy->onVideo(pdata, len, ts, 0);
return 0;
}
int srt_audio_data_cb(uint8 * pdata, int len, uint32 ts, void *puser)
{
CMediaProxy * p_proxy = (CMediaProxy *) puser;
p_proxy->onAudio(pdata, len, ts, 0);
return 0;
}
#endif // SRT_PROXY
void * notifyThread(void * argv)
{
CMediaProxy * p_proxy = (CMediaProxy *) argv;
p_proxy->notifyHandler();
return NULL;
}
/*************************************************************/
MEDIA_PRY * media_add_proxy()
{
MEDIA_PRY * p_tmp;
MEDIA_PRY * p_new_proxy = (MEDIA_PRY *) malloc(sizeof(MEDIA_PRY));
if (NULL == p_new_proxy)
{
return NULL;
}
memset(p_new_proxy, 0, sizeof(MEDIA_PRY));
p_tmp = g_proxy;
if (NULL == p_tmp)
{
g_proxy = p_new_proxy;
}
else
{
while (p_tmp && p_tmp->next)
{
p_tmp = p_tmp->next;
}
p_tmp->next = p_new_proxy;
}
return p_new_proxy;
}
void media_free_proxies()
{
MEDIA_PRY * p_next;
MEDIA_PRY * p_tmp = g_proxy;
while (p_tmp)
{
p_next = p_tmp->next;
if (p_tmp->proxy)
{
delete p_tmp->proxy;
p_tmp->proxy = NULL;
}
free(p_tmp);
p_tmp = p_next;
}
g_proxy = NULL;
}
BOOL media_init_proxy(MEDIA_PRY * p_proxy)
{
BOOL ret = FALSE;
p_proxy->proxy = new CMediaProxy(&p_proxy->cfg);
if (p_proxy->proxy)
{
ret = p_proxy->proxy->startConn(p_proxy->cfg.url, p_proxy->cfg.user, p_proxy->cfg.pass);
}
else
{
log_print(HT_LOG_ERR, "%s, new rtsp proxy failed\r\n", __FUNCTION__);
}
return ret;
}
void media_init_proxies()
{
MEDIA_PRY * p_proxy = g_proxy;
while (p_proxy)
{
media_init_proxy(p_proxy);
p_proxy = p_proxy->next;
}
}
int media_get_proxy_nums()
{
int nums = 0;
MEDIA_PRY * p_proxy = g_proxy;
while (p_proxy)
{
nums++;
p_proxy = p_proxy->next;
}
return nums;
}
MEDIA_PRY * media_proxy_match(const char * key)
{
MEDIA_PRY * p_proxy = g_proxy;
while (p_proxy)
{
if (strcmp(key, p_proxy->cfg.key) == 0)
{
return p_proxy;
}
p_proxy = p_proxy->next;
}
return NULL;
}
#if defined(MEDIA_FILE) || defined(MEDIA_DEVICE)
void media_proxy_vdecoder_cb(AVFrame * frame, void *pUserdata)
{
CMediaProxy * pProxy = (CMediaProxy *)pUserdata;
pProxy->videoDataEncode(frame);
}
void media_proxy_vencoder_cb(uint8 *data, int size, int waitnext, void *pUserdata)
{
CMediaProxy * pProxy = (CMediaProxy *)pUserdata;
pProxy->dataCallback(data, size, DATA_TYPE_VIDEO);
}
void media_proxy_adecoder_cb(AVFrame * frame, void *pUserdata)
{
CMediaProxy * pProxy = (CMediaProxy *)pUserdata;
pProxy->audioDataEncode(frame);
}
void meida_proxy_aencoder_cb(uint8 *data, int size, int nbsamples, void *pUserdata)
{
CMediaProxy * pProxy = (CMediaProxy *)pUserdata;
pProxy->dataCallback(data, size, DATA_TYPE_AUDIO);
}
#endif
/*************************************************************/
CMediaProxy::CMediaProxy(PROXY_CFG * pConfig)
: inited(0)
, exiting(0)
, m_pConfig(NULL)
, m_reconnto(1)
, m_rtsp(NULL)
, m_mjpeg(NULL)
#ifdef RTMP_PROXY
, m_rtmp(NULL)
#endif
#ifdef SRT_PROXY
, m_srt(NULL)
#endif
{
memset(&m_info, 0, sizeof(MEDIA_INFO));
memset(m_url, 0, sizeof(m_url));
memset(m_user, 0, sizeof(m_user));
memset(m_pass, 0, sizeof(m_pass));
if (pConfig)
{
m_pConfig = (PROXY_CFG *) malloc(sizeof(PROXY_CFG));
memcpy(m_pConfig, pConfig, sizeof(PROXY_CFG));
}
m_notifyQueue = hqCreate(10, sizeof(int), HQ_GET_WAIT | HQ_PUT_WAIT);
m_hNotify = sys_os_create_thread((void *) notifyThread, this);
m_pCallbackMutex = sys_os_create_mutex();
m_pCallbackList = h_list_create(FALSE);
#if defined(MEDIA_FILE) || defined(MEDIA_DEVICE)
m_nVideoRecodec = 0;
m_nAudioRecodec = 0;
m_pVideoDecoder = NULL;
m_pAudioDecoder = NULL;
m_pVideoEncoder = NULL;
m_pAudioEncoder = NULL;
#endif
}
CMediaProxy::~CMediaProxy(void)
{
int evt = -1;
exiting = 1;
freeConn();
hqBufPut(m_notifyQueue, (char *)&evt);
while (m_hNotify)
{
usleep(20*1000);
}
hqDelete(m_notifyQueue);
#if defined(MEDIA_FILE) || defined(MEDIA_DEVICE)
if (m_pVideoDecoder)
{
delete m_pVideoDecoder;
m_pVideoDecoder = NULL;
}
if (m_pAudioDecoder)
{
delete m_pAudioDecoder;
m_pAudioDecoder = NULL;
}
if (m_pVideoEncoder)
{
delete m_pVideoEncoder;
m_pVideoEncoder = NULL;
}
if (m_pAudioEncoder)
{
delete m_pAudioEncoder;
m_pAudioEncoder = NULL;
}
#endif
if (m_pConfig)
{
free(m_pConfig);
m_pConfig = NULL;
}
h_list_free_container(m_pCallbackList);
sys_os_destroy_sig_mutex(m_pCallbackMutex);
}
void CMediaProxy::freeConn()
{
if (m_rtsp)
{
// stop rtsp connection
m_rtsp->rtsp_stop();
m_rtsp->rtsp_close();
delete m_rtsp;
m_rtsp = NULL;
}
if (m_mjpeg)
{
delete m_mjpeg;
m_mjpeg = NULL;
}
#ifdef RTMP_PROXY
if (m_rtmp)
{
// stop rtmp connection
m_rtmp->rtmp_stop();
m_rtmp->rtmp_close();
delete m_rtmp;
m_rtmp = NULL;
}
#endif
#ifdef SRT_PROXY
if (m_srt)
{
// stop srt connection
m_srt->srt_stop();
m_srt->srt_close();
delete m_srt;
m_srt = NULL;
}
#endif
}
BOOL CMediaProxy::startConn()
{
BOOL isRtsp = FALSE;
BOOL isMjpeg = FALSE;
#ifdef RTMP_PROXY
BOOL isRtmp = FALSE;
#endif
#ifdef SRT_PROXY
BOOL isSrt = FALSE;
#endif
if (memcmp(m_url, "rtsp://", 7) == 0)
{
isRtsp = TRUE;
}
else if (memcmp(m_url, "http://", 7) == 0 || memcmp(m_url, "https://", 8) == 0)
{
#ifdef OVER_HTTP
HTTPCTT ctt = CTT_NULL;
if (http_test(m_url, m_user, m_pass, &ctt))
{
if (ctt == CTT_RTSP_TUNNELLED)
{
// rtsp over http or rtsp over https
isRtsp = TRUE;
}
else
{
isMjpeg = TRUE;
}
}
else
#endif
{
isMjpeg = TRUE;
}
}
#ifdef OVER_WEBSOCKET
else if (memcmp(m_url, "ws://", 5) == 0 || memcmp(m_url, "wss://", 6) == 0)
{
isRtsp = TRUE;
}
#endif
#ifdef RTMP_PROXY
else if (memcmp(m_url, "rtmp://", 7) == 0 ||
memcmp(m_url, "rtmpt://", 8) == 0 ||
memcmp(m_url, "rtmps://", 8) == 0 ||
memcmp(m_url, "rtmpe://", 8) == 0 ||
memcmp(m_url, "rtmpfp://", 9) == 0 ||
memcmp(m_url, "rtmpte://", 9) == 0 ||
memcmp(m_url, "rtmpts://", 9) == 0)
{
isRtmp = TRUE;
}
#endif
#ifdef SRT_PROXY
else if (memcmp(m_url, "srt://", 6) == 0)
{
isSrt = TRUE;
}
#endif
if (isRtsp)
{
m_rtsp = new CRtspClient;
if (NULL == m_rtsp)
{
log_print(HT_LOG_ERR, "%s, new rtsp failed\r\n", __FUNCTION__);
return FALSE;
}
m_rtsp->set_notify_cb(event_notify_cb, this);
m_rtsp->set_video_cb(rtsp_video_cb);
m_rtsp->set_audio_cb(rtsp_audio_cb);
if (m_pConfig)
{
if (m_pConfig->transfer == RC_TRANSFER_UDP)
{
m_rtsp->set_rtp_over_udp(1);
}
else if (m_pConfig->transfer == RC_TRANSFER_MULTICAST)
{
m_rtsp->set_rtp_multicast(1);
}
}
return m_rtsp->rtsp_start(m_url, m_user, m_pass);
}
else if (isMjpeg)
{
m_mjpeg = new CHttpMjpeg;
if (NULL == m_mjpeg)
{
log_print(HT_LOG_ERR, "%s, new httpmjpeg failed\r\n", __FUNCTION__);
return FALSE;
}
m_mjpeg->set_notify_cb(event_notify_cb, this);
m_mjpeg->set_video_cb(jpeg_video_cb);
return m_mjpeg->mjpeg_start(m_url, m_user, m_pass);
}
#ifdef RTMP_PROXY
else if (isRtmp)
{
m_rtmp = new CRtmpClient;
if (NULL == m_rtmp)
{
log_print(HT_LOG_ERR, "%s, new rtmp client failed\r\n", __FUNCTION__);
return FALSE;
}
m_rtmp->set_notify_cb(event_notify_cb, this);
m_rtmp->set_video_cb(rtmp_video_data_cb);
m_rtmp->set_audio_cb(rtmp_audio_data_cb);
return m_rtmp->rtmp_start(m_url, m_user, m_pass);
}
#endif
#ifdef SRT_PROXY
else if (isSrt)
{
m_srt = new CSrtClient;
if (NULL == m_srt)
{
log_print(HT_LOG_ERR, "%s, new srt failed\r\n", __FUNCTION__);
return FALSE;
}
m_srt->set_notify_cb(event_notify_cb, this);
m_srt->set_video_cb(srt_video_data_cb);
m_srt->set_audio_cb(srt_audio_data_cb);
return m_srt->srt_start(m_url, m_user, m_pass);
}
#endif
return FALSE;
}
BOOL CMediaProxy::startConn(const char * url, char * user, char * pass)
{
strncpy(m_url, url, sizeof(m_url)-1);
strncpy(m_user, user, sizeof(m_user)-1);
strncpy(m_pass, pass, sizeof(m_pass)-1);
return startConn();
}
BOOL CMediaProxy::restartConn()
{
freeConn();
return startConn();
}
void CMediaProxy::onNotify(int evt)
{
if (evt == RTSP_EVE_CONNSUCC)
{
m_info.video.codec = m_rtsp->video_codec();
m_info.audio.codec = m_rtsp->audio_codec();
if (m_info.video.codec != VIDEO_CODEC_NONE)
{
m_info.has_video = 1;
}
if (m_info.audio.codec != AUDIO_CODEC_NONE)
{
m_info.has_audio = 1;
m_info.audio.samplerate = m_rtsp->get_audio_samplerate();
m_info.audio.channels = m_rtsp->get_audio_channels();
m_info.audio.bitpersample = m_rtsp->get_audio_bitpersample();
}
inited = 1;
}
else if (evt == MJPEG_EVE_CONNSUCC)
{
m_info.video.codec = VIDEO_CODEC_JPEG;
m_info.has_video = 1;
inited = 1;
}
#ifdef RTMP_PROXY
else if (evt == RTMP_EVE_VIDEOREADY)
{
m_info.video.codec = m_rtmp->video_codec();
if (m_info.video.codec != VIDEO_CODEC_NONE)
{
m_info.has_video = 1;
}
inited = 1;
}
else if (evt == RTMP_EVE_AUDIOREADY)
{
m_info.audio.codec = m_rtmp->audio_codec();
if (m_info.audio.codec != AUDIO_CODEC_NONE)
{
m_info.has_audio = 1;
m_info.audio.samplerate = m_rtmp->get_audio_samplerate();
m_info.audio.channels = m_rtmp->get_audio_channels();
}
inited = 1;
}
#endif
#ifdef SRT_PROXY
else if (evt == SRT_EVE_VIDEOREADY)
{
m_info.video.codec = m_srt->video_codec();
if (m_info.video.codec != VIDEO_CODEC_NONE)
{
m_info.has_video = 1;
}
inited = 1;
}
else if (evt == SRT_EVE_AUDIOREADY)
{
m_info.audio.codec = m_srt->audio_codec();
if (m_info.audio.codec != AUDIO_CODEC_NONE)
{
m_info.has_audio = 1;
m_info.audio.samplerate = m_srt->get_audio_samplerate();
m_info.audio.channels = m_srt->get_audio_channels();
}
inited = 1;
}
#endif
hqBufPut(m_notifyQueue, (char *)&evt);
}
void CMediaProxy::onVideo(uint8 * pdata, int len, uint32 ts, uint16 seq)
{
if (m_info.video.width == 0 || m_info.video.height == 0)
{
avc_parse_video_size(m_info.video.codec, pdata, len, &m_info.video.width, &m_info.video.height);
}
#if defined(MEDIA_FILE) || defined(MEDIA_DEVICE)
int recodec = needVideoRecodec(pdata, len);
if (1 == recodec) // need recodec
{
videoDataDecode(pdata, len);
}
else if (-1 == recodec) // not need recodec
{
dataCallback(pdata, len, DATA_TYPE_VIDEO);
}
else if (0 == recodec) // still not sure
{
}
#else
dataCallback(pdata, len, DATA_TYPE_VIDEO);
#endif
}
void CMediaProxy::onAudio(uint8 * pdata, int len, uint32 ts, uint16 seq)
{
#if defined(MEDIA_FILE) || defined(MEDIA_DEVICE)
int recodec = needAudioRecodec();
if (1 == recodec) // need recodec
{
audioDataDecode(pdata, len);
}
else if (-1 == recodec) // not need recodec
{
dataCallback(pdata, len, DATA_TYPE_AUDIO);
}
else if (0 == recodec) // still not sure
{
}
#else
dataCallback(pdata, len, DATA_TYPE_AUDIO);
#endif
}
void CMediaProxy::clearNotify()
{
int evt;
while (!hqBufIsEmpty(m_notifyQueue))
{
hqBufGet(m_notifyQueue, (char *)&evt);
}
}
void CMediaProxy::notifyHandler()
{
int evt;
while (!exiting && hqBufGet(m_notifyQueue, (char *)&evt))
{
log_print(HT_LOG_DBG, "%s, evt = %d\r\n", __FUNCTION__, evt);
if (evt < 0)
{
break;
}
if ( evt == RTSP_EVE_CONNFAIL || evt == RTSP_EVE_NOSIGNAL
|| evt == RTSP_EVE_NODATA || evt == RTSP_EVE_STOPPED
|| evt == MJPEG_EVE_CONNFAIL || evt == MJPEG_EVE_NOSIGNAL
|| evt == MJPEG_EVE_NODATA || evt == MJPEG_EVE_STOPPED
#ifdef RTMP_PROXY
|| evt == RTMP_EVE_CONNFAIL || evt == RTMP_EVE_NOSIGNAL
|| evt == RTMP_EVE_NODATA || evt == RTMP_EVE_STOPPED
#endif
#ifdef SRT_PROXY
|| evt == SRT_EVE_CONNFAIL || evt == SRT_EVE_NOSIGNAL
|| evt == SRT_EVE_NODATA || evt == SRT_EVE_STOPPED
#endif
)
{
if (m_reconnto < 10)
{
m_reconnto *= 2;
}
sleep(m_reconnto);
clearNotify();
restartConn();
}
else if (evt == RTSP_EVE_CONNSUCC)
{
if (VIDEO_CODEC_H264 == m_info.video.codec)
{
m_rtsp->get_h264_params();
}
else if (VIDEO_CODEC_H265 == m_info.video.codec)
{
m_rtsp->get_h265_params();
}
}
#ifdef RTMP_PROXY
else if (evt == RTMP_EVE_VIDEOREADY)
{
if (VIDEO_CODEC_H264 == m_info.video.codec)
{
m_rtmp->get_h264_params();
}
else if (VIDEO_CODEC_H265 == m_info.video.codec)
{
m_rtmp->get_h265_params();
}
}
#endif
#ifdef SRT_PROXY
else if (evt == SRT_EVE_VIDEOREADY)
{
if (VIDEO_CODEC_H264 == m_info.video.codec)
{
m_srt->get_h264_params();
}
else if (VIDEO_CODEC_H265 == m_info.video.codec)
{
m_srt->get_h265_params();
}
}
#endif
}
m_hNotify = 0;
}
BOOL CMediaProxy::isCallbackExist(ProxyDataCB pCallback, void * pUserdata)
{
BOOL exist = FALSE;
ProxyCB * p_cb = NULL;
LINKED_NODE * p_node = NULL;
sys_os_mutex_enter(m_pCallbackMutex);
p_node = h_list_lookup_start(m_pCallbackList);
while (p_node)
{
p_cb = (ProxyCB *) p_node->p_data;
if (p_cb->pCallback == pCallback && p_cb->pUserdata == pUserdata)
{
exist = TRUE;
break;
}
p_node = h_list_lookup_next(m_pCallbackList, p_node);
}
h_list_lookup_end(m_pCallbackList);
sys_os_mutex_leave(m_pCallbackMutex);
return exist;
}
void CMediaProxy::addCallback(ProxyDataCB pCallback, void * pUserdata)
{
if (isCallbackExist(pCallback, pUserdata))
{
return;
}
ProxyCB * p_cb = (ProxyCB *) malloc(sizeof(ProxyCB));
if (NULL == p_cb)
{
return;
}
memset(p_cb, 0, sizeof(ProxyCB));
p_cb->pCallback = pCallback;
p_cb->pUserdata = pUserdata;
p_cb->bVFirst = TRUE;
p_cb->bAFirst = TRUE;
sys_os_mutex_enter(m_pCallbackMutex);
h_list_add_at_back(m_pCallbackList, p_cb);
sys_os_mutex_leave(m_pCallbackMutex);
}
void CMediaProxy::delCallback(ProxyDataCB pCallback, void * pUserdata)
{
ProxyCB * p_cb = NULL;
LINKED_NODE * p_node = NULL;
sys_os_mutex_enter(m_pCallbackMutex);
p_node = h_list_lookup_start(m_pCallbackList);
while (p_node)
{
p_cb = (ProxyCB *) p_node->p_data;
if (p_cb->pCallback == pCallback && p_cb->pUserdata == pUserdata)
{
free(p_cb);
h_list_remove(m_pCallbackList, p_node);
break;
}
p_node = h_list_lookup_next(m_pCallbackList, p_node);
}
h_list_lookup_end(m_pCallbackList);
sys_os_mutex_leave(m_pCallbackMutex);
}
void CMediaProxy::dataCallback(uint8 * data, int size, int type)
{
ProxyCB * p_cb = NULL;
LINKED_NODE * p_node = NULL;
sys_os_mutex_enter(m_pCallbackMutex);
p_node = h_list_lookup_start(m_pCallbackList);
while (p_node)
{
p_cb = (ProxyCB *) p_node->p_data;
if (p_cb->pCallback != NULL)
{
uint8 * buff = data;
int len = size;
if (type == DATA_TYPE_VIDEO)
{
#if defined(MEDIA_FILE) || defined(MEDIA_DEVICE)
if (NULL == m_pVideoEncoder)
#endif
{
if (m_info.video.codec == VIDEO_CODEC_H264)
{
uint8 naltype = avc_h264_nalu_type(data, size);
if (naltype == H264_NAL_IDR)
{
uint8 extra[1024];
int extra_len = sizeof(extra);
if (getExtraData(extra, &extra_len) && extra_len > 0)
{
len = extra_len + size;
buff = (uint8 *)malloc(len);
if (buff)
{
memcpy(buff, extra, extra_len);
memcpy(buff + extra_len, data, size);
}
else
{
buff = data;
len = size;
}
}
}
}
else if (m_info.video.codec == VIDEO_CODEC_H265)
{
uint8 naltype = avc_h265_nalu_type(data, size);
if (naltype >= HEVC_NAL_BLA_W_LP && naltype <= HEVC_NAL_CRA_NUT)
{
uint8 extra[1024];
int extra_len = sizeof(extra);
if (getExtraData(extra, &extra_len) && extra_len > 0)
{
len = extra_len + size;
buff = (uint8 *)malloc(len);
if (buff)
{
memcpy(buff, extra, extra_len);
memcpy(buff + extra_len, data, size);
}
else
{
buff = data;
len = size;
}
}
}
}
}
}
p_cb->pCallback(buff, len, type, p_cb->pUserdata);
if (buff != data)
{
free(buff);
}
}
p_node = h_list_lookup_next(m_pCallbackList, p_node);
}
h_list_lookup_end(m_pCallbackList);
sys_os_mutex_leave(m_pCallbackMutex);
}
BOOL CMediaProxy::getExtraData(uint8 * extra, int * extra_len)
{
BOOL ret = FALSE;
if (NULL == extra_len)
{
return FALSE;
}
if (m_info.video.codec == VIDEO_CODEC_H264)
{
uint8 sps[512], pps[512];
int sps_len = 0, pps_len = 0, len = 0;
if (m_rtsp && m_rtsp->get_h264_params(sps, &sps_len, pps, &pps_len))
{
if (*extra_len >= len + sps_len)
{
if (extra)
{
memcpy(extra+len, sps, sps_len);
}
len += sps_len;
}
if (*extra_len >= len + pps_len)
{
if (extra)
{
memcpy(extra+len, pps, pps_len);
}
len += pps_len;
}
}
#ifdef RTMP_PROXY
else if (m_rtmp && m_rtmp->get_h264_params(sps, &sps_len, pps, &pps_len))
{
if (*extra_len >= len + sps_len)
{
if (extra)
{
memcpy(extra+len, sps, sps_len);
}
len += sps_len;
}
if (*extra_len >= len + pps_len)
{
if (extra)
{
memcpy(extra+len, pps, pps_len);
}
len += pps_len;
}
}
#endif
#ifdef SRT_PROXY
else if (m_srt && m_srt->get_h264_params(sps, &sps_len, pps, &pps_len))
{
if (*extra_len >= len + sps_len)
{
if (extra)
{
memcpy(extra+len, sps, sps_len);
}
len += sps_len;
}
if (*extra_len >= len + pps_len)
{
if (extra)
{
memcpy(extra+len, pps, pps_len);
}
len += pps_len;
}
}
#endif
*extra_len = len;
ret = TRUE;
}
else if (m_info.video.codec == VIDEO_CODEC_H265)
{
uint8 vps[512], sps[512], pps[512];
int vps_len, sps_len = 0, pps_len = 0, len = 0;
if (m_rtsp && m_rtsp->get_h265_params(sps, &sps_len, pps, &pps_len, vps, &vps_len))
{
if (*extra_len >= len + vps_len)
{
if (extra)
{
memcpy(extra+len, vps, vps_len);
}
len += vps_len;
}
if (*extra_len >= len + sps_len)
{
if (extra)
{
memcpy(extra+len, sps, sps_len);
}
len += sps_len;
}
if (*extra_len >= len + pps_len)
{
if (extra)
{
memcpy(extra+len, pps, pps_len);
}
len += pps_len;
}
}
#ifdef RTMP_PROXY
else if (m_rtmp && m_rtmp->get_h265_params(sps, &sps_len, pps, &pps_len, vps, &vps_len))
{
if (*extra_len >= len + vps_len)
{
if (extra)
{
memcpy(extra+len, vps, vps_len);
}
len += vps_len;
}
if (*extra_len >= len + sps_len)
{
if (extra)
{
memcpy(extra+len, sps, sps_len);
}
len += sps_len;
}
if (*extra_len >= len + pps_len)
{
if (extra)
{
memcpy(extra+len, pps, pps_len);
}
len += pps_len;
}
}
#endif
#ifdef SRT_PROXY
else if (m_srt && m_srt->get_h265_params(sps, &sps_len, pps, &pps_len, vps, &vps_len))
{
if (*extra_len >= len + vps_len)
{
if (extra)
{
memcpy(extra+len, vps, vps_len);
}
len += vps_len;
}
if (*extra_len >= len + sps_len)
{
if (extra)
{
memcpy(extra+len, sps, sps_len);
}
len += sps_len;
}
if (*extra_len >= len + pps_len)
{
if (extra)
{
memcpy(extra+len, pps, pps_len);
}
len += pps_len;
}
}
#endif
*extra_len = len;
ret = TRUE;
}
if (!ret)
{
*extra_len = 0;
}
return ret;
}
void CMediaProxy::sendExtraData(ProxyCB * p_cb, uint8 * data, int size, int type)
{
if (m_info.video.codec == VIDEO_CODEC_H264)
{
uint8 naltype = avc_h264_nalu_type(data, size);
if (H264_NAL_SPS == naltype || H264_NAL_PPS == naltype)
{
}
else if (H264_NAL_IDR != p_cb->nPreNalu && (H264_NAL_IDR == naltype || p_cb->bVFirst))
{
uint8 extra[1024];
int extra_len = sizeof(extra);
if (getExtraData(extra, &extra_len) && extra_len > 0)
{
p_cb->pCallback(extra, extra_len, type, p_cb->pUserdata);
}
}
p_cb->nPreNalu = naltype;
p_cb->bVFirst = FALSE;
}
else if (m_info.video.codec == VIDEO_CODEC_H265)
{
uint8 naltype = avc_h265_nalu_type(data, size);
if (HEVC_NAL_VPS == naltype || HEVC_NAL_SPS == naltype || HEVC_NAL_PPS == naltype)
{
}
else if (HEVC_NAL_IDR_W_RADL != p_cb->nPreNalu &&
HEVC_NAL_IDR_N_LP != p_cb->nPreNalu &&
HEVC_NAL_CRA_NUT != p_cb->nPreNalu &&
(HEVC_NAL_IDR_W_RADL == naltype || HEVC_NAL_IDR_N_LP == naltype ||
HEVC_NAL_CRA_NUT == naltype || p_cb->bVFirst))
{
uint8 extra[2048];
int extra_len = sizeof(extra);
if (getExtraData(extra, &extra_len) && extra_len > 0)
{
p_cb->pCallback(extra, extra_len, type, p_cb->pUserdata);
}
}
p_cb->nPreNalu = naltype;
p_cb->bVFirst = FALSE;
}
}
char * CMediaProxy::getH264AuxSDPLine(int rtp_pt)
{
uint8 sps[512]; int spsSize = 0;
uint8 pps[512]; int ppsSize = 0;
if (m_rtsp && !m_rtsp->get_h264_params(sps, &spsSize, pps, &ppsSize))
{
return NULL;
}
#ifdef RTMP_PROXY
else if (m_rtmp && !m_rtmp->get_h264_params(sps, &spsSize, pps, &ppsSize))
{
return NULL;
}
#endif
#ifdef SRT_PROXY
else if (m_srt && !m_srt->get_h264_params(sps, &spsSize, pps, &ppsSize))
{
return NULL;
}
#endif
if (spsSize == 0 || ppsSize == 0)
{
return NULL;
}
// Set up the "a=fmtp:" SDP line for this stream:
uint8* spsWEB = new uint8[spsSize]; // "WEB" means "Without Emulation Bytes"
uint32 spsWEBSize = remove_emulation_bytes(spsWEB, spsSize, sps, spsSize);
if (spsWEBSize < 4)
{
// Bad SPS size => assume our source isn't ready
delete[] spsWEB;
return NULL;
}
uint32 profileLevelId = (spsWEB[1]<<16) | (spsWEB[2]<<8) | spsWEB[3];
delete[] spsWEB;
char* sps_base64 = new char[spsSize*2+1];
char* pps_base64 = new char[ppsSize*2+1];
base64_encode(sps, spsSize, sps_base64, spsSize*2+1);
base64_encode(pps, ppsSize, pps_base64, ppsSize*2+1);
char const* fmtpFmt =
"a=fmtp:%d packetization-mode=1"
";profile-level-id=%06X"
";sprop-parameter-sets=%s,%s";
uint32 fmtpFmtSize = strlen(fmtpFmt)
+ 3 /* max char len */
+ 6 /* 3 bytes in hex */
+ strlen(sps_base64) + strlen(pps_base64);
char* fmtp = new char[fmtpFmtSize+1];
memset(fmtp, 0, fmtpFmtSize+1);
sprintf(fmtp, fmtpFmt, rtp_pt, profileLevelId, sps_base64, pps_base64);
delete[] sps_base64;
delete[] pps_base64;
return fmtp;
}
char * CMediaProxy::getH265AuxSDPLine(int rtp_pt)
{
uint8 vps[512]; int vpsSize = 0;
uint8 sps[512]; int spsSize = 0;
uint8 pps[512]; int ppsSize = 0;
if (m_rtsp && !m_rtsp->get_h265_params(sps, &spsSize, pps, &ppsSize, vps, &vpsSize))
{
return NULL;
}
#ifdef RTMP_PROXY
else if (!m_rtmp->get_h265_params(sps, &spsSize, pps, &ppsSize, vps, &vpsSize))
{
return NULL;
}
#endif
#ifdef SRT_PROXY
else if (m_srt && !m_srt->get_h265_params(sps, &spsSize, pps, &ppsSize, vps, &vpsSize))
{
return NULL;
}
#endif
if (vpsSize == 0 || spsSize == 0 || ppsSize == 0)
{
return NULL;
}
// Set up the "a=fmtp:" SDP line for this stream.
uint8* vpsWEB = new uint8[vpsSize]; // "WEB" means "Without Emulation Bytes"
uint32 vpsWEBSize = remove_emulation_bytes(vpsWEB, vpsSize, vps, vpsSize);
if (vpsWEBSize < 6/*'profile_tier_level' offset*/ + 12/*num 'profile_tier_level' bytes*/)
{
// Bad VPS size => assume our source isn't ready
delete[] vpsWEB;
return NULL;
}
uint8 const* profileTierLevelHeaderBytes = &vpsWEB[6];
uint32 profileSpace = profileTierLevelHeaderBytes[0]>>6; // general_profile_space
uint32 profileId = profileTierLevelHeaderBytes[0]&0x1F; // general_profile_idc
uint32 tierFlag = (profileTierLevelHeaderBytes[0]>>5)&0x1;// general_tier_flag
uint32 levelId = profileTierLevelHeaderBytes[11]; // general_level_idc
uint8 const* interop_constraints = &profileTierLevelHeaderBytes[5];
char interopConstraintsStr[100];
sprintf(interopConstraintsStr, "%02X%02X%02X%02X%02X%02X",
interop_constraints[0], interop_constraints[1], interop_constraints[2],
interop_constraints[3], interop_constraints[4], interop_constraints[5]);
delete[] vpsWEB;
char* sprop_vps = new char[vpsSize*2+1];
char* sprop_sps = new char[spsSize*2+1];
char* sprop_pps = new char[ppsSize*2+1];
base64_encode(vps, vpsSize, sprop_vps, vpsSize*2+1);
base64_encode(sps, spsSize, sprop_sps, spsSize*2+1);
base64_encode(pps, ppsSize, sprop_pps, ppsSize*2+1);
char const* fmtpFmt =
"a=fmtp:%d profile-space=%u"
";profile-id=%u"
";tier-flag=%u"
";level-id=%u"
";interop-constraints=%s"
";sprop-vps=%s"
";sprop-sps=%s"
";sprop-pps=%s";
uint32 fmtpFmtSize = strlen(fmtpFmt)
+ 3 /* max num chars: rtpPayloadType */ + 20 /* max num chars: profile_space */
+ 20 /* max num chars: profile_id */
+ 20 /* max num chars: tier_flag */
+ 20 /* max num chars: level_id */
+ strlen(interopConstraintsStr)
+ strlen(sprop_vps)
+ strlen(sprop_sps)
+ strlen(sprop_pps);
char* fmtp = new char[fmtpFmtSize+1];
memset(fmtp, 0, fmtpFmtSize+1);
sprintf(fmtp, fmtpFmt,
rtp_pt, profileSpace,
profileId,
tierFlag,
levelId,
interopConstraintsStr,
sprop_vps,
sprop_sps,
sprop_pps);
delete[] sprop_vps;
delete[] sprop_sps;
delete[] sprop_pps;
return fmtp;
}
char * CMediaProxy::getMP4AuxSDPLine(int rtp_pt)
{
char sdp[1024] = {'\0'};
if (NULL == m_rtsp)
{
return NULL;
}
if (m_rtsp->get_mp4_sdp_desc(sdp, sizeof(sdp)) == FALSE)
{
log_print(HT_LOG_ERR, "%s, get_mp4_sdp_desc failed\r\n", __FUNCTION__);
return NULL;
}
char const* fmtpFmt = "a=fmtp:%d %s";
uint32 fmtpFmtSize = strlen(fmtpFmt)
+ 3 /* max char len */
+ strlen(sdp);
char* fmtp = new char[fmtpFmtSize+1];
memset(fmtp, 0, fmtpFmtSize+1);
sprintf(fmtp, fmtpFmt, rtp_pt, sdp);
return fmtp;
}
char * CMediaProxy::getAACAuxSDPLine(int rtp_pt)
{
uint8 * ac = NULL;
int aclen = 0;
uint8 config[2];
if (m_rtsp)
{
ac = m_rtsp->get_audio_config();
aclen = m_rtsp->get_audio_config_len();
}
#ifdef RTMP_PROXY
else if (m_rtmp)
{
ac = m_rtmp->get_audio_config();
aclen = m_rtmp->get_audio_config_len();
}
#endif
#ifdef SRT_PROXY
else if (m_srt)
{
ac = m_srt->get_audio_config();
aclen = m_srt->get_audio_config_len();
}
#endif
if (NULL == ac || 0 == aclen)
{
int sample_rate = 8000;
int channels = 1;
int rate_idx = 0;
uint8 obj_type = 2; // AAC-LC
if (m_rtsp)
{
sample_rate = m_rtsp->get_audio_samplerate();
channels = m_rtsp->get_audio_channels();
}
#ifdef RTMP_PROXY
else if (m_rtmp)
{
sample_rate = m_rtmp->get_audio_samplerate();
channels = m_rtmp->get_audio_channels();
}
#endif
#ifdef SRT_PROXY
else if (m_srt)
{
sample_rate = m_srt->get_audio_samplerate();
channels = m_srt->get_audio_channels();
}
#endif
switch (sample_rate)
{
case 96000:
rate_idx = 0;
break;
case 88200:
rate_idx = 1;
break;
case 64000:
rate_idx = 2;
break;
case 48000:
rate_idx = 3;
break;
case 44100:
rate_idx = 4;
break;
case 32000:
rate_idx = 5;
break;
case 24000:
rate_idx = 6;
break;
case 22050:
rate_idx = 7;
break;
case 16000:
rate_idx = 8;
break;
case 12000:
rate_idx = 9;
break;
case 11025:
rate_idx = 10;
break;
case 8000:
rate_idx = 11;
break;
case 7350:
rate_idx = 12;
break;
default:
rate_idx = 4;
break;
}
ac = config;
aclen = 2;
config[0] = (obj_type << 3) | (rate_idx >> 1);
config[1] = (rate_idx << 7) | (channels << 3);
}
char const* fmtpFmt =
"a=fmtp:%d "
"streamtype=5;profile-level-id=1;"
"mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;"
"config=";
uint32 fmtpFmtSize = strlen(fmtpFmt)
+ 3 /* max char len */
+ 2 * aclen; /* 2*, because each byte prints as 2 chars */
char* fmtp = new char[fmtpFmtSize+1];
memset(fmtp, 0, fmtpFmtSize+1);
sprintf(fmtp, fmtpFmt, rtp_pt);
char* endPtr = &fmtp[strlen(fmtp)];
for (int i = 0; i < aclen; ++i)
{
sprintf(endPtr, "%02X", ac[i]);
endPtr += 2;
}
return fmtp;
}
char * CMediaProxy::getVideoAuxSDPLine(int rtp_pt)
{
#if defined(MEDIA_FILE) || defined(MEDIA_DEVICE)
if (m_pVideoEncoder)
{
return m_pVideoEncoder->getAuxSDPLine(rtp_pt);
}
else
#endif
if (m_info.video.codec == VIDEO_CODEC_H264)
{
return getH264AuxSDPLine(rtp_pt);
}
else if (m_info.video.codec == VIDEO_CODEC_H265)
{
return getH265AuxSDPLine(rtp_pt);
}
else if (m_info.video.codec == VIDEO_CODEC_MP4)
{
return getMP4AuxSDPLine(rtp_pt);
}
return NULL;
}
char * CMediaProxy::getAudioAuxSDPLine(int rtp_pt)
{
#if defined(MEDIA_FILE) || defined(MEDIA_DEVICE)
if (m_pAudioEncoder)
{
return m_pAudioEncoder->getAuxSDPLine(rtp_pt);
}
else
#endif
if (m_info.audio.codec == AUDIO_CODEC_AAC)
{
return getAACAuxSDPLine(rtp_pt);
}
return NULL;
}
#if defined(MEDIA_FILE) || defined(MEDIA_DEVICE)
/**
* 1 - need
* 0 - Still not sure
* -1 - not need
*/
int CMediaProxy::needVideoRecodec(uint8 * p_data, int len)
{
if (0 != m_nVideoRecodec)
{
return m_nVideoRecodec;
}
if (!m_pConfig->has_output || !m_pConfig->output.has_video)
{
m_nVideoRecodec = -1;
return -1;
}
if (m_info.video.width == 0 || m_info.video.height == 0)
{
return 0;
}
if (VIDEO_CODEC_NONE == m_pConfig->output.video.codec)
{
m_pConfig->output.video.codec = m_info.video.codec;
}
if (0 == m_pConfig->output.video.width)
{
m_pConfig->output.video.width = m_info.video.width;
}
if (0 == m_pConfig->output.video.height)
{
m_pConfig->output.video.height = m_info.video.height;
}
if (0 == m_pConfig->output.video.framerate)
{
m_pConfig->output.video.framerate = 25;
}
if (m_pConfig->output.video.codec != m_info.video.codec ||
m_pConfig->output.video.width != m_info.video.width ||
m_pConfig->output.video.height != m_info.video.height)
{
m_pVideoDecoder = new CVideoDecoder;
if (NULL == m_pVideoDecoder)
{
m_nVideoRecodec = -1;
log_print(HT_LOG_ERR, "%s, new video decoder failed\r\n", __FUNCTION__);
return -1;
}
uint8 extra[1024];
int extra_len = sizeof(extra);
getExtraData(extra, &extra_len);
if (!m_pVideoDecoder->init(m_info.video.codec, extra, extra_len))
{
m_nVideoRecodec = -1;
log_print(HT_LOG_ERR, "%s, video decoder init failed\r\n", __FUNCTION__);
return -1;
}
m_pVideoDecoder->setCallback(media_proxy_vdecoder_cb, this);
m_pVideoEncoder = new CVideoEncoder;
if (NULL == m_pVideoEncoder)
{
m_nVideoRecodec = -1;
log_print(HT_LOG_ERR, "%s, new video encoder failed\r\n", __FUNCTION__);
return -1;
}
VideoEncoderParam params;
memset(&params, 0, sizeof(params));
params.SrcWidth = m_info.video.width;
params.SrcHeight = m_info.video.height;
params.SrcPixFmt = AV_PIX_FMT_YUV420P;
params.DstCodec = m_pConfig->output.video.codec;
params.DstWidth = m_pConfig->output.video.width;
params.DstHeight = m_pConfig->output.video.height;
params.DstFramerate = m_pConfig->output.video.framerate;
params.DstBitrate = m_pConfig->output.video.bitrate;
if (FALSE == m_pVideoEncoder->init(&params))
{
m_nVideoRecodec = -1;
log_print(HT_LOG_ERR, "%s, video encoder init failed\r\n", __FUNCTION__);
return -1;
}
m_pVideoEncoder->addCallback(media_proxy_vencoder_cb, this);
m_nVideoRecodec = 1;
}
else
{
m_nVideoRecodec = -1;
}
return m_nVideoRecodec;
}
/**
* 1 - need
* 0 - Still not sure
* -1 - not need
*/
int CMediaProxy::needAudioRecodec()
{
if (0 != m_nAudioRecodec)
{
return m_nAudioRecodec;
}
if (!m_pConfig->has_output || !m_pConfig->output.has_audio)
{
m_nAudioRecodec = -1;
return -1;
}
if (AUDIO_CODEC_NONE == m_pConfig->output.audio.codec)
{
m_pConfig->output.audio.codec = m_info.audio.codec;
}
if (0 == m_pConfig->output.audio.samplerate)
{
m_pConfig->output.audio.samplerate = m_info.audio.samplerate;
}
if (0 == m_pConfig->output.audio.channels)
{
m_pConfig->output.audio.channels = m_info.audio.channels;
}
if (m_pConfig->output.audio.codec != m_info.audio.codec ||
m_pConfig->output.audio.samplerate != m_info.audio.samplerate ||
m_pConfig->output.audio.channels != m_info.audio.channels)
{
m_pAudioDecoder = new CAudioDecoder;
if (NULL == m_pAudioDecoder)
{
m_nAudioRecodec = -1;
log_print(HT_LOG_ERR, "%s, new audio decoder failed\r\n", __FUNCTION__);
return -1;
}
if (!m_pAudioDecoder->init(m_info.audio.codec, m_info.audio.samplerate, m_info.audio.channels, m_info.audio.bitpersample))
{
m_nAudioRecodec = -1;
log_print(HT_LOG_ERR, "%s, audio decoder init failed\r\n", __FUNCTION__);
return -1;
}
m_pAudioDecoder->setCallback(media_proxy_adecoder_cb, this);
m_pAudioEncoder = new CAudioEncoder;
if (NULL == m_pAudioEncoder)
{
m_nAudioRecodec = -1;
log_print(HT_LOG_ERR, "%s, new audio encoder failed\r\n", __FUNCTION__);
return -1;
}
AudioEncoderParam params;
memset(&params, 0, sizeof(params));
params.SrcSamplerate = m_info.audio.samplerate;
params.SrcChannels = m_info.audio.channels;
params.SrcSamplefmt = AV_SAMPLE_FMT_S16;
params.DstCodec = m_pConfig->output.audio.codec;
params.DstSamplerate = m_pConfig->output.audio.samplerate;
params.DstChannels = m_pConfig->output.audio.channels;
params.DstSamplefmt = AV_SAMPLE_FMT_S16;
params.DstBitrate = m_pConfig->output.audio.bitrate;
if (FALSE == m_pAudioEncoder->init(&params))
{
m_nAudioRecodec = -1;
log_print(HT_LOG_ERR, "%s, audio encoder init failed\r\n", __FUNCTION__);
return -1;
}
m_pAudioEncoder->addCallback(meida_proxy_aencoder_cb, this);
m_nAudioRecodec = 1;
}
else
{
m_nAudioRecodec = -1;
}
return m_nAudioRecodec;
}
void CMediaProxy::videoDataDecode(uint8 * p_data, int len)
{
if (m_pVideoDecoder)
{
m_pVideoDecoder->decode(p_data, len);
}
}
void CMediaProxy::videoDataEncode(AVFrame * frame)
{
if (m_pVideoEncoder)
{
m_pVideoEncoder->encode(frame);
}
}
void CMediaProxy::audioDataDecode(uint8 * p_data, int len)
{
if (m_pAudioDecoder)
{
m_pAudioDecoder->decode(p_data, len);
}
}
void CMediaProxy::audioDataEncode(AVFrame * frame)
{
if (m_pAudioEncoder)
{
m_pAudioEncoder->encode(frame);
}
}
#endif // end of defined(MEDIA_FILE) || defined(MEDIA_DEVICE)
#endif // end of MEDIA_PROXY