在tp框架下,使用iframe框架时,退出跳转的写法

本文介绍了使用iframe框架实现的页面布局方式,并展示了如何通过PHP设置不同区域的页面链接。此外,还提供了一种实现用户退出功能的具体方法,确保整个框架能够正确刷新。

1.这是iframe 框架

<\frameset rows="50px,*,50px" frameborder="1" border="0" noresize="noresize"> 

 <\frame name="top" src="{:U('User/top')}"/> 

 <\frameset cols="250px,*"> 

        <\frame name="left" src="{:U('User/left')}"/> 

        <\frame name="right" src="{:U('User/right')}" /> 

 <\/frameset> 

 <\frame name="bottom" src="{:U('User/bottom')}"/>

<\/frameset>

2. 下面的是left.html 里面的一个li

<\a href="{:U('BankAccount/index')}" target="right"><\li><\span class="glyphicon glyphicon-credit-card">  银行账户

3. 这是退出的方法

public function do_out(){

            unset($_SESSION['uid']);
            $this->assign('jumpUrl',"javascript:window.open('/','_parent');window.close();");
            $this->assign('waitSecond','1');
            $this->error="登录超时,请重新登录";
            $this->display(C('TMPL_ACTION_SUCCESS'));

            //$this->redirect('Index/index');  这样写只会在中间部分显示,不会整个框架退出显示
    }
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; } } } 分析一下,特别是锁的地方
最新发布
10-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值