| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- #include "playback.h"
- #include <pthread.h>
- #include <dirent.h>
- #include <sys/stat.h>
- /* ============================================================
- * 内部数据结构
- * ============================================================ */
- #define MAX_ACTIVE_PLAYBACKS 32
- static PlaybackSession* g_active_playbacks[MAX_ACTIVE_PLAYBACKS];
- static int g_playback_count = 0;
- static int g_playback_id_counter = 0;
- static pthread_mutex_t g_playback_mutex = PTHREAD_MUTEX_INITIALIZER;
- /* ============================================================
- * 内部函数
- * ============================================================ */
- static PlaybackSession* find_playback(int playback_id) {
- for (int i = 0; i < g_playback_count; i++) {
- if (g_active_playbacks[i] != NULL && g_active_playbacks[i]->playback_id == playback_id) {
- return g_active_playbacks[i];
- }
- }
- return NULL;
- }
- static int add_playback(PlaybackSession* session) {
- pthread_mutex_lock(&g_playback_mutex);
-
- if (g_playback_count >= MAX_ACTIVE_PLAYBACKS) {
- pthread_mutex_unlock(&g_playback_mutex);
- LOG_ERROR("Maximum playback sessions reached");
- return FAILURE;
- }
-
- session->playback_id = ++g_playback_id_counter;
- session->state = PLAYBACK_STATE_IDLE;
- session->is_running = 0;
- session->frame_count = 0;
- pthread_mutex_init(&session->lock, NULL);
-
- g_active_playbacks[g_playback_count++] = session;
-
- pthread_mutex_unlock(&g_playback_mutex);
- LOG_INFO("Playback session added: ID=%d, Device=%s, Channel=%d",
- session->playback_id, session->device_id, session->channel);
- return SUCCESS;
- }
- static void remove_playback(int playback_id) {
- pthread_mutex_lock(&g_playback_mutex);
-
- for (int i = 0; i < g_playback_count; i++) {
- if (g_active_playbacks[i] != NULL && g_active_playbacks[i]->playback_id == playback_id) {
- pthread_mutex_destroy(&g_active_playbacks[i]->lock);
- g_active_playbacks[i] = NULL;
- g_playback_count--;
-
- /* 压缩数组 */
- int write_idx = 0;
- for (int read_idx = 0; read_idx < g_playback_count; read_idx++) {
- if (g_active_playbacks[read_idx] != NULL) {
- g_active_playbacks[write_idx++] = g_active_playbacks[read_idx];
- }
- }
- for (int j = write_idx; j < g_playback_count; j++) {
- g_active_playbacks[j] = NULL;
- }
-
- LOG_INFO("Playback session removed: ID=%d", playback_id);
- break;
- }
- }
-
- pthread_mutex_unlock(&g_playback_mutex);
- }
- /* ============================================================
- * 公共接口实现
- * ============================================================ */
- int playback_init(void) {
- LOG_INFO("Initializing playback module...");
-
- g_playback_count = 0;
- memset(g_active_playbacks, 0, sizeof(g_active_playbacks));
-
- /* 确保录像目录存在 */
- /* 目录由配置决定 */
-
- LOG_INFO("Playback module initialized");
- return SUCCESS;
- }
- void playback_cleanup(void) {
- LOG_INFO("Cleaning up playback module...");
-
- playback_stop_all();
-
- for (int i = 0; i < MAX_ACTIVE_PLAYBACKS; i++) {
- if (g_active_playbacks[i] != NULL) {
- pthread_mutex_destroy(&g_active_playbacks[i]->lock);
- if (g_active_playbacks[i]->stream) {
- stream_destroy_session(g_active_playbacks[i]->stream);
- }
- free(g_active_playbacks[i]);
- g_active_playbacks[i] = NULL;
- }
- }
-
- g_playback_count = 0;
-
- LOG_INFO("Playback module cleaned up");
- }
- PlaybackSession* playback_start(const char* device_id, int channel,
- time_t start_time, time_t end_time,
- int speed,
- PlaybackDataCB data_cb,
- PlaybackStatusCB status_cb,
- void* user_data) {
- if (device_id == NULL || channel < 0) {
- LOG_ERROR("Invalid parameters");
- return NULL;
- }
-
- LOG_INFO("Starting playback: Device=%s, Channel=%d, Start=%ld, End=%ld, Speed=%d",
- device_id, channel, (long)start_time, (long)end_time, speed);
-
- PlaybackSession* session = (PlaybackSession*)calloc(1, sizeof(PlaybackSession));
- if (session == NULL) {
- LOG_ERROR("Failed to allocate playback session");
- return NULL;
- }
-
- safe_strcpy(session->device_id, device_id, MAX_DEVICE_ID);
- session->channel = channel;
- session->start_time = start_time;
- session->end_time = end_time;
- session->speed = speed;
- session->state = PLAYBACK_STATE_STARTING;
-
- /* 创建关联的流会话用于接收回放数据 */
- session->stream = stream_create_session(device_id, channel, STREAM_TYPE_SUB,
- CODEC_TYPE_H264, 1920, 1080, 25, 2048000);
-
- if (session->stream == NULL) {
- LOG_ERROR("Failed to create stream session for playback");
- free(session);
- return NULL;
- }
-
- /* 设置回放数据回调 */
- if (data_cb != NULL) {
- /* 在实际实现中,这里会注册到流媒体库的回调 */
- session->stream->internal_handle = (void*)data_cb;
- }
-
- if (add_playback(session) != SUCCESS) {
- stream_destroy_session(session->stream);
- free(session);
- return NULL;
- }
-
- /* 调用HCNetSDK启动回放
- * 完整流程:
- * 1. NET_ESTREAM_StartListenPlayBack - 开启SMS回放监听
- * 2. NET_ESTREAM_SetExceptionCallBack - 注册异常回调
- * 3. NET_ECMS_StartPlayBack - 向设备发送回放开始请求
- * 4. NET_ESTREAM_SetPlayBackDataCB - 注册回放数据回调
- * 5. NET_ECMS_StartPushPlayBack - 发送码流传输请求(可选)
- */
-
- /* 步骤1: 开启SMS回放监听 */
- stream_start_playback_listen("0.0.0.0", 7660, NULL, NULL);
-
- /* 步骤2: 向设备发送回放开始请求 */
- /* NET_ECMS_StartPlayBack(user_id, dev_ip, dev_port, sms_ip, sms_port, channel) */
-
- session->state = PLAYBACK_STATE_PLAYING;
- session->is_running = 1;
-
- /* 触发状态回调 */
- if (status_cb) {
- status_cb(session, PLAYBACK_STATE_PLAYING, user_data);
- }
-
- LOG_INFO("Playback started: ID=%d, Device=%s, Channel=%d",
- session->playback_id, device_id, channel);
-
- return session;
- }
- int playback_stop(PlaybackSession* session) {
- if (session == NULL) return FAILURE;
-
- LOG_INFO("Stopping playback: ID=%d, Device=%s, Channel=%d",
- session->playback_id, session->device_id, session->channel);
-
- session->is_running = 0;
- session->state = PLAYBACK_STATE_STOPPING;
-
- /* 步骤7: 停止SMS码流转发 */
- /* NET_ESTREAM_StopPlayBack() */
-
- /* 步骤8: 向设备发送回放停止请求 */
- /* NET_ECMS_StopPlayBack(user_id) */
-
- /* 停止关联的流 */
- if (session->stream) {
- stream_stop_push(session->stream);
- stream_destroy_session(session->stream);
- session->stream = NULL;
- }
-
- /* 步骤9: 停止SMS回放监听(如果没有其他回放在使用) */
- /* NET_ESTREAM_StopListenPlayBack() */
-
- remove_playback(session->playback_id);
- free(session);
-
- LOG_INFO("Playback stopped: ID=%d", session->playback_id);
- return SUCCESS;
- }
- int playback_pause(PlaybackSession* session) {
- if (session == NULL) return FAILURE;
-
- if (session->state != PLAYBACK_STATE_PLAYING) {
- return FAILURE;
- }
-
- LOG_INFO("Pausing playback: ID=%d", session->playback_id);
- session->state = PLAYBACK_STATE_PAUSED;
-
- /* 在实际实现中,这里会暂停码流转发 */
-
- return SUCCESS;
- }
- int playback_resume(PlaybackSession* session) {
- if (session == NULL) return FAILURE;
-
- if (session->state != PLAYBACK_STATE_PAUSED) {
- return FAILURE;
- }
-
- LOG_INFO("Resuming playback: ID=%d", session->playback_id);
- session->state = PLAYBACK_STATE_PLAYING;
-
- /* 在实际实现中,这里会恢复码流转发 */
-
- return SUCCESS;
- }
- int playback_get_file_list(const char* device_id, int channel,
- time_t start_time, time_t end_time,
- char files[][MAX_PATH_SIZE], int max_files) {
- if (device_id == NULL || files == NULL) {
- return FAILURE;
- }
-
- LOG_INFO("Getting file list: Device=%s, Channel=%d, Start=%ld, End=%ld",
- device_id, channel, (long)start_time, (long)end_time);
-
- /* 在实际实现中,这里会调用HCNetSDK查询录像文件
- * 例如: NET_DVR_FindFirstFile / NET_DVR_FindNextFile / NET_DVR_FindClose
- */
-
- /* 模拟实现: 返回空列表 */
- (void)start_time;
- (void)end_time;
- (void)max_files;
-
- return 0;
- }
- int playback_download_file(const char* device_id, int channel,
- const char* file_name, const char* save_path) {
- if (device_id == NULL || file_name == NULL || save_path == NULL) {
- return FAILURE;
- }
-
- LOG_INFO("Downloading file: Device=%s, Channel=%d, File=%s, Path=%s",
- device_id, channel, file_name, save_path);
-
- /* 在实际实现中,这里会调用HCNetSDK下载录像文件
- * 例如: NET_DVR_PlayBackByTime / NET_DVR_SaveRealData
- */
-
- return SUCCESS;
- }
- int playback_get_active_sessions(PlaybackSession* sessions[], int max_sessions) {
- int count = 0;
- pthread_mutex_lock(&g_playback_mutex);
-
- for (int i = 0; i < g_playback_count && count < max_sessions; i++) {
- if (g_active_playbacks[i] != NULL) {
- sessions[count++] = g_active_playbacks[i];
- }
- }
-
- pthread_mutex_unlock(&g_playback_mutex);
- return count;
- }
- void playback_stop_all(void) {
- pthread_mutex_lock(&g_playback_mutex);
-
- for (int i = 0; i < g_playback_count; i++) {
- if (g_active_playbacks[i] != NULL) {
- g_active_playbacks[i]->is_running = 0;
- if (g_active_playbacks[i]->stream) {
- stream_stop_push(g_active_playbacks[i]->stream);
- }
- }
- }
-
- pthread_mutex_unlock(&g_playback_mutex);
- }
- PlaybackState playback_get_state(PlaybackSession* session) {
- if (session == NULL) return PLAYBACK_STATE_ERROR;
- return session->state;
- }
|