【SSL_P1231】容易的网络游戏(normal)

容易的网络游戏(normal)


Description

现在网络游戏一款接一款地推出,佳佳和他的同学们也迷上了网络游戏。他们最近在玩N款不同的网络游戏。
  一些网络游戏允许玩家购买双倍经验卡。拥有双倍经验卡的玩家可以在有效期内获得更多的经验值。佳佳和他的同学们有着丰富的网游经验,对于任何一款网络游戏,只要是在双倍经验的条件下,无论谁玩都可以在单位时间内轻松获得一个单位的经验值。
  国庆节马上到了,网游公司不会错过这难得的机会大捞一把。中国网游常用的赚钱手段便是免费提供双倍经验(因为如果玩家再买一张双倍卡,便可获得4倍经验)。
  在9、10、11月份,佳佳和他的同学们玩的N个网络游戏中每一个都会有一段开放免费双倍经验的时间。佳佳事先作了调查,他已经把每一款网游的双倍经验开放时间都记了下来。佳佳是不会乱用自己的零花钱购买双倍经验卡的,他决定在免费双倍经验时叫同学到家里一起玩;同时,他们也不会浪费自己的时间,为了提高效率,他们只玩处于免费双倍经验开放时期的游戏。
  我们假定,每台电脑最多只能有一人操作,一个人最多只能操作一台电脑;并且每款游戏最多只能在一台电脑上玩,每台电脑最多运行一个游戏。我们忽略开始游戏和结束游戏时所消耗的时间。
  现在佳佳想知道,假如佳佳共有M台电脑,且佳佳一共叫来了P个同学,那么他和他的同学们最多能得到多少单位的经验呢?

Input

第一行有三个用空格隔开的整数N,M和P,它们表示的意义如题目描述。
  以下N行,每行有两个用空格隔开的整数Xi,Yi(Xi<=Yi),表示从Xi单位时间到Yi单位时间为第i款游戏开放双倍经验的时间。
  对于70%数据,0<=Xi,Yi<=10000;
  对于100%数据,0<=Xi,Yi<=5000000,0<=P<=2147483647,1<=N<=1000,1<=M<=1000。

Output

一个整数,表示佳佳和他的同学们能获得的最大经验值。

Sample Input

1 1 1
0 100

Sample Output

101

解题思路

用离散化处理,但是人数、时间都要+1

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long n,m,p;
struct abc{
	int x,y;
}a[2010];
bool hh(abc q,abc p)
{
	return q.x<p.x;
}
int main()
{
	cin>>n>>m>>p;
	p++;
	m=min(m,p);
	for(int i=1;i<=n;i++)
	{
		int aa,b;
		scanf("%d%d",&aa,&b);
		a[i*2].x=aa;
		a[i*2-1].x=b+1;
		a[i*2].y=1;
		a[i*2-1].y=0; 
	}
	sort(a+1,a+2*n+1,hh);
	long long t=0,ans=0;
	for(int i=1;i<=2*n;i++)
	{
		long long h=min(t,m);
		ans+=h*(a[i].x-a[i-1].x);
		if(a[i].y) t++;
		if(!a[i].y) t--;
	}
	cout<<ans<<endl;
}
zqz@zqz-Legion-Y9000P-IRX9:~/ros2_ws$ # 查找系统证书路径(根据引用[1]) ls /usr/lib/ssl/certs/ca-certificates.crt # 设置环境变量指向系统证书 export SSL_CERT_FILE=/usr/lib/ssl/certs/ca-certificates.crt # 永久生效(添加到 ~/.bashrc) echo "export SSL_CERT_FILE=/usr/lib/ssl/certs/ca-certificates.crt" >> ~/.bashrc source ~/.bashrc /usr/lib/ssl/certs/ca-certificates.crt bash: /home/zqz/ros2_ws/install/setup.bash: 没有那个文件或目录 zqz@zqz-Legion-Y9000P-IRX9:~/ros2_ws$ # 1. 安装/更新 certifi 包 pip install --upgrade certifi # 2. 获取证书路径并设置临时环境变量 export SSL_CERT_FILE=$(python -c "import certifi; print(certifi.where())") # 3. 永久生效配置(添加到 ~/.bashrc) echo "export SSL_CERT_FILE=\$(python -c 'import certifi; print(certifi.where())')" >> ~/.bashrc source ~/.bashrc Defaulting to user installation because normal site-packages is not writeable Requirement already satisfied: certifi in /usr/lib/python3/dist-packages (2020.6.20) WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ReadTimeoutError("HTTPSConnectionPool(host='pypi.org', port=443): Read timed out. (read timeout=15)")': /simple/certifi/ Collecting certifi Downloading certifi-2025.6.15-py3-none-any.whl (157 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 157.7/157.7 KB 692.0 kB/s eta 0:00:00 Installing collected packages: certifi Successfully installed certifi-2025.6.15 找不到命令 “python”,您的意思是: “python3” 命令来自 Debian 软件包 python3 “python” 命令来自 Debian 软件包 python-is-python3 bash: /home/zqz/ros2_ws/install/setup.bash: 没有那个文件或目录 找不到命令 “python”,您的意思是: “python3” 命令来自 Debian 软件包 python3 “python” 命令来自 Debian 软件包 python-is-python3 zqz@zqz-Legion-Y9000P-IRX9:~/ros2_ws$ echo $ SSL_CERT_FILE $ SSL_CERT_FILE zqz@zqz-Legion-Y9000P-IRX9:~/ros2_ws$
07-04
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, &param_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的解释
12-25
豌豆代理(又称豌豆 IP)是一款一站式国内代理 IP 服务平台,主打高匿名、低延迟、高可用的 IP 资源,支持 HTTP/HTTPS/SOCKS5 协议,适配 Windows、Mac、Android、iOS 多平台。 多类型 IP 资源与高覆盖节点 提供动态住宅 IP、静态独享 IP、数据中心 IP,覆盖全国 200 + 城市,可用率 99%+;支持省市精准选择或全国混拨,适配不同业务合规与稳定性需求。 使用:在客户端 “节点 / 线路” 页,按城市 / 类型筛选,一键连接目标 IP,适合爬虫、电商多账号运营等场景。 秒级 IP 切换与灵活调度 支持手动一键切换、秒级动态切换(切换速度低至 100ms)、定时切换(自定义时长),并自动过滤重复 IP,避免重复使用导致风险。 使用:在 “设置” 中开启 “自动切换” 并设时间间隔,或按 Ctrl+Q 快捷键一键换 IP,适配反爬虫、批量测试等高频切换场景。 全协议支持与多端适配 兼容 HTTP/HTTPS/SOCKS5 主流代理协议,可对接浏览器、爬虫脚本、客户端软件;支持 Windows、Mac、安卓、iOS 多端同步使用,跨设备无缝切换。 使用:在客户端 “协议设置” 选择对应协议,生成代理地址与端口,直接填入目标软件即可生效。 隐私安全与数据加密 自研传输加密技术保护数据传输,搭配高匿名 IP 隐藏真实地址,同时支持自动清除 Cookie / 缓存,降低隐私泄露与追踪风险。 使用:在 “安全设置” 中开启 “数据加密” 与 “自动清理缓存”,公共 WiFi 环境下优先启用,提升隐私防护等级。 智能筛选与稳定网络优化 系统自动筛选低延迟、高可用 IP,过滤失效 / 重复地址;依托自建纯净机房与独享带宽,搭配 BGP 多线接入,保障连接稳定性与速度。 使用:无需手动配置,客户端默认智能匹配合适节点,复杂网络环境可在 “网络
在网络高速发展的时代,众多的软件被开发出来,给用户带来了很大的选择余地,而且人们越来越追求更个性的需求。在这种时代背景下,商家只能以用户为导向,以商品的持续创新作为商家最重要的事项。 在新发展的时代,人们对幼儿资源互助共享平台越来越重视,才能实现幼儿资源互助共享平台的有效发挥,本文将通过幼儿资源互助共享平台的信息,分析在日常生活中对幼儿资源互助共享平台存在哪些问题探讨出进一步提升效率,管理能力的对策。 系统采用了Java技术,将所有模块采用以浏览器交互的模式,选择MySQL作为系统的数据库,来进行系统的设计。基本实现了幼儿资源互助共享平台应有的主要功能模块,本系统有管理员:首页、个人中心、用户管理、卖家管理、咨询师管理、萌宝信息管理、幼儿知识管理、保姆推荐管理、音频资源管理、二手商品管理、商品分类管理、资源分类管理、交流论坛、系统管理,用户;首页、个人中心、萌宝信息管理、保姆推荐管理、音频资源管理,卖家;首页、个人中心、二手商品管理、订单管理,咨询师;首页、个人中心、幼儿知识管理,前台首页;首页、萌宝信息、幼儿知识、保姆推荐、音频资源、二手商品、交流论坛、个人中心、后台管理、购物车等功能。 对系统进行测试后,改善了程序逻辑和代码。同时确保系统中所有的程序都能正常运行,所有的功能都能操作,本系统的开发获取幼儿资源互助共享平台信息能够更加方便快捷,同时也使幼儿资源互助共享平台信息变的更加系统化、有序化。系统界面较友好,易于操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值