while(g_record && g_record->thread_state != THREAD_STATE_FINISHED)
{
#ifdef VIDEO_AVBR_ENABLE
need_set_i_frame = FALSE;
#endif
if (THREAD_STATE_STOPPED == g_record->thread_state)
{
/* 线程停止,解除avdm关联并等待唤醒信号 */
if (TRUE == video_cursor.attaching)
{
avdm_detach_block(g_record->type, &video_cursor);
}
if (TRUE == audio_cursor.attaching)
{
avdm_detach_block(AVDM_TYPE_AUDIO, &audio_cursor);
}
#ifdef DUAL_CAM
if (TRUE == video2_cursor.attaching)
{
avdm_detach_block(g_record->video2_type, &video2_cursor);
}
#endif
/* 线程停止,结束侦测事件clip */
if (StgDisk_IsV1())
{
SnapshotIndexMod_ClipEnd();
SnapshotIndexMod_PostMsg(eDetMsgType_Idle, 0);
ops = get_partition_ops(STORAGE_TYPE_VIDEO);
if (ops->update_event_entry_end_status)
{
STM_INFO("update event entry end status");
record_stm_fd = get_record_curr_stm_fd();
ops->update_event_entry_end_status(get_stm_file(record_stm_fd));
}
stm_update_index();
}
else
{
StgMod_Sync();
}
record_stop_events();
if (get_cur_record_type() == REC_TYPE_MOTION)
{
g_record->rec_status = REC_STAT_PRE;
}
/* 线程停止再开始时为定时录像状态时,让其拿最新的GOP,避免出现音频或视频GOP被覆盖导致音视频不同步现象 */
if (FALSE == first_video_gop)
first_video_gop = TRUE;
if (FALSE == first_audio_gop)
first_audio_gop = TRUE;
is_got_future_audio = 0;
#ifdef DUAL_CAM
is_got_future_video2 = 0;
#endif
pthread_mutex_lock(&g_record->thread_mutex);
if (g_record->thread_state == THREAD_STATE_STOPPED)
{
pthread_cond_wait(&g_record->thread_cond, &g_record->thread_mutex);
}
pthread_mutex_unlock(&g_record->thread_mutex);
continue;
}
if (REC_STAT_PRE == g_record->rec_status)
{
/* 从录像状态切换回预录状态,更新索引回放才不会缺失录像 */
if (StgDisk_IsV1())
{
SnapshotIndexMod_ClipEnd();
ops = get_partition_ops(STORAGE_TYPE_VIDEO);
if (ops->update_event_entry_end_status)
{
STM_INFO("update event entry end status");
record_stm_fd = get_record_curr_stm_fd();
ops->update_event_entry_end_status(get_stm_file(record_stm_fd));
}
/* 在录像状态切换到预录状态时,将各事件的写入状态初始化。 */
for (event_id = TP_EVENT_TYPE_MIN; event_id <= TP_EVENT_TYPE_MAX; ++event_id)
{
ptr_event_params[event_id].write_status = NOT_START;
}
stm_update_index();
}
else
{
StgMod_Sync();
}
g_record->need_update_index = FALSE;
/* 如果从录像状态切换回预录状态,解除与avdm的关联 */
if (TRUE == video_cursor.attaching)
{
avdm_detach_block(g_record->type, &video_cursor);
}
if (TRUE == audio_cursor.attaching)
{
avdm_detach_block(AVDM_TYPE_AUDIO, &audio_cursor);
}
#ifdef DUAL_CAM
if (TRUE == video2_cursor.attaching)
{
avdm_detach_block(g_record->video2_type, &video2_cursor);
}
#endif
if (FALSE == first_video_gop)
first_video_gop = TRUE;
if (FALSE == first_audio_gop)
first_audio_gop = TRUE;
is_got_future_audio = 0;
#ifdef DUAL_CAM
is_got_future_video2 = 0;
#endif
/* TODO: 如果当前GOP还没有保存是否需要强制产生I帧;
* 当前还没写到最新的GOP,需要通过时间戳进行判断;*/
/* 预录GOP由AVDM保留,等待唤醒 */
pthread_mutex_lock(&g_record->status_mutex);
if ((g_record->thread_state == THREAD_STATE_RUNNING) && (REC_STAT_PRE == g_record->rec_status))
{
pthread_cond_wait(&g_record->status_cond, &g_record->status_mutex);
}
pthread_mutex_unlock(&g_record->status_mutex);
continue;
}
if (REC_STAT_RECORD == g_record->rec_status || REC_STAT_DELAY == g_record->rec_status)
{
if ((status_of_event(0, TP_EVENT_TYPE_MOTION_DETECT) == EVENT_STATUS_START
|| status_of_event(0, TP_EVENT_TYPE_MOTION_DETECT) == EVENT_STATUS_PROCESS
|| status_of_event(0, TP_EVENT_TYPE_FACE_GALLERY) == EVENT_STATUS_START
|| status_of_event(0, TP_EVENT_TYPE_FACE_GALLERY) == EVENT_STATUS_PROCESS)
&& (first_video_gop == TRUE))
{
/* 移动侦测录像,取预录GOP */
ret = avdm_get_pre_block(g_record->type, &video_cursor, &video_block);
#ifdef DUAL_CAM
ret_v2 = avdm_get_pre_block(g_record->video2_type, &video2_cursor, &video2_block); //DC_TODO 时间同步机制
#endif
}
else
{
/* 定时录像第一次取GOP,将cursor置空防止取到上一次停止时录像的下一个GOP */
if (first_video_gop == TRUE)
{
memset(&video_cursor, 0, sizeof(avdm_cursor_t));
#ifdef DUAL_CAM
memset(&video2_cursor, 0, sizeof(avdm_cursor_t));
#endif
}
/* 定时录像,取最新GOP,或移动侦测/定时录像的下一个GOP */
ret = avdm_get_next_block(g_record->type, &video_cursor, &video_block);
#ifdef DUAL_CAM
if (ret == ERR_NONE || ret == -ERR_OW_GET_LATEST)
{
/* 上次video2已经取得比video晚则先不取video2,否则取video2 */
if (!is_got_future_video2)
{
get_video2_block:
ret_v2 = avdm_get_next_block(g_record->video2_type, &video2_cursor, &video2_block);
}
/* 若取video2有误或video2比video早,重新取video2 */
if ((-ERR_FRAME_NOT_READY == ret_v2) || (-ERR_AVDM_NOT_READY == ret_v2) ||
video_block.start_time - video2_block.start_time >= 2)
{
pthread_mutex_lock(&g_record->video2_gop_mutex);
if (g_record->thread_state == THREAD_STATE_RUNNING)
{
pthread_cond_wait(&g_record->video2_gop_cond, &g_record->video2_gop_mutex);
}
pthread_mutex_unlock(&g_record->video2_gop_mutex);
if (g_record->thread_state == THREAD_STATE_RUNNING)
{
goto get_video2_block;
}
else
{
continue;
}
}
/* 若video2比video晚,本次不携带,下次不取 */
else if (video2_block.start_time - video_block.start_time >= 2)
{
is_carry_video2 = 0;
is_got_future_video2 = 1;
}
/* video2与video时间同步,本次携带,下次需要取 */
else
{
is_carry_video2 = 1;
is_got_future_video2 = 0;
}
}
#endif
}
/*
* 不管是否是取预录GOP,将其置为FALSE,从定时录像切换到正在移
* 动时的移动侦测录像,不需要拿预录GOP,仍然只需要直接拿下一个GOP
*/
first_video_gop = FALSE;
if ((-ERR_FRAME_NOT_READY == ret) || (-ERR_AVDM_NOT_READY == ret))
{
pthread_mutex_lock(&g_record->video_gop_mutex);
if (g_record->thread_state == THREAD_STATE_RUNNING)
{
pthread_cond_wait(&g_record->video_gop_cond, &g_record->video_gop_mutex);
}
pthread_mutex_unlock(&g_record->video_gop_mutex);
continue;
}
else
{
/* 取到的GOP中包含会显示镜头遮蔽画面的视频帧,丢弃该GOP */
if (video_block.start_time <= g_record->lens_mask_stopped_time + 1)
{
STM_INFO("drop gop: start_time: %ld, lens mask_time: %ld", video_block.start_time, g_record->lens_mask_stopped_time);
is_got_future_audio = 0;
#ifdef DUAL_CAM
is_got_future_video2 = 0;
#endif
continue;
}
#ifdef DUAL_CAM
if (video2_block.start_time <= g_record->lens_mask_stopped_time + 1)
{
STM_INFO("drop video2 gop: start_time: %ld, lens mask_time: %ld", video2_block.start_time, g_record->lens_mask_stopped_time);
is_got_future_audio = 0;
is_got_future_video2 = 0;
continue;
}
#endif
/*
* 如果视频get_next取到的是最新的GOP,音频也需要取最新的GOP,
* detach并memset cursor保证取到音频最新的GOP
*
* -ERR_OW_GET_LATEST在去预录GOP接口不会返回,这里不会进入取
* 音频预录GOP的分支
*/
if (-ERR_OW_GET_LATEST == ret)
{
STM_INFO("video gop overlap, current start_time(%d)", video_block.start_time);
avdm_detach_block(AVDM_TYPE_AUDIO, &audio_cursor);
memset(&audio_cursor, 0, sizeof(avdm_cursor_t));
is_got_future_audio = 0;
}
#ifdef DUAL_CAM
/* DC_TODO video2取到最新帧,以video1为准,暂不做处理*/
if (ret_v2 == -ERR_OW_GET_LATEST)
{
STM_INFO("Ignore video2 gop overlap, current start_time(%d)", video2_block.start_time);
ret_v2 = ERR_NONE;
}
#endif
get_audio_block:
if (!is_got_future_audio)
{
if ((status_of_event(0, TP_EVENT_TYPE_MOTION_DETECT) == EVENT_STATUS_START
|| status_of_event(0, TP_EVENT_TYPE_MOTION_DETECT) == EVENT_STATUS_PROCESS
|| status_of_event(0, TP_EVENT_TYPE_FACE_GALLERY) == EVENT_STATUS_START
|| status_of_event(0, TP_EVENT_TYPE_FACE_GALLERY) == EVENT_STATUS_PROCESS)
&& (first_audio_gop == TRUE))
{
/* 移动侦测录像,取预录GOP */
ret = avdm_get_pre_block(AVDM_TYPE_AUDIO, &audio_cursor, &audio_block);
}
else
{
/* 定时录像第一次取GOP,将cursor置空防止取到上一次停止时录像的下一个GOP */
if (first_audio_gop == TRUE)
{
memset(&audio_cursor, 0, sizeof(avdm_cursor_t));
}
ret = avdm_get_next_block(AVDM_TYPE_AUDIO, &audio_cursor, &audio_block);
}
}
first_audio_gop = FALSE;
if ((-ERR_FRAME_NOT_READY == ret) || (-ERR_AVDM_NOT_READY == ret))
{
pthread_mutex_lock(&g_record->audio_gop_mutex);
if (g_record->thread_state == THREAD_STATE_RUNNING)
{
pthread_cond_wait(&g_record->audio_gop_cond, &g_record->audio_gop_mutex);
}
pthread_mutex_unlock(&g_record->audio_gop_mutex);
if (g_record->thread_state == THREAD_STATE_RUNNING)
{
goto get_audio_block;
}
else
{
continue;
}
}
if (-ERR_OW_GET_LATEST == ret)
{
STM_INFO("audio gop overlap, current start_time(%d)", video_block.start_time);
}
}
}
int is_carry_audio = 1;
if (labs(video_block.start_time - audio_block.start_time) >= 2)
{
/* : 视频时间大于音频时间,则继续取靠后的音频,直到时间同步 */
if (video_block.start_time > audio_block.start_time)
{
STM_INFO("video(%d) audio(%d) interval(%d)", video_block.start_time, audio_block.start_time, video_block.start_time - audio_block.start_time);
while (1)
{
int err = avdm_get_next_block(AVDM_TYPE_AUDIO, &audio_cursor, &audio_block);
// 1.取不到下一个音频帧,跳出
if (err != ERR_NONE && err != (-ERR_OW_GET_LATEST))
{
is_carry_audio = 0;
is_got_future_audio = 0;
break;
}
// 2.判断取到音频帧是否满足时间同步条件
if (labs(video_block.start_time - audio_block.start_time) < 2)
{
is_got_future_audio = 0;
break;
}
// 3.取到的音频帧时间超前(大于视频帧2s)
if (audio_block.start_time > video_block.start_time)
{
is_got_future_audio = 1;
break;
}
}
}
/* : 音频时间大于视频时间,暂停取音频帧,先写入视频,直到音频视频时间同步,再一起写入 */
else
{
is_carry_audio = 0;
is_got_future_audio = 1;
}
STM_INFO("carry_audio(%d) future_audio(%d) v(%d)-a(%d) %d", is_carry_audio, is_got_future_audio, video_block.start_time, audio_block.start_time, labs(video_block.start_time - audio_block.start_time));
}
/* 处理GOP */
init_storage_data(&storage_data, &video_block, 1, 0);
#ifdef DUAL_CAM
memset(&video2_storage_data, 0, sizeof(video2_storage_data));
if(!ret_v2 && is_carry_video2)
{
#ifndef DUAL_ALGO_ENABLE
init_storage_data(&video2_storage_data, &video2_block, 0, 1);
#else /* 由于支持双路算法的机型中,第二路视频流也有侦测,所以参数置1 */
init_storage_data(&video2_storage_data, &video2_block, 1, 1);
#endif
}
#endif
/* 写入带音频帧才对其初始化 */
memset(&audio_storage_data, 0, sizeof(audio_storage_data));
if (is_carry_audio)
{
init_storage_data(&audio_storage_data, &audio_block, 0, 0);
}
/* 视频GOP赋值编码类型 */
storage_data.video_codec_type = video_block.unique_info.video.encode_type;
/* 音频编码类型 */
audio_codec_type = audio_block.unique_info.audio.encode_type;
/* 定时录像,如果GOP开始时间比定时录像设定时间早,需丢弃该GOP */
if (get_cur_record_type() == REC_TYPE_TIME &&
!check_gop_valid_by_plan_time(storage_data.end_time))
{
STM_INFO("time record set time %u, gop start/end time: %u ~ %u, abandon",
get_cur_record_type_set_time(), storage_data.start_time, storage_data.end_time);
continue;
}
/* 如果拿到的视频GOP结束时间大于起始时间超过5秒或者结束时间小于起始时间,需要丢弃该GOP */
if ((storage_data.end_time - storage_data.start_time > GOP_DURATION_THRESHOLD) ||
(storage_data.end_time < storage_data.start_time))
{
STM_INFO("gop time error, start_time: %u, end_time: %u", storage_data.start_time, storage_data.end_time);
continue;
}
/* 如果上一个GOP结束时间与当前GOP的起始时间相差超过TIME_JUMP_THRESHOLD,需要新起事件处理时间跳变 */
if ((storage_data.start_time - stopped_time > g_record->time_jump_threshold) ||
(stopped_time - storage_data.start_time > 0))
{
STM_INFO("time jump, gop start_time: %u, stopped time: %u", storage_data.start_time, stopped_time);
if (stopped_time - storage_data.start_time > 0)
{
/* 时间向前跳变,强制新起文件 */
STM_DEBUG("force new file");
storage_data.force_new_file = 1;
}
if ((g_record->force_file_when_time_jumped) &&
(storage_data.start_time - stopped_time > g_record->time_jump_threshold))
{
/* 部分电信平台机型要求时间向后发生跳变时也要强制新起文件,
* 但对于磁盘刚格式完,还未写入数据的情况,不强制新起文件 */
if (g_newly_formatted)
{
STM_INFO("Newly formatted, not force new file evenif time jump.");
}
else
{
storage_data.force_new_file = 1;
STM_DEBUG("force new file");
}
}
time_jump = TRUE;
}
else
{
time_jump = FALSE;
}
/* 如果上一个GOP的编码类型与当前GOP编码类型不同(H264/H265),需要新起文件存储 */
if ((last_video_codec_type != 0xff) && (last_video_codec_type != storage_data.video_codec_type))
{
storage_data.force_new_file = 1;
STM_INFO("video codec changed from %d to %d", last_video_codec_type, storage_data.video_codec_type);
STM_DEBUG("force new file");
}
if ((last_audio_codec_type != 0xff) && (last_audio_codec_type != audio_codec_type))
{
storage_data.force_new_file = 1;
STM_INFO("audio codec changed from %d to %d", last_audio_codec_type, audio_codec_type);
STM_DEBUG("force new file");
}
/* 支持多事件 start */
for (event_id = TP_EVENT_TYPE_MIN; event_id <= TP_EVENT_TYPE_MAX; event_id++)
{
if ((time_jump || (last_video_codec_type != storage_data.video_codec_type) ||
(last_audio_codec_type != audio_codec_type)/* || item->need_new_event*/)
&& (ptr_event_params[event_id].write_status == START_NOT_END))
{
if (storage_data.event_status[event_id] == EVENT_STATUS_END)
{
if (event_id != TP_EVENT_TYPE_GENERAL)
{
storage_data.event_status[event_id] = EVENT_STATUS_ONE_BUFF;
}
}
else if (storage_data.event_status[event_id] != EVENT_STATUS_EMPTY)
{
storage_data.event_status[event_id] = EVENT_STATUS_START;
}
ptr_event_params[event_id].write_status = NOT_START;
}
switch (storage_data.event_status[event_id])
{
case EVENT_STATUS_START:
case EVENT_STATUS_RECORD_DELAY:
if (ptr_event_params[event_id].write_status == START_NOT_END)
{
storage_data.event_status[event_id] = EVENT_STATUS_PROCESS;
}
else
{
storage_data.event_status[event_id] = EVENT_STATUS_START;
#ifdef VIDEO_AVBR_ENABLE
if (video_block.unique_info.video.iframe_type == TP_FRAME_TYPE_VIRTUAL_I)
{
need_set_i_frame = TRUE;
}
#endif
}
break;
case EVENT_STATUS_END:
if (ptr_event_params[event_id].write_status == START_NOT_END)
{
storage_data.event_status[event_id] = EVENT_STATUS_END;
}
else
{
storage_data.event_status[event_id] = EVENT_STATUS_EMPTY;
}
break;
case EVENT_STATUS_ONE_BUFF:
#ifdef VIDEO_AVBR_ENABLE
if (video_block.unique_info.video.iframe_type == TP_FRAME_TYPE_VIRTUAL_I)
{
need_set_i_frame = TRUE;
}
#endif
break;
default:
storage_data.event_status[event_id] = EVENT_STATUS_EMPTY;
break;
}
}
/* 支持多事件 end */
stopped_time = storage_data.end_time; /* Save end time of gop */
last_video_codec_type = storage_data.video_codec_type; /* Save video codec type of gop */
last_audio_codec_type = audio_codec_type;
#ifdef VIDEO_AVBR_ENABLE
/* 事件发生但是该gop没有真实i帧,需要在该gop前加入前一个真实i帧 */
if (need_set_i_frame)
{
STM_INFO("need set i frame");
if (OK != avdm_get_refer_block(g_record->type, video_cursor.desc, &refer_block))
{
STM_ERROR("get refer block fail");
continue;
}
else
{
storage_data.i_frame_buf = refer_block.addr;
}
}
#endif
if (stm_check_event_duration(&storage_data))
{
storage_data.force_new_file = 1;
}
#ifdef DUAL_CAM
if (stm_check_mp4_file_size(&storage_data, &video2_storage_data, &audio_storage_data))
#else
if (stm_check_mp4_file_size(&storage_data, &audio_storage_data))
#endif
{
storage_data.force_new_file = 1;
}
#ifdef VIDEO_AVBR_ENABLE
/* 需要切换文件时该gop没有真实i帧,需要在该gop前加入前一个真实i帧 */
if (storage_data.force_new_file && video_block.unique_info.video.iframe_type == TP_FRAME_TYPE_VIRTUAL_I
&& !storage_data.i_frame_buf)
{
STM_INFO("Force new file, need set i frame.");
if (OK != avdm_get_refer_block(g_record->type, video_cursor.desc, &refer_block))
{
STM_ERROR("get refer block fail");
continue;
}
else
{
storage_data.i_frame_buf = refer_block.addr;
}
}
#endif
#define STM_WRITE_MAX_COST_TIME_INTERVAL 600 //sd卡写入GOP速度过慢(写入时间超过定义值,单位:ms),则打印相关信息,方便分析问题
uint64_t t_start, t_end;
t_start = monotonic_ms();
if (StgDisk_IsV1())
{
/* 写入SD卡 */
memset(&gop_event, 0, sizeof(EVENT_ENTRY));
ret = stm_write(g_record->stm_fd, &storage_data, &audio_storage_data, &gop_event);
t_end = monotonic_ms();
if (t_end - t_start > STM_WRITE_MAX_COST_TIME_INTERVAL)
{
STM_INFO("stm_write time_cost(%dms) ret(%d) new(%d) v_gop(%dKB) a_gop(%dKB) ", (int)(t_end - t_start), ret, storage_data.force_new_file, storage_data.buf_len/1024, audio_storage_data.buf_len/1024);
}
/* 写入结果失败,则打印相关信息 */
if (last_ret != ret)
{
STM_ERROR("stm_write ret: %d -> %d", last_ret, ret);
last_ret = ret;
if (ret == STM_ERR_DISK_FULL || ret == STM_ERR_DISK_UNAVAIL)
{
VodStatus_UpdateFullStatus(1);
}
else
{
VodStatus_UpdateFullStatus(0);
}
}
if (ret == STM_NO_ERR)
{ //写入sd卡成功,更新clip时间
SnapshotIndexMod_ClipUpdateTime(storage_data.start_time, storage_data.end_time, storage_data.is_det_end);
if (is_event_push_sync(SnapshotIndexMod_LastSyncTime()))
{
g_record->need_update_index = TRUE;
}
}
else
{ //写入sd卡错误,则结束侦测事件clip
SnapshotIndexMod_ClipEnd();
}
if (ret == STM_NO_ERR)
{
/* 写入数据成功后,将标志位复位 */
if (g_newly_formatted)
{
STM_INFO("stm_write success, reset flag g_newly_formatted!");
g_newly_formatted = 0;
}
for (event_id = TP_EVENT_TYPE_MIN; event_id <= TP_EVENT_TYPE_MAX; event_id++)
{
switch (storage_data.event_status[event_id])
{
case EVENT_STATUS_END:
case EVENT_STATUS_EMPTY:
ptr_event_params[event_id].write_status = NOT_START;
break;
case EVENT_STATUS_PROCESS:
STM_DEBUG("event_id[%d] EVENT_STATUS_PROCESS", event_id);
break;
case EVENT_STATUS_START:
STM_DEBUG("event_id[%d] EVENT_STATUS_START", event_id);
ptr_event_params[event_id].write_status = START_NOT_END;
break;
default:
break;
}
}
}
if (TRUE == g_record->need_update_index)
{
stm_update_index();
g_record->need_update_index = FALSE;
}
}
else
{
#ifdef DUAL_CAM
ret = StgMod_Write((void *)&storage_data, (void *)&video2_storage_data, (void *)&audio_storage_data);
#else
ret = StgMod_Write((void *)&storage_data, (void *)&audio_storage_data);
#endif
/* 写入结果失败,则打印相关信息 */
if (last_ret != ret)
{
STM_ERROR("StgMod_Write ret: %d -> %d", last_ret, ret);
last_ret = ret;
if (ret == STM_ERR_DISK_FULL)
{
VodStatus_UpdateFullStatus(1);
}
else
{
VodStatus_UpdateFullStatus(0);
}
}
t_end = monotonic_ms();
if (t_end - t_start > STM_WRITE_MAX_COST_TIME_INTERVAL)
{
STM_INFO("StgMod_Write time_cost(%dms) ret(%d) new(%d) v_gop(%dKB) a_gop(%dKB) ", (int)(t_end - t_start), ret, storage_data.force_new_file, storage_data.buf_len/1024, audio_storage_data.buf_len/1024);
}
//消息推送同步事件索引
#define CLIP_MIN_DURATION 5
for (U8 lens_id = 0; lens_id < IPC_CHANNEL_NUM; lens_id++)
{
if (EventCtx_ClipDuration(lens_id) > CLIP_MIN_DURATION)
{
if (is_event_push_sync((time_t)EventCtx_LastSyncTime(lens_id)))
{
g_record->need_update_index = TRUE;
}
}
}
/* 索引同步 */
if (TRUE == g_record->need_update_index)
{
StgMod_UpdateIndex();
g_record->need_update_index = FALSE;
}
}
}
分析一下,特别是锁的地方
最新发布