LOCAL void *clip_upload_thd_func(void *arg)
{
CLIP_OBJ_S *obj = (CLIP_OBJ_S *)arg;
S32 ret = ERROR;
CLIP_AVDM_TUPLE_S avdm_tuple;
DATA_UNIT_S data_unit;
avdm_cursor_t *p_vc = NULL; /* video cursor */
avdm_unit_t *p_vu = NULL; /* video unit */
#ifdef DUAL_CAM
S32 ret2 = ERROR;
avdm_cursor_t *p_vc2 = NULL; /* video2 cursor */
avdm_unit_t *p_vu2 = NULL; /* video2 unit */
BOOL vu2_start_from_pre_record = TRUE;
BOOL vu2_start_from_i_frame = FALSE;
#endif
avdm_cursor_t *p_ac = NULL; /* audio cursor */
avdm_unit_t *p_au = NULL; /* audio unit */
void *ssl_ctx = NULL;
U64 connect_time = 0; /* 连接建立的持续时间 */
S64 total_data_length = 0; /* clips 总字节大小,统计码率 */
U64 clip_expect_end_pts = 0; /* clips预期结束的pts时间s */
S64 clip_expect_endtime = 0; /* clips预期结束的UTC时间 */
S64 clip_actual_startime = 0; /* clips实际开始的UTC时间 */
S64 clip_actual_endtime = 0; /* clips实际结束的UTC时间 */
U64 clip_actual_start_pts = 0; /* clips实际开始的pts时间 */
U64 clip_actual_end_pts = 0; /* clips实际结束的pts时间 */
U32 old_inSegs = 0; /* clips上传前收包数 */
U32 old_outSegs = 0; /* clips上传前发包数 */
U32 old_retransSegs = 0; /* clips上传前重传报文数 */
U32 inSegs = 0; /* clips上传后收包数 */
U32 outSegs = 0; /* clips上传后发包数 */
U32 retransSegs = 0; /* clips上传后重传报文数 */
S32 rssi = 0; /* clips上传结束时的rssi */
BOOL vu_start_from_pre_record = TRUE; /* 视频帧是否从预录开始上传 */
BOOL au_start_from_pre_record = TRUE; /* 音频帧是否从预录开始上传 */
BOOL vu_start_from_i_frame = FALSE;
S32 hdr_len = 0;
S8 header[1024] = {0};
#ifdef HUB_MANAGE_SUPPORT
unsigned char enhance_para[1024] = {0};
REPLY_BUF_S reply_buf = {{0}};
S8 is_replying_hub = 0;
S32 param_len = 0;
#ifdef AOV_SUPPORT
static S64 max_I_frame_utc_end_time = 0;
static S64 max_P_frame_utc_end_time = 0;
#ifdef DUAL_CAM
static S64 max_I_frame_utc_end_time2 = 0;
static S64 max_P_frame_utc_end_time2 = 0;
#endif /* DUAL_CAM */
#endif
#endif
S32 cur_time = 0, send_block_time = 0;
#ifdef TAPO_CARE_CDN_SUPPORT
//链路类型 1 cdn 2 node url 3 elb
LINK_TYPE link_type = TAPOCARE_LINK_TYPE_NONE;
//最先使用的链路类型
LINK_TYPE first_link_type = TAPOCARE_LINK_TYPE_NONE;
char cdn_ori_host[MAX_HOST_NAME_LEN+1] = {0};
S32 connected_failed_flag = 0;//是否为连接失败
LINK_S link_data[MAX_LINK_NUM+1];
#endif
#ifdef TAPO_CARE_QUIC_SUPPORT
char auth[256] = {0};
QUIC_HEADER_NV_S quic_hdrs[MAX_VIDEO_HEADER_CNT] = {0};
#endif
#ifdef TELEMETRY_SUPPORT
TAPOCARE_TELEMETRY_VIDEO *video_el = get_telemetry_video_info(obj->idx);
#endif
BOOL video_miss_flag = FALSE;
float video_miss_duration = 0;
S64 tmp_clip_startime = 0; /* clips预期开始的UTC时间 */
RETURN_VALUE_IF(!arg, (void *)ERROR);
sprintf((char *)header, "tapo_care_%d", obj->idx);
prctl(PR_SET_NAME, header, 0, 0, 0);
memset(&avdm_tuple, 0, sizeof(CLIP_AVDM_TUPLE_S));
p_vc = &avdm_tuple.video.cursor;
p_ac = &avdm_tuple.audio.cursor;
#ifdef DUAL_CAM
p_vc2 = &avdm_tuple.video2.cursor;
p_vu2 = &avdm_tuple.video2.unit;
#endif
p_vu = &avdm_tuple.video.unit;
p_au = &avdm_tuple.audio.unit;
STREAM_DEBUG("tapo care thread create");
while (obj->thd_stat != CLIP_THD_STAT_FINISHED)
{
if (obj->thd_stat == CLIP_THD_STAT_STOPPED)
{
if (ssl_ctx
#ifdef TAPO_CARE_QUIC_SUPPORT
|| obj->upload_protocol == UPLOAD_PROTOCOL_QUIC
#endif
)
{
S64 expected_duration = 0;
if (obj->clip_endtime > obj->clip_startime)
{
expected_duration = (obj->clip_endtime - obj->clip_startime);
}
if (http_still_connected(ssl_ctx) == TRUE
#ifdef TAPO_CARE_QUIC_SUPPORT
|| obj->upload_protocol == UPLOAD_PROTOCOL_QUIC
#endif
)
{
#ifdef TAPO_CARE_QUIC_SUPPORT
if (obj->upload_protocol == UPLOAD_PROTOCOL_QUIC)
{
xqc_cli_send_finish(obj->xqc_task_idx);
}
#endif
S64 now = (S64)time(NULL);
float actual_duration = 0;
if (clip_actual_end_pts > clip_actual_start_pts)
{
actual_duration = (clip_actual_end_pts - clip_actual_start_pts)/1000000.0;
}
STREAM_INFO("clips upload idx %d stop, expected start ts:%lld, expected end ts: %lld, cur time: %lld, expected duration=%llds.", obj->idx,
obj->clip_startime, obj->clip_endtime, now, expected_duration);
STREAM_INFO("clips upload stop, actual start ts:%lld, actual end ts: %lld, event time: %lld, actual duration=%.3fs.",
clip_actual_startime, clip_actual_endtime, obj->event_time, actual_duration);
if (connect_time)
{
connect_time = nvmp_get_us() - connect_time;
connect_time /= 1000;
}
float last_time = connect_time /1000.0;
float video_bitrate = total_data_length /1000.0 / actual_duration;
float realtime_bitrate = total_data_length /1000.0 / last_time;
#ifdef TAPO_CARE_QUIC_SUPPORT
#ifdef HUB_MANAGE_SUPPORT
if (obj->storage_type == STORAGE_TYPE_CLOUD)
#endif
{
g_video_duration = expected_duration;
g_video_speed = (double)realtime_bitrate;
}
#endif
STREAM_INFO("ts total length: %d, last_time:%.3fs, transmission bitrate: %.3fKB/s, video bitrate: %.3fKB/s",
total_data_length, last_time, realtime_bitrate, video_bitrate);
get_tcp_segs_info(&inSegs, &outSegs, &retransSegs);
inSegs -= old_inSegs;
outSegs -= old_outSegs;
retransSegs -= old_retransSegs;
float retrans = retransSegs * 100.0 / outSegs;
get_rssi_info(&rssi);/* 在video接口结束时再获取一次当前rssi */
STREAM_INFO("RSSI: %d, TCP InSegs: %u, OutSegs:%u, RetransSegs:%u, retrans:%.2f%%\n", rssi, inSegs, outSegs, retransSegs, retrans);
//连接一直建立,但线程状态变为STOPPED,发送完chunk尾部后断开连接
http_send_data_block(ssl_ctx, "0\r\n\r\n", 5);
#if defined(TELEMETRY_SUPPORT) && defined(TP_TAPO_BATTERY_CAM)
video_el->tcp_retrans_rate = retrans;
DYNAMIC_DATA_ST telemetry_data = {0};
telemetry_data.ival = 1;
avts_telemetry_get_dynamic_data(CL_CNT, INT32_ADD, telemetry_data);
memset(&telemetry_data, 0, sizeof(DYNAMIC_DATA_ST));
telemetry_data.dval = (double)(actual_duration);
avts_telemetry_get_dynamic_data(CL_DUR, DOUBLE_ADD, telemetry_data);
}
else
{
get_rssi_info(&rssi);/* 在video接口结束时再获取一次当前rssi */
DYNAMIC_DATA_ST telemetry_data = {0};
telemetry_data.ival = 1;
avts_telemetry_get_dynamic_data(CL_FCNT, INT32_ADD, telemetry_data);
#endif
}
#ifdef TELEMETRY_SUPPORT
if (video_miss_duration > 0)
{
video_el->video_stop_msg = END_BUT_OVERWRITTEN;
}
//最终记录较差的rssi值
if (rssi < (video_el->rssi))
{
video_el->rssi = rssi;
}
video_el->video_end_time = clip_actual_endtime;
video_el->video_duration = (int)(video_el->video_end_time - video_el->video_start_time - video_miss_duration);
video_el->video_upload_cost = get_utctime_ms() - video_el->upload_start_time;
video_el->video_total_size = total_data_length;
video_el->expect_video_duration = expected_duration;
#ifdef HUB_MANAGE_SUPPORT
if (1 != obj->storage_type)
{
send_video_telemetry_data(*video_el);
}
#else
send_video_telemetry_data(*video_el);
#endif
#endif
#ifdef TAPO_CARE_CDN_SUPPORT
#ifdef TELEMETRY_SUPPORT
g_video_integrity_rate = (double)(video_el->video_duration)/(double)((int)(obj->clip_endtime - obj->clip_startime));
#else
g_video_integrity_rate = (double)(clip_actual_end_pts - clip_actual_start_pts - video_miss_duration)/(double)((int)(obj->clip_endtime - obj->clip_startime));
#endif
if (g_video_integrity_rate > 1)
{
/* clips实际结束时间可能会比预期结束时间多一秒,导致完整度大于100% */
g_video_integrity_rate = 1;
}
STREAM_INFO("video_integrity_rate:%f minimum required video_integrity_rate:%f" ,g_video_integrity_rate ,obj->video_integrity_rate);
//如果之前连上了服务器
if ((0 == connected_failed_flag) &&
(g_video_integrity_rate < obj->video_integrity_rate))
{
//视频完整性达不到要求时归责为最先使用的链路
switch (first_link_type)
{
case TAPOCARE_LINK_TYPE_NONE:
break;
case TAPOCARE_LINK_TYPE_CDN:
g_node_url_last_fail_time = 0;
g_last_fail_reason[TAPOCARE_LINK_TYPE_CDN - 1] = g_last_fail_reason[TAPOCARE_LINK_TYPE_CDN - 1] | TAPOCARE_FAIL_REASON_VIDDEO_INTEGRITY;
break;
case TAPOCARE_LINK_TYPE_NODE_URL:
STREAM_INFO("set node_url_last_fail because of video integrity\n");
g_node_url_last_fail_time = time(NULL);
g_last_fail_reason[TAPOCARE_LINK_TYPE_NODE_URL - 1] = g_last_fail_reason[TAPOCARE_LINK_TYPE_NODE_URL - 1] | TAPOCARE_FAIL_REASON_VIDDEO_INTEGRITY;
break;
case TAPOCARE_LINK_TYPE_ELB:
g_last_fail_reason[TAPOCARE_LINK_TYPE_ELB - 1] = g_last_fail_reason[TAPOCARE_LINK_TYPE_ELB - 1] | TAPOCARE_FAIL_REASON_VIDDEO_INTEGRITY;
break;
case TAPOCARE_LINK_TYPE_MAX:
break;
}
}
#endif
#ifdef LAST_FRAME_COMPLETE
if (g_send_left_data_pid[obj->idx] != -1)
{
clip_send_left_data_stop(obj->idx);
}
g_send_left_data_info[obj->idx].index = obj->idx;
g_send_left_data_info[obj->idx].ssl = ssl_ctx;
g_send_left_data_info[obj->idx].stop = FALSE;
#ifdef TAPO_CARE_QUIC_SUPPORT
g_send_left_data_info[obj->idx].upload_protocol = obj->upload_protocol;
g_send_left_data_info[obj->idx].xqc_task_idx = obj->xqc_task_idx;
if (obj->upload_protocol == UPLOAD_PROTOCOL_QUIC)
{
obj->xqc_task_idx = -1;
}
#endif
clip_send_left_data_start(obj->idx);
#else
#ifdef TAPO_CARE_QUIC_SUPPORT
if (obj->upload_protocol == UPLOAD_PROTOCOL_QUIC)
{
obj->xqc_task_idx = -1;
}
else
#endif
{
http_disconnect_server(ssl_ctx);
ssl_ctx = NULL;
}
#endif
media_set_req_state(REQ_TYPE_VIDEO, req_state_idle);
#ifdef AOV_SUPPORT
if (g_stream_type & (1 << STREAM_TYPE_BIT_AOV))
{
STREAM_INFO("stream stop, send aov datafull erase");
avdc_aov_video_event_erase(EVENT_AOV_DATA_MEM_FULL);
avdc_aov_video_event_erase(EVENT_AOV_HUB_CONNECT_STATUS);
}
#endif
}
obj->clip_startime = 0;
obj->clip_endtime = 0;
#ifdef AOV_SUPPORT
obj->aov_record_info.start_time = 0;
obj->aov_record_info.end_time = 0;
#endif
connect_time = 0;
total_data_length = 0;
clip_expect_end_pts = 0;
clip_expect_endtime = 0;
clip_actual_startime = 0;
clip_actual_endtime = 0;
clip_actual_start_pts = 0;
clip_actual_end_pts = 0;
tmp_clip_startime = 0; /* clips预期开始的UTC时间 */
old_inSegs = 0;
old_outSegs = 0;
old_retransSegs = 0;
inSegs = 0;
outSegs = 0;
retransSegs = 0;
rssi = 0;
#ifdef TP_TAPO_BATTERY_CAM
clip_uploading_power_down_delay_notify(obj->idx, FALSE, 0, FALSE);
#endif
obj->event_time = 0;
obj->event_type = 0;
#ifdef TAPO_CARE_CDN_SUPPORT
connected_failed_flag = 0;
#endif
cur_time = 0;
send_block_time = 0;
#ifdef TELEMETRY_SUPPORT
memset(video_el, 0, sizeof(TAPOCARE_TELEMETRY_VIDEO));
#endif
video_miss_duration = 0;
video_miss_flag = FALSE;
vu_start_from_pre_record = TRUE;
#ifdef DUAL_CAM
vu2_start_from_pre_record = TRUE;
vu2_start_from_i_frame = FALSE;
#endif
au_start_from_pre_record = TRUE;
vu_start_from_i_frame = FALSE;
/* 线程暂停,将unit detach */
avdm_detach_unit(AVDM_TYPE_MAIN, p_vc);
avdm_detach_unit(AVDM_TYPE_AUDIO, p_ac);
#ifdef DUAL_CAM
if (TRUE == p_vc2->attaching)
{
avdm_detach_unit(AVDM_TYPE_MAIN2, p_vc2);
}
#endif
memset(&avdm_tuple, 0, sizeof(CLIP_AVDM_TUPLE_S));
obj->upload_stat = UPLOAD_STAT_IDLE;
pthread_mutex_lock(&obj->thd_stat_mutex);
/* 线程处于STOPPED状态才需要等待信号 */
if (obj->thd_stat == CLIP_THD_STAT_STOPPED)
{
pthread_cond_wait(&obj->thd_stat_cond, &obj->thd_stat_mutex);
}
pthread_mutex_unlock(&obj->thd_stat_mutex);
if (obj->thd_stat != CLIP_THD_STAT_RUNNING)
{
continue;
}
// ts buf占用内存空间较大,等线程实际运行后再申请内存
if (!obj->ts_pack && OK != clip_ts_pack_init(&obj->ts_pack))
{
obj->thd_stat = CLIP_THD_STAT_FINISHED;
STREAM_ERROR("clip pack buf init failed,thread(%d) exit.", obj->idx);
continue;
}
#ifdef HUB_MANAGE_SUPPORT
if (!obj->aes_pack)
{
obj->aes_pack = (HUB_AES_UNENC_PACKAGE *)malloc(sizeof(HUB_AES_UNENC_PACKAGE));
}
if (!obj->aes_pack)
{
obj->thd_stat = CLIP_THD_STAT_FINISHED;
STREAM_ERROR("clip aes_pack buf init failed,thread(%d) exit.", obj->idx);
continue;
}
memset(obj->aes_pack, 0, sizeof(HUB_AES_UNENC_PACKAGE));
memset(&reply_buf, 0, sizeof(REPLY_BUF_S));
#endif
STREAM_DEBUG("clips thread start running!");
}
switch (obj->upload_stat)
{
case UPLOAD_STAT_IDLE:
obj->upload_stat = UPLOAD_STAT_CONNECTING;
case UPLOAD_STAT_CONNECTING:
#ifdef TELEMETRY_SUPPORT
video_el->req_time = get_utctime_ms();
video_el->tid = obj->event_time;
/* 在video接口开始时,获取一次当前rssi */
get_rssi_info(&rssi);
video_el->rssi = rssi;
#endif
media_set_req_state(REQ_TYPE_VIDEO, req_state_connecting);
//cdn_enable为1开启cdn链路,否则链路优先级node url>elb
//当cdn_first为1时,链路优先级cdn>node url>elb,否则node url>cdn>elb
#if (defined(HUB_MANAGE_SUPPORT) && defined(TAPO_CARE_CDN_SUPPORT))
//判断当前是否推送到hub
if (obj->storage_type == STORAGE_TYPE_HUB)
{
ssl_ctx = http_connect_server(obj->svr_host, obj->svr_port, obj->storage_type, obj->svr_ip, FALSE, &ret);
if (!ssl_ctx)
{
ssl_ctx = clip_connect_fail(obj, video_el, &ret);
if (!ssl_ctx)
{
break;
}
}
}
else
{
ssl_ctx = cdn_try_connect(obj, link_data, &link_type, &first_link_type
#ifdef TELEMETRY_SUPPORT
, video_el
#endif
, &connected_failed_flag, cdn_ori_host, &ret);
if (!ssl_ctx
#ifdef TAPO_CARE_QUIC_SUPPORT
&& obj->xqc_task_idx < 0
#endif
)
{
break;
}
}
#elif defined(HUB_MANAGE_SUPPORT)
#ifdef TAPO_CARE_QUIC_SUPPORT
if (obj->storage_type == STORAGE_TYPE_CLOUD && obj->upload_protocol == UPLOAD_PROTOCOL_QUIC)
{
obj->xqc_task_idx = quic_connect_server_block(obj->svr_host, obj->svr_port, obj->svr_ip, CA_CRT_PATH, NULL, &ret);
}
else
#endif
{
ssl_ctx = http_connect_server(obj->svr_host, obj->svr_port, obj->storage_type, obj->svr_ip, &ret);
}
#ifdef TELEMETRY_SUPPORT
video_el->node_cost = get_utctime_ms() - video_el->req_time;
#endif
#ifdef TAPO_CARE_QUIC_SUPPORT
if (obj->storage_type == STORAGE_TYPE_CLOUD)
{
g_last_video_link = TAPOCARE_LINK_TYPE_NODE_URL;
#ifdef TELEMETRY_SUPPORT
video_el->node_prot = obj->upload_protocol;
#endif
}
#endif
if (ret < 0)
{
#ifdef TELEMETRY_SUPPORT
video_el->node_fail_reason = telemetry_video_connect_error(ret);
#endif
ssl_ctx = clip_connect_fail(obj, video_el, &ret);
if (!ssl_ctx)
{
break;
}
}
#elif defined(TAPO_CARE_CDN_SUPPORT)
ssl_ctx = cdn_try_connect(obj, link_data, &link_type, &first_link_type
#ifdef TELEMETRY_SUPPORT
, video_el
#endif
, &connected_failed_flag, cdn_ori_host, &ret);
if (!ssl_ctx
#ifdef TAPO_CARE_QUIC_SUPPORT
&& obj->xqc_task_idx < 0
#endif
)
{
break;
}
#else
#ifdef TAPO_CARE_QUIC_SUPPORT
if (obj->upload_protocol == UPLOAD_PROTOCOL_QUIC)
{
obj->xqc_task_idx = quic_connect_server_block(obj->svr_host, obj->svr_port, obj->svr_ip, CA_CRT_PATH, NULL, &ret);
}
else
#endif
{
ssl_ctx = http_connect_server(obj->svr_host, obj->svr_port, obj->svr_ip, &ret);
}
#ifdef TELEMETRY_SUPPORT
video_el->node_fail_reason = telemetry_video_connect_error(ret);
#ifdef TAPO_CARE_QUIC_SUPPORT
video_el->node_prot = obj->upload_protocol;
#endif
#endif
#ifdef TAPO_CARE_QUIC_SUPPORT
g_last_video_link = TAPOCARE_LINK_TYPE_NODE_URL;
#endif
if (ret < 0)
{
ssl_ctx = clip_connect_fail(obj, video_el, &ret);
#ifdef TAPO_CARE_QUIC_SUPPORT
g_last_video_link = TAPOCARE_LINK_TYPE_ELB;
#endif
if (!ssl_ctx)
{
break;
}
}
#endif
media_set_req_state(REQ_TYPE_VIDEO, req_state_connected);
obj->upload_stat = UPLOAD_STAT_CONNECTED;
#if defined(HUB_MANAGE_SUPPORT) && defined(AOV_SUPPORT)
if (STORAGE_TYPE_HUB == obj->storage_type && (g_stream_type & (1 << STREAM_TYPE_BIT_AOV)))
{
avdc_aov_video_flag_set(FLAG_AOV_CTRL_EVENT_FLAG, EVENT_AOV_HUB_CONNECT_STATUS);
}
#endif
case UPLOAD_STAT_CONNECTED:
#ifdef HUB_MANAGE_SUPPORT
if (1 == obj->storage_type)
{
/* 推流时无需区分 channel,传入 0 即可 */
hdr_len = http_build_hub_header(HUB_REQ_TYPE_HUB_VIDEO, obj->svr_host, obj->event_type, obj->event_subtype,
obj->event_time, 0, -1, -1, obj->storage_nonce, (char *)header, sizeof(header), obj->rec_type, g_stream_type, 0);
}
else
#endif
#ifdef TAPO_CARE_QUIC_SUPPORT
if (obj->upload_protocol == UPLOAD_PROTOCOL_QUIC)
{
memset(auth, 0, sizeof(auth));
snprintf(auth, sizeof(auth), "%s:%d", obj->svr_host, obj->svr_port);
#ifdef TAPO_CARE_CDN_SUPPORT
quic_build_header(REQ_TYPE_VIDEO, cdn_ori_host, auth, obj->xtoken, obj->event_type, obj->event_time, quic_hdrs);
#else
quic_build_header(REQ_TYPE_VIDEO, NULL, auth, obj->xtoken, obj->event_type, obj->event_time, quic_hdrs);
#endif
}
else
#endif
{
hdr_len = http_build_header(REQ_TYPE_VIDEO, obj->svr_host, obj->xtoken, obj->event_type, obj->event_time
#ifdef TELEMETRY_SUPPORT
, video_el->tid
#else
, -1
#endif
, -1, (char *)header, sizeof(header)
#ifdef TAPO_CARE_CDN_SUPPORT
, link_type, cdn_ori_host
#endif
#ifdef DUAL_ALGO_ENABLE
/* 视频上传无需区分chn */
, 0
#endif
);
}
#ifdef TAPO_CARE_QUIC_SUPPORT
if (obj->upload_protocol == UPLOAD_PROTOCOL_QUIC)
{
ret = xqc_cli_send_header(obj->xqc_task_idx, quic_hdrs, sizeof(quic_hdrs)/sizeof(QUIC_HEADER_NV_S));
}
else
#endif
{
http_dump_send_msg((char *)header, NULL);
ret = http_send_data_block(ssl_ctx, header, hdr_len);
}
if ((ret != hdr_len
#ifdef TAPO_CARE_QUIC_SUPPORT
&& obj->upload_protocol != UPLOAD_PROTOCOL_QUIC) || (ret < 0
#endif
))
{
STREAM_ERROR_LOG("http send head failed, ret:%d, buf_len:%d.\n", ret, hdr_len);
#ifdef TELEMETRY_SUPPORT
video_el->elb_fail_reason = CONN_ERR_SEND_PACKET;
#endif
clip_stop_upload(obj->idx);
break;
}
#ifdef TELEMETRY_SUPPORT
video_el->upload_start_time = get_utctime_ms();
#endif
#ifdef HUB_MANAGE_SUPPORT
if (1 == obj->storage_type && 1 == obj->enhance_switch)
{
STREAM_INFO("enhance param will build");
sync_enhance_param(obj, enhance_para, ¶m_len, sizeof(enhance_para));
ret = http_send_data_block(ssl_ctx, enhance_para, param_len);
if (ret != param_len)
{
STREAM_ERROR_LOG("send enhance param failed, ret:%d, param_len:%d.\n", ret, param_len);
clip_stop_upload(obj->idx);
break;
}
obj->seq += 1;
}
#endif
connect_time = nvmp_get_us();
get_tcp_segs_info(&old_inSegs, &old_outSegs, &old_retransSegs);
STREAM_DEBUG("http clips send head success!\n");
obj->audio_need_sync = FALSE;
obj->upload_stat = UPLOAD_STAT_TAKE;
case UPLOAD_STAT_TAKE:
/* 上一视频帧pts时间戳大于视频截止时间,停止视频上传 */
#ifndef DUAL_CAM
if (clip_expect_end_pts && p_vu->pts > clip_expect_end_pts)
{
STREAM_INFO("clip stop, start pts:%lld, end pts:%lld, current vu pts:%lld, au utc:%lld.\n",
clip_actual_start_pts, clip_expect_end_pts, p_vu->pts, p_au->pts);
#ifdef TELEMETRY_SUPPORT
video_el->video_stop_msg = END_NORMAL;
#endif
#if defined(AOV_SUPPORT) && defined(HUB_MANAGE_SUPPORT)
if (max_I_frame_utc_end_time < max_P_frame_utc_end_time)
{
max_I_frame_utc_end_time = max_P_frame_utc_end_time;
}
#endif
clip_stop_upload(obj->idx);
break;
}
#else
if (clip_expect_endtime && p_vu->utc_time > clip_expect_endtime)
{
avdm_tuple.video.frame_state = TAKE_FRAME_STATE_STOP;
}
if (clip_expect_endtime && p_vu2->utc_time > clip_expect_endtime)
{
avdm_tuple.video2.frame_state = TAKE_FRAME_STATE_STOP;
}
//为避免两个镜头帧率不同导致的长度差异,仅当两者都取到满足条件后退出。
if ((TAKE_FRAME_STATE_STOP == avdm_tuple.video.frame_state) && (TAKE_FRAME_STATE_STOP == avdm_tuple.video2.frame_state))
{
STREAM_INFO("p_vu->utc_time:%llu p_vu2->utc_time:%llu au %llu, clip_expect_endtime:%llu", p_vu->utc_time, p_vu2->utc_time, p_au->utc_time, clip_expect_endtime);
#ifdef TELEMETRY_SUPPORT
video_el->video_stop_msg = END_NORMAL;
#endif
#if defined(AOV_SUPPORT) && defined(HUB_MANAGE_SUPPORT)
if (max_I_frame_utc_end_time < max_P_frame_utc_end_time)
{
max_I_frame_utc_end_time = max_P_frame_utc_end_time;
}
if (max_I_frame_utc_end_time2 < max_P_frame_utc_end_time2)
{
max_I_frame_utc_end_time2 = max_P_frame_utc_end_time2;
}
#endif /* AOV_SUPPORT & HUB_MANAGE_SUPPORT */
clip_stop_upload(obj->idx);
break;
}
#endif
avdm_tuple.video.frame_state = (avdm_tuple.video.is_update ? avdm_tuple.video.frame_state: TAKE_FRAME_STATE_IDLE);
avdm_tuple.audio.frame_state = (avdm_tuple.audio.is_update ? avdm_tuple.audio.frame_state: TAKE_FRAME_STATE_IDLE);
#ifdef DUAL_CAM
avdm_tuple.video2.frame_state = (avdm_tuple.video2.is_update ? avdm_tuple.video2.frame_state: TAKE_FRAME_STATE_IDLE);
#endif
if (avdm_tuple.video.is_update)
{
#ifdef DUAL_CAM
goto take_cam2;
#else
goto take_audio;
#endif
}
if (vu_start_from_pre_record)
{
ret = avdm_get_pre_unit(AVDM_TYPE_MAIN, p_vc, p_vu);
if (ERR_NONE == ret)
{
vu_start_from_pre_record = FALSE;
avdm_tuple.video.is_update = TRUE;
#ifdef TP_TAPO_BATTERY_CAM
/*如首次上传视频帧,取预录帧应参考clip_startime,反之取预录帧应参考clip_actual_end_pts */
if (!clip_actual_end_pts)
{
int start_I_bias = 1; // 取得I帧时间戳的间隔距离默认为1s
#ifdef AOV_SUPPORT
if (g_stream_type & (1 << STREAM_TYPE_BIT_AOV) && 0 != obj->aov_record_info.start_time)
{
tmp_clip_startime = obj->aov_record_info.start_time;
start_I_bias = 0; // 对于 AOV 低帧率而言转为 0s, 因为低帧率的utc时间是准的
}
else
#endif
if (p_vu->utc_time <= obj->clip_startime)
{
tmp_clip_startime = obj->clip_startime;
}
else
{
STREAM_WARN_LOG("get pre vu failed, clip start time: %llu < vu utc: %llu", obj->clip_startime, p_vu->utc_time);
}
#if defined(HUB_MANAGE_SUPPORT) && defined(AOV_SUPPORT)
if (1 == obj->storage_type && (g_stream_type & (1 << STREAM_TYPE_BIT_AOV)) // 开启 HUB 存储并且开启 AOV 时
&& max_I_frame_utc_end_time > tmp_clip_startime // 不允许 utc 时间回退
&& (max_I_frame_utc_end_time - tmp_clip_startime < HUB_AOV_MAX_TIME_DIFF) // 除了时间回退已经超过 HUB_AOV_MAX_TIME_DIFF 的范围
)
{
tmp_clip_startime = max_I_frame_utc_end_time;
}
#endif
// 确保首帧必须是I帧
while ((tmp_clip_startime) && (p_vu->utc_time < tmp_clip_startime || TP_FRAME_TYPE_I != clip_check_video_frame_type((FRAME_HEAD_S *)p_vu->addr)))
{
/* I帧时间戳距离clip起始时间 start_I_bias (1s)以内,则不必等到clip起始时间,直接取该I帧 */
if (TP_FRAME_TYPE_I == clip_check_video_frame_type((FRAME_HEAD_S *)p_vu->addr) &&
(p_vu->utc_time + start_I_bias) >= tmp_clip_startime)
{
break;
}
ret = avdm_get_next_unit(AVDM_TYPE_MAIN, p_vc, p_vu);
if (ERR_NONE != ret)
{
vu_start_from_pre_record = TRUE;
avdm_tuple.video.is_update = FALSE;
STREAM_WARN_LOG("get pre video unit failed, clip start time: %lld, ret = %d\n",
tmp_clip_startime, ret);
break;
}
}
if (ERR_NONE == ret)
{
STREAM_INFO("first vu pts: %llu, vu utc: %llu, clip_startime: %llu.",
p_vu->pts, p_vu->utc_time, obj->clip_startime);
}
}
else
{
/* 从预录开始从前向后搜索没有上传过的、最早的帧数据,且视频帧必须从I帧开始 */
while (p_vu->pts <= clip_actual_end_pts || TP_FRAME_TYPE_I != clip_check_video_frame_type((FRAME_HEAD_S *)p_vu->addr))
{
ret = avdm_get_next_unit(AVDM_TYPE_MAIN, p_vc, p_vu);
if (ERR_NONE != ret)
{
/* 预录搜索出现视频帧被覆盖,放弃从预录搜索逻辑,从最近一个I帧开始上传 */
if (-ERR_OW_GET_LATEST == ret)
{
STREAM_WARN_LOG("vu overwritten while searching, next frame type: %s frame, vu: %lld",
(TP_FRAME_TYPE_I == clip_check_video_frame_type((FRAME_HEAD_S *)p_vu->addr))? "I":"P", p_vu->utc_time);
if (TP_FRAME_TYPE_I != clip_check_video_frame_type((FRAME_HEAD_S *)p_vu->addr))
{
vu_start_from_i_frame = TRUE;
avdm_tuple.video.is_update = FALSE;
ret = -ERR_FRAME_NOT_READY;
}
else
{
ret = ERR_NONE;
}
}
else
{
vu_start_from_pre_record = TRUE;
avdm_tuple.video.is_update = FALSE;
STREAM_WARN_LOG("get pre vu failed, clip actual end pts: %lld, ret = %d.",
clip_actual_end_pts, ret);
}
break;
}
}
if (ERR_NONE == ret)
{
/* 跳帧后从预录搜索可上传视频帧,取到视频帧时间戳大于截止时间,则停止上传 */
if (clip_expect_end_pts && p_vu->pts > clip_expect_end_pts)
{
STREAM_INFO("clip stop after vu overwritten, start pts:%lld, end pts:%lld, current vu pts:%lld.\n",
clip_actual_start_pts, clip_expect_end_pts, p_vu->pts);
#ifdef TELEMETRY_SUPPORT
video_el->video_stop_msg = END_BUT_OVERWRITTEN;
#endif
clip_stop_upload(obj->idx);
break;
}
STREAM_INFO("get pre vu, vu utc: %llu, vu pts: %llu, clip_actual_end_pts: %llu.",
p_vu->utc_time, p_vu->pts, clip_actual_end_pts);
}
}
#endif
}
}
else
{
ret = avdm_get_next_unit(AVDM_TYPE_MAIN, p_vc, p_vu);
if (ERR_NONE == ret)
{
if (vu_start_from_i_frame)
{
if (TP_FRAME_TYPE_I != clip_check_video_frame_type((FRAME_HEAD_S *)p_vu->addr))
{
ret = -ERR_FRAME_NOT_READY;
}
else
{
vu_start_from_i_frame = FALSE;
avdm_tuple.video.is_update = TRUE;
STREAM_WARN_LOG("get I frame, vu utc: %lld, vu pts: %llu.\n", p_vu->utc_time, p_vu->pts);
}
}
else
{
avdm_tuple.video.is_update = TRUE;
STREAM_INFO("\033[45;37mVIDEO\033[0m: %lld\n", p_vu->utc_time);
}
}
}
if ((-ERR_FRAME_NOT_READY == ret) || (-ERR_AVDM_NOT_READY == ret))
{
avdm_tuple.video.is_update = FALSE;
avdm_tuple.video.frame_state = TAKE_FRAME_STATE_NOT_READY;
}
else
{
// 第一镜头取帧完毕,若此时异常退出,不再取第一镜头帧
avdm_tuple.video.frame_state = TAKE_FRAME_STATE_GET;
}
#ifdef DUAL_CAM
take_cam2:
if (avdm_tuple.video2.is_update)
{
goto take_audio;
}
if (vu2_start_from_pre_record)
{
ret2 = avdm_get_pre_unit(AVDM_TYPE_MAIN2, p_vc2, p_vu2);
if (ERR_NONE == ret2)
{
vu2_start_from_pre_record = FALSE;
avdm_tuple.video2.is_update = TRUE;
#ifdef TP_TAPO_BATTERY_CAM
/*如首次上传视频帧,取预录帧应参考clip_startime,反之取预录帧应参考clip_actual_end_pts */
if (!clip_actual_end_pts)
{
/* 取得I帧时间戳的间隔距离默认为1s */
int start_I_bias2 = 1;
#ifdef AOV_SUPPORT
if (g_stream_type & (1 << STREAM_TYPE_BIT_AOV) && 0 != obj->aov_record_info.start_time)
{
tmp_clip_startime = obj->aov_record_info.start_time;
/* 对于 AOV 低帧率而言转为 0s, 因为低帧率的utc时间是准的 */
start_I_bias2 = 0;
}
else
#endif /* AOV_SUPPORT */
if (p_vu2->utc_time <= obj->clip_startime)
{
tmp_clip_startime = obj->clip_startime;
}
else
{
STREAM_WARN_LOG("get pre vu failed, clip start time: %llu < vu utc: %llu", obj->clip_startime, p_vu2->utc_time);
}
#if defined(HUB_MANAGE_SUPPORT) && defined(AOV_SUPPORT)
if (1 == obj->storage_type && (g_stream_type & (1 << STREAM_TYPE_BIT_AOV)) /* 开启 HUB 存储并且开启 AOV 时 */
&& max_I_frame_utc_end_time2 > tmp_clip_startime /* 不允许 utc 时间回退 */
&& (max_I_frame_utc_end_time2 - tmp_clip_startime < HUB_AOV_MAX_TIME_DIFF) /* 除了时间回退已经超过 HUB_AOV_MAX_TIME_DIFF 的范围 */
)
{
tmp_clip_startime = max_I_frame_utc_end_time2;
}
#endif /* HUB_MANAGE_SUPPORT & AOV_SUPPORT */
// 确保首帧必须是I帧
while ((tmp_clip_startime) && (p_vu2->utc_time < tmp_clip_startime || TP_FRAME_TYPE_I != clip_check_video_frame_type((FRAME_HEAD_S *)p_vu2->addr)))
{
/* I帧时间戳距离clip起始时间1s以内,则不必等到clip起始时间,直接取该I帧 */
if (TP_FRAME_TYPE_I == clip_check_video_frame_type((FRAME_HEAD_S *)p_vu2->addr) &&
(p_vu2->utc_time + start_I_bias2) >= tmp_clip_startime)
{
break;
}
ret2 = avdm_get_next_unit(AVDM_TYPE_MAIN2, p_vc2, p_vu2);
if (ERR_NONE != ret2)
{
vu2_start_from_pre_record = TRUE;
avdm_tuple.video2.is_update = FALSE;
STREAM_WARN_LOG("get pre video unit failed, clip start time: %lld, ret = %d\n",
tmp_clip_startime, ret2);
break;
}
}
if (ERR_NONE == ret2)
{
STREAM_INFO("first vu pts: %llu, vu utc: %llu, clip_startime: %llu.",
p_vu2->pts, p_vu2->utc_time, obj->clip_startime);
}
}
else
{
/* 从预录开始从前向后搜索没有上传过的、最早的帧数据,且视频帧必须从I帧开始 */
while (p_vu2->pts <= clip_actual_end_pts || TP_FRAME_TYPE_I != clip_check_video_frame_type((FRAME_HEAD_S *)p_vu2->addr))
{
ret2 = avdm_get_next_unit(AVDM_TYPE_MAIN2, p_vc2, p_vu2);
if (ERR_NONE != ret2)
{
/* 预录搜索出现视频帧被覆盖,放弃从预录搜索逻辑,从最近一个I帧开始上传 */
if (-ERR_OW_GET_LATEST == ret2)
{
STREAM_WARN_LOG("vu overwritten while searching, next frame type: %s frame, vu: %lld",
(TP_FRAME_TYPE_I == clip_check_video_frame_type((FRAME_HEAD_S *)p_vu2->addr))? "I":"P", p_vu2->utc_time);
if (TP_FRAME_TYPE_I != clip_check_video_frame_type((FRAME_HEAD_S *)p_vu2->addr))
{
vu2_start_from_i_frame = TRUE;
avdm_tuple.video2.is_update = FALSE;
ret2 = -ERR_FRAME_NOT_READY;
}
else
{
ret2 = ERR_NONE;
}
}
else
{
vu2_start_from_pre_record = TRUE;
avdm_tuple.video2.is_update = FALSE;
STREAM_WARN_LOG("get pre vu failed, clip actual end pts: %lld, ret = %d.",
clip_actual_end_pts, ret2);
}
break;
}
}
}
#endif
}
}
else
{
ret2 = avdm_get_next_unit(AVDM_TYPE_MAIN2, p_vc2, p_vu2);
if (ERR_NONE == ret2)
{
if (vu2_start_from_i_frame)
{
if (TP_FRAME_TYPE_I != clip_check_video_frame_type((FRAME_HEAD_S *)p_vu2->addr))
{
ret2 = -ERR_FRAME_NOT_READY;
}
else
{
vu2_start_from_i_frame = FALSE;
avdm_tuple.video2.is_update = TRUE;
STREAM_WARN_LOG("get I frame, vu utc: %lld, vu pts: %llu.\n", p_vu2->utc_time, p_vu2->pts);
}
}
else
{
avdm_tuple.video2.is_update = TRUE;
//STREAM_DEBUG("\033[45;37mVIDEO2\033[0m: %llu\n", p_vu2->utc_time);
}
}
}
if ((-ERR_FRAME_NOT_READY == ret2) || (-ERR_AVDM_NOT_READY == ret2))
{
avdm_tuple.video2.is_update = FALSE;
avdm_tuple.video2.frame_state = TAKE_FRAME_STATE_NOT_READY;
}
else
{
//第二镜头取帧成功,异常退出不取第二镜头帧。
avdm_tuple.video2.frame_state = TAKE_FRAME_STATE_GET;
}
#endif
/*如果视频帧已被覆盖,需要detach avdm并从预录位置搜索最早的I帧 */
if (-ERR_OW_GET_LATEST == ret
#ifdef DUAL_CAM
|| -ERR_OW_GET_LATEST == ret2
#endif
)
{
STREAM_WARN_LOG("video frame overwritten, next frame type: %s frame, vu: %lld, au: %lld",
(TP_FRAME_TYPE_I == clip_check_video_frame_type((FRAME_HEAD_S *)p_vu->addr))? "I":"P", p_vu->utc_time, p_au->utc_time);
if (TRUE == p_vc->attaching)
{
avdm_detach_unit(AVDM_TYPE_MAIN, p_vc);
}
if (TRUE == p_ac->attaching)
{
avdm_detach_unit(AVDM_TYPE_AUDIO, p_ac);
}
#ifdef DUAL_CAM
if (TRUE == p_vc2->attaching)
{
avdm_detach_unit(AVDM_TYPE_MAIN2, p_vc2);
}
#endif
memset(&avdm_tuple, 0, sizeof(CLIP_AVDM_TUPLE_S));
vu_start_from_pre_record = TRUE;
au_start_from_pre_record = TRUE;
#ifdef DUAL_CAM
vu2_start_from_pre_record = TRUE;
#endif
video_miss_flag = TRUE;
continue;
}
#ifdef HUB_MANAGE_SUPPORT
#ifdef AOV_SUPPORT
/* AOV模式下帧间隔较长,为避免重复,需要判断最新取出帧的结束时间是否已大于预期时间 */
if (clip_expect_endtime && p_vu->utc_time > clip_expect_endtime && (g_stream_type & (1 << STREAM_TYPE_BIT_AOV))
#ifdef DUAL_CAM
&& (p_vu2->utc_time > clip_expect_endtime)
#endif /* DUAL_CAM */
)
{
STREAM_INFO("clip stop, start pts:%lld, end pts:%lld, lastest vu pts:%lld.\n",
clip_actual_start_pts, clip_expect_end_pts, p_vu->pts);
clip_stop_upload(obj->idx);
if (max_I_frame_utc_end_time < max_P_frame_utc_end_time)
{
max_I_frame_utc_end_time = max_P_frame_utc_end_time;
}
#ifdef DUAL_CAM
if (max_I_frame_utc_end_time2 < max_P_frame_utc_end_time2)
{
max_I_frame_utc_end_time2 = max_P_frame_utc_end_time2;
}
#endif /* DUAL_CAM */
break;
}
#endif
while(0 != g_event_time[0].event_time && p_vu->utc_time >= g_event_time[0].event_time)
{
if (0 == (g_stream_type & (1 << STREAM_TYPE_BIT_CLIP)) && g_event_time[0].event_end_time > p_vu->utc_time)
{
clip_detect_event_sync(TRUE);
break;
}
else if (g_event_time[0].event_end_time < p_vu->utc_time)
{
clip_detect_event_sync(FALSE);
pop_event_time_header();
}
else
{
break;
}
}
#endif
take_audio:
if (au_start_from_pre_record)
{
ret = avdm_get_pre_unit(AVDM_TYPE_AUDIO, p_ac, p_au);
if (ERR_NONE == ret)
{
while (p_au->pts < p_vu->pts)
{
ret = avdm_get_next_unit(AVDM_TYPE_AUDIO, p_ac, p_au);
if (ERR_NONE != ret)
{
STREAM_WARN_LOG("get pre au failed, vu pts: %lld, ret = -%d\n", p_vu->pts, ret);
break;
}
}
if (ERR_NONE == ret)
{
avdm_tuple.audio.is_update = TRUE;
au_start_from_pre_record = FALSE;
STREAM_INFO("get pre au, au pts: %llu, au utc:%llu", p_au->pts, p_au->utc_time);
}
}
}
else
{
if (!avdm_tuple.audio.is_update && p_au->pts <= p_vu->pts)
{
ret = avdm_get_next_unit(AVDM_TYPE_AUDIO, p_ac, p_au);
if (ERR_NONE == ret)
{
avdm_tuple.audio.is_update = TRUE;
STREAM_INFO("\033[44;37mAUDIO\033[0m: %llu, vu %llu\n", p_au->pts, p_vu->pts);
}
}
}
if (-ERR_OW_GET_LATEST == ret)
{
STREAM_WARN_LOG("audio frame overwritten,vu: %lld,au: %lld", p_vu->utc_time, p_au->utc_time);
avdm_tuple.audio.is_update = TRUE;
}
if ((-ERR_FRAME_NOT_READY == ret) || (-ERR_AVDM_NOT_READY == ret))
{
avdm_tuple.audio.is_update = FALSE;
avdm_tuple.audio.frame_state = TAKE_FRAME_STATE_NOT_READY;
}
else
{
avdm_tuple.audio.frame_state = TAKE_FRAME_STATE_GET;
}
/* 取不到音频帧,将音视频的状态复位 */
if (obj->audio_need_sync && !avdm_tuple.audio.is_update)
{
obj->audio_need_sync = FALSE;
if (TAKE_FRAME_STATE_WAITTING == avdm_tuple.video.frame_state)
{
avdm_tuple.video.frame_state = TAKE_FRAME_STATE_GET;
}
#ifdef DUAL_CAM
if (TAKE_FRAME_STATE_WAITTING == avdm_tuple.video2.frame_state)
{
avdm_tuple.video2.frame_state = TAKE_FRAME_STATE_GET;
}
#endif
}
/* 如果音视频流均无获取到数据,则等待信号触发 */
if (avdm_tuple.video.frame_state == TAKE_FRAME_STATE_NOT_READY && (avdm_tuple.audio.frame_state == TAKE_FRAME_STATE_NOT_READY)
#ifdef DUAL_CAM
&& avdm_tuple.video2.frame_state == TAKE_FRAME_STATE_NOT_READY
#endif
)
{
/* 线程处于RUNNING状态才需要等待信号 */
if (obj->thd_stat == CLIP_THD_STAT_RUNNING)
{
STREAM_INFO("frame not ready, vu: %llu pts %llu, au: %llu, pts %llu", p_vu->utc_time, p_vu->pts, p_au->utc_time, p_au->pts);
usleep(40*1000);
}
continue;
}
if ((avdm_tuple.video.is_update
#ifdef DUAL_CAM
|| avdm_tuple.video2.is_update
#endif
) && avdm_tuple.audio.is_update)
{
avdm_tuple.video.frame_state = TAKE_FRAME_STATE_GET;
avdm_tuple.audio.frame_state = TAKE_FRAME_STATE_GET;
#ifdef DUAL_CAM
avdm_tuple.video2.frame_state = TAKE_FRAME_STATE_GET;
#endif
if (labs((S32)p_vu->utc_time - (S32)p_au->utc_time) > 2)
{
STREAM_INFO("vu utc(%lld) and au utc(%lld) not sync.", p_vu->utc_time, p_au->utc_time);
/* : 视频时间大于音频时间,则继续取靠后的音频,直到时间同步 */
if (p_vu->utc_time > p_au->utc_time)
{
do
{
ret = avdm_get_next_unit(AVDM_TYPE_AUDIO, p_ac, p_au);
// 1.取不到音频帧,本次传输不携带audio帧
if (ret != ERR_NONE)
{
avdm_tuple.audio.is_update = FALSE;
break;
}
}
while(labs((S32)p_vu->utc_time - (S32)p_au->utc_time) > 2);
STREAM_INFO("audio is update:%d, vu utc: %lld, and au utc: %lld.",
avdm_tuple.audio.is_update, p_vu->utc_time, p_au->utc_time);
}
/* : 音频时间大于视频时间,暂停取音频帧,先传输视频,直到音频视频时间同步,再一起传输 */
else
{
avdm_tuple.audio.frame_state = TAKE_FRAME_STATE_WAITTING;
}
}
else
{
/* 视频帧大于音频帧,暂停发送视频帧,让音频先处理 */
obj->audio_need_sync = FALSE;
if (p_au->utc_time < p_vu->utc_time && p_au->pts < p_vu->pts)
{
obj->audio_need_sync = TRUE;
avdm_tuple.video.frame_state = TAKE_FRAME_STATE_WAITTING;
#ifdef DUAL_CAM
avdm_tuple.video2.frame_state = TAKE_FRAME_STATE_WAITTING;
#endif
}
/* 音频帧大于视频帧,暂停发送音频 */
else if (p_au->utc_time > p_vu->utc_time)
{
avdm_tuple.audio.frame_state = TAKE_FRAME_STATE_WAITTING;
}
}
}
/* 每次取完帧,重置TS未封包的剩余长度 */
obj->ts_pack->ts_unpack_len = 0;
#ifdef DUAL_CAM
// 取到的帧是否发送,做如下同步处理
/* 音频同步期间暂不处理 */
if (!obj->audio_need_sync)
{
if (avdm_tuple.video.is_update && avdm_tuple.video2.is_update)
{
//两个镜头都准备发送帧时,通过时间戳对比,避免因帧率导致的两镜头时长差异过大
if (p_vu->utc_time > p_vu2->utc_time)
{
avdm_tuple.video.frame_state = TAKE_FRAME_STATE_WAITTING;
}
if(p_vu2->utc_time > p_vu->utc_time)
{
avdm_tuple.video2.frame_state = TAKE_FRAME_STATE_WAITTING;
}
}
if (TAKE_FRAME_STATE_WAITTING == avdm_tuple.video.frame_state && !(p_vu->utc_time > p_vu2->utc_time))
{
avdm_tuple.video.frame_state = TAKE_FRAME_STATE_GET;
}
if (TAKE_FRAME_STATE_WAITTING == avdm_tuple.video2.frame_state && !(p_vu2->utc_time > p_vu->utc_time))
{
avdm_tuple.video2.frame_state = TAKE_FRAME_STATE_GET;
}
}
#endif
obj->upload_stat = UPLOAD_STAT_PACK;
case UPLOAD_STAT_PACK:
#ifdef HUB_MANAGE_SUPPORT
if (1 == obj->storage_type)
{
ret = hub_clip_data_pack(&avdm_tuple, &data_unit, obj);
}
else
#endif
{
ret = clip_data_pack(&avdm_tuple, obj->ts_pack, obj->record_audio);
if (OK == ret)
{
/* 每次封完包,重置数据待发送数据信息 */
memset(&data_unit, 0, sizeof(DATA_UNIT_S));
}
}
if (OK != ret)
{
STREAM_ERROR_LOG("pack failed, vu utc:%d, au utc: %d.", (int)p_vu->utc_time, (int)p_au->utc_time);
/* 当前帧打包失败,跳到取下一个I帧 */
vu_start_from_i_frame = TRUE;
obj->upload_stat = UPLOAD_STAT_TAKE;
break;
}
obj->upload_stat = UPLOAD_STAT_SEND;
case UPLOAD_STAT_SEND:
#ifdef HUB_MANAGE_SUPPORT
if (1 == obj->storage_type)
{
ret = hub_clip_data_send(ssl_ctx, obj->aes_pack, &data_unit, &total_data_length);
}
else
#endif
#ifdef TAPO_CARE_QUIC_SUPPORT
if (obj->upload_protocol == UPLOAD_PROTOCOL_QUIC)
{
ret = quic_data_send(obj->xqc_task_idx, obj->ts_pack->ts_buf, obj->ts_pack->ts_buf_len, &total_data_length);
if (ret == XQC_CLI_TRY_AGAIN)
{
break;
}
}
else
#endif
{
ret = clip_data_send(ssl_ctx ,obj->ts_pack, &data_unit, &total_data_length);
}
if (ret == DATA_SEND_SUCCESS)
{
send_block_time = 0;
#ifdef HUB_MANAGE_SUPPORT
if (is_replying_hub)
{
is_replying_hub = 0;
obj->upload_stat = UPLOAD_STAT_TAKE;
break;
}
#endif
// 视频或音频还剩余未打包完成
if ((avdm_tuple.video.is_update && avdm_tuple.video.frame_state == TAKE_FRAME_STATE_GET)
|| (avdm_tuple.audio.is_update && avdm_tuple.audio.frame_state == TAKE_FRAME_STATE_GET)
#ifdef DUAL_CAM
|| (avdm_tuple.video2.is_update && avdm_tuple.video2.frame_state == TAKE_FRAME_STATE_GET)
#endif
)
{
obj->upload_stat = UPLOAD_STAT_PACK;
}
else
{
/* 更新时间戳信息 */
if (!clip_actual_startime)
{
clip_actual_startime = p_vu->utc_time;
#ifdef TELEMETRY_SUPPORT
video_el->video_start_time = clip_actual_startime;
#endif
}
if (clip_actual_endtime != p_vu->utc_time)
{
clip_actual_endtime = p_vu->utc_time;
}
if (!clip_actual_start_pts)
{
clip_actual_start_pts = p_vu->pts;
}
if (clip_actual_end_pts != p_vu->pts)
{
if (video_miss_flag) // 若出现跳帧的情况,同时记录缺失的视频长度
{
video_miss_duration += (p_vu->pts - clip_actual_end_pts)/1000000.0;
video_miss_flag = FALSE;
}
clip_actual_end_pts = p_vu->pts;
}
#if defined(HUB_MANAGE_SUPPORT) && defined(AOV_SUPPORT)
if (TP_FRAME_TYPE_I == clip_check_video_frame_type((FRAME_HEAD_S *)p_vu->addr))
{
if (max_I_frame_utc_end_time < p_vu->utc_time || (max_I_frame_utc_end_time - p_vu->utc_time > HUB_AOV_MAX_TIME_DIFF))
{
max_I_frame_utc_end_time = p_vu->utc_time;
}
}
if (max_P_frame_utc_end_time < p_vu->utc_time || (max_P_frame_utc_end_time - p_vu->utc_time > HUB_AOV_MAX_TIME_DIFF))
{
max_P_frame_utc_end_time = p_vu->utc_time;
}
#ifdef DUAL_CAM
if (TP_FRAME_TYPE_I == clip_check_video_frame_type((FRAME_HEAD_S *)p_vu2->addr))
{
if (max_I_frame_utc_end_time2 < p_vu2->utc_time || (max_I_frame_utc_end_time2 - p_vu2->utc_time > HUB_AOV_MAX_TIME_DIFF))
{
max_I_frame_utc_end_time2 = p_vu2->utc_time;
}
}
if (max_P_frame_utc_end_time2 < p_vu2->utc_time || (max_P_frame_utc_end_time2 - p_vu2->utc_time > HUB_AOV_MAX_TIME_DIFF))
{
max_P_frame_utc_end_time2 = p_vu2->utc_time;
}
#endif /* DUAL_CAM */
#endif
if (obj->clip_endtime && clip_expect_endtime != obj->clip_endtime)
{
clip_expect_end_pts = clip_actual_start_pts + (obj->clip_endtime - clip_actual_startime)* 1000000LL;
clip_expect_endtime = obj->clip_endtime;
}
#ifdef AOV_SUPPORT
else if (!obj->clip_endtime && clip_expect_endtime != obj->aov_record_info.end_time)
{
clip_expect_end_pts = clip_actual_start_pts + (obj->aov_record_info.end_time - clip_actual_startime)* 1000000LL;
clip_expect_endtime = obj->aov_record_info.end_time;
}
#endif
#ifdef TAPOCARE_DYNAMIC_UPLOAD_TIME_EXTENSION
if (TRUE == g_prestop[obj->idx] && ((clip_actual_end_pts - clip_actual_start_pts)/1000000.0 - video_miss_duration) >= obj->record_buffer)
{
STREAM_INFO("clip stop, start pts:%lld, end pts:%lld, current vu pts:%lld.\n",
clip_actual_start_pts, clip_expect_end_pts, p_vu->pts);
#ifdef TELEMETRY_SUPPORT
video_el->video_stop_msg = END_BY_SHUT_DOWN;
#endif
clip_stop_upload(obj->idx);
g_prestop[obj->idx] = FALSE;
break;
}
#elif defined(LAST_FRAME_COMPLETE)
if (TRUE == g_prestop[obj->idx])
{
clip_stop_upload(obj->idx);
g_prestop[obj->idx] = FALSE;
#ifdef TELEMETRY_SUPPORT
video_el->video_stop_msg = END_BY_SHUT_DOWN;
#endif
break;
}
#endif
obj->upload_stat = UPLOAD_STAT_TAKE;
}
}
else if (ret == DATA_SEND_BUF_FULL || ret == DATA_SEND_WAIT)
{
usleep(10000); // sleep 10ms
if (ret == DATA_SEND_BUF_FULL)
{
cur_time = get_sys_uptime();
if (!send_block_time)
{
send_block_time = cur_time;
}
else if (cur_time - send_block_time > UPLOAD_BLOCK_TIMEOUT_S)
{
ret = DATA_SEND_TIMEOUT;
#ifdef TELEMETRY_SUPPORT
video_el->video_stop_msg = END_BLOCK_TIMEOUT;
#endif
STREAM_ERROR_LOG("clips stop upload err: %d.", ret);
clip_stop_upload(obj->idx);
}
}
else
{
send_block_time = 0;
}
}
else // 上传链路异常,需要停止上传。
{
#ifdef TELEMETRY_SUPPORT
video_el->video_stop_msg = END_CONN_CLOSED;
#endif
STREAM_ERROR_LOG("clips stop upload err: %d.", ret);
clip_stop_upload(obj->idx);
}
#ifdef HUB_MANAGE_SUPPORT
if (ssl_ctx && STORAGE_TYPE_HUB == obj->storage_type && CLIP_THD_STAT_STOPPED != obj->thd_stat)
{
hub_storage_recv_notify(ssl_ctx, obj, &reply_buf, REPLY_HUB_BUFFER_SIZE);
if (obj->enhance_switch && obj->enhance_para_sync)
{
STREAM_WARN("noti signal build");
sync_enhance_param(obj, reply_buf.send_buf, &reply_buf.data_len, REPLY_HUB_BUFFER_SIZE);
}
if (UPLOAD_STAT_TAKE == obj->upload_stat && 0 < reply_buf.data_len)
{
memset(&data_unit, 0, sizeof(DATA_UNIT_S));
memset(obj->aes_pack->aes_pack_buf, 0, HUB_AES_CLIPS_BUF_LEN + 2);
memcpy(obj->aes_pack->aes_pack_buf, reply_buf.send_buf, reply_buf.data_len);
data_unit.len = reply_buf.data_len;
data_unit.offset = 0;
memset(&reply_buf, 0, sizeof(REPLY_BUF_S));
is_replying_hub = 1;
obj->upload_stat = UPLOAD_STAT_SEND;
}
}
#endif
break;
default:
break;
}
}
/*等待tapo_care上传线程退出后,再释放ts package */
clip_ts_pack_deinit(&obj->ts_pack);
#ifdef HUB_MANAGE_SUPPORT
if (obj->aes_pack)
{
free(obj->aes_pack);
obj->aes_pack = NULL;
}
#ifdef AUDIO_OPUS_SUPPORT
if (g_opus_decoder)
{
free(g_opus_decoder);
g_opus_decoder = NULL;
}
#endif
#endif
STREAM_DEBUG("clips upload exit.");
return (void *)OK;
}
详细解释代码,无需dual_cam的解释