793 lines
25 KiB
C
793 lines
25 KiB
C
// e_player_list.c
|
||
#include "e_player_list.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
#include "e_logger.h"
|
||
#include <sys/time.h>
|
||
#include "e_conf.h"
|
||
#include <errno.h>
|
||
|
||
|
||
#define GET_SCR_WIDTH() (LV_USE_HOR_SIZE)
|
||
#define GET_SCR_HEIGHT() (LV_USE_VER_SIZE)
|
||
#define USEC_TO_SECONDS (1000 * 1000)
|
||
|
||
#define TOLERANCE_MS 200 // 误差容忍度200ms
|
||
#define retry_max 5 // 播放失败最大重试次数
|
||
#define USEC_TO_MS 1000 // 微秒转毫秒
|
||
#define MS_TO_USEC 1000 // 毫秒转微秒
|
||
#define DECODE_REDUNDANCY_MS 1500 // 解码性能差设备的时间冗余 (1 秒)
|
||
|
||
static bool sync_play = true; // 是否开启同步播放
|
||
static int retry_count = 0;
|
||
extern int g_screen_rotation;
|
||
|
||
// ======================== 内部函数声明 ========================
|
||
static void *player_thread_func(void *arg);
|
||
static void dis_player_hole(int display_idx);
|
||
static int my_play_callback(void *user, int evt, void *info);
|
||
static int play_video(VideoPlayer *video_player, const MediaPath *file_path);
|
||
|
||
static void play_error(VideoPlayer *video_player, const int code);
|
||
static void xos_player_set_volume(void *player, int volume);
|
||
|
||
extern void fbdev_set_hole(int x, int y, int width, int height);
|
||
extern void fbdev2_set_hole(int x, int y, int width, int height);
|
||
|
||
// ======================== 播放器API实现 ========================
|
||
|
||
QUA_BOOL is_stop = QUA_FALSE;
|
||
|
||
VideoPlayer *video_player_init(int display_idx)
|
||
{
|
||
VideoPlayer *player = calloc(1, sizeof(VideoPlayer));
|
||
if (!player)
|
||
return NULL;
|
||
player->display_idx = display_idx;
|
||
snprintf(player->display, sizeof(player->display), "id:display%d", display_idx);
|
||
|
||
player->media_paths = calloc(MAX_PLAYLIST_ITEMS, sizeof(MediaPath *));
|
||
if (!player->media_paths)
|
||
{
|
||
LOGE("播放列表初始化失败:内存分配失败");
|
||
free(player);
|
||
return NULL;
|
||
}
|
||
|
||
pthread_mutex_init(&player->playlist_mutex, NULL);
|
||
|
||
pthread_mutex_init(&player->play_mutex, NULL);
|
||
pthread_cond_init(&player->play_cond, NULL);
|
||
|
||
return player;
|
||
}
|
||
// 设置是否开启同步播放
|
||
void video_player_set_sync_play(bool sync)
|
||
{
|
||
sync_play = sync;
|
||
}
|
||
|
||
/**
|
||
* 临时关闭播放器,不销毁video_player。
|
||
* 清空播放列表,并停止播放
|
||
*/
|
||
void video_player_close(VideoPlayer *video_player)
|
||
{
|
||
if (!video_player)
|
||
return;
|
||
video_player->first_play = true;
|
||
if (video_player->stop_requested)
|
||
{
|
||
// 当下载失败后。黑洞已打开,但是视频并没有开始播放,因此此处添加关闭黑洞
|
||
dis_player_hole(video_player->display_idx);
|
||
return;
|
||
}
|
||
|
||
LOGI("开始关闭播放器");
|
||
// 停止播放线程
|
||
video_player->stop_requested = true;
|
||
|
||
// 发送条件变量信号通知播放线程停止
|
||
pthread_mutex_lock(&video_player->play_mutex);
|
||
pthread_cond_signal(&video_player->play_cond);
|
||
pthread_mutex_unlock(&video_player->play_mutex);
|
||
|
||
// 等待播放线程结束,设置超时避免死锁
|
||
if (video_player->thread_running)
|
||
{
|
||
LOGI("等待播放线程结束");
|
||
struct timespec timeout;
|
||
clock_gettime(CLOCK_REALTIME, &timeout);
|
||
timeout.tv_sec += 3; // 设置3秒超时
|
||
|
||
int join_result = pthread_timedjoin_np(video_player->player_thread, NULL, &timeout);
|
||
if (join_result == ETIMEDOUT)
|
||
{
|
||
LOGE("播放线程结束超时,强制取消线程");
|
||
pthread_cancel(video_player->player_thread);
|
||
}
|
||
video_player->thread_running = false;
|
||
LOGI("播放线程已结束");
|
||
}
|
||
|
||
// 安全销毁播放器
|
||
if (video_player->player)
|
||
{
|
||
LOGI("安全销毁播放器");
|
||
// 先尝试正常停止
|
||
int stop_result = qua_mm_player_stop(video_player->player);
|
||
if (stop_result != 0)
|
||
{
|
||
LOGE("播放器stop失败,ret=%d,尝试强制重置", stop_result);
|
||
qua_mm_player_reset(video_player->player);
|
||
}
|
||
|
||
// 等待一小段时间确保清理完成
|
||
usleep(100 * 1000); // 100ms
|
||
|
||
qua_mm_player_destroy(video_player->player);
|
||
video_player->player = NULL;
|
||
LOGI("播放器销毁完成");
|
||
}
|
||
|
||
// 清空播放列表
|
||
video_player_clear_playlist(video_player);
|
||
|
||
LOGI("关闭黑洞显示");
|
||
// 关闭黑洞
|
||
dis_player_hole(video_player->display_idx);
|
||
|
||
LOGI("播放器关闭完成");
|
||
}
|
||
|
||
/**
|
||
* 销毁播放器
|
||
*/
|
||
void video_player_destroy(VideoPlayer *video_player)
|
||
{
|
||
if (!video_player)
|
||
return;
|
||
|
||
// 先关闭播放器
|
||
if (!video_player->stop_requested)
|
||
{
|
||
video_player_close(video_player);
|
||
}
|
||
|
||
// 清理播放列表
|
||
pthread_mutex_lock(&video_player->playlist_mutex);
|
||
for (int i = 0; i < video_player->media_count; i++)
|
||
{
|
||
if (video_player->media_paths[i])
|
||
{
|
||
free(video_player->media_paths[i]);
|
||
video_player->media_paths[i] = NULL;
|
||
}
|
||
}
|
||
free(video_player->media_paths); // 释放二级指针数组
|
||
video_player->media_paths = NULL;
|
||
video_player->media_count = 0;
|
||
pthread_mutex_unlock(&video_player->playlist_mutex);
|
||
|
||
// 确保播放线程已停止
|
||
video_player->stop_requested = true;
|
||
if (video_player->thread_running)
|
||
{
|
||
pthread_join(video_player->player_thread, NULL);
|
||
}
|
||
|
||
// 确保播放器已清理
|
||
if (video_player->player)
|
||
{
|
||
LOGI("video_player_destroy中清理剩余播放器");
|
||
qua_mm_player_stop(video_player->player);
|
||
qua_mm_player_destroy(video_player->player);
|
||
video_player->player = NULL;
|
||
}
|
||
|
||
// 关闭黑洞
|
||
dis_player_hole(video_player->display_idx);
|
||
|
||
pthread_mutex_destroy(&video_player->playlist_mutex);
|
||
|
||
free(video_player);
|
||
}
|
||
|
||
// ======================== 线程安全的播放列表管理 ========================
|
||
|
||
void video_player_add_item(VideoPlayer *player, const MediaPath *local_path)
|
||
{
|
||
if (!player || !local_path)
|
||
return;
|
||
|
||
pthread_mutex_lock(&player->playlist_mutex);
|
||
if (player->media_count < MAX_PLAYLIST_ITEMS)
|
||
{
|
||
player->media_paths[player->media_count++] = local_path;
|
||
}
|
||
pthread_mutex_unlock(&player->playlist_mutex);
|
||
}
|
||
void video_player_clear_playlist(VideoPlayer *player)
|
||
{
|
||
if (!player)
|
||
return;
|
||
|
||
pthread_mutex_lock(&player->playlist_mutex);
|
||
|
||
for (int i = 0; i < player->media_count; i++)
|
||
{
|
||
if (player->media_paths[i])
|
||
{
|
||
free(player->media_paths[i]);
|
||
player->media_paths[i] = NULL;
|
||
}
|
||
}
|
||
|
||
player->media_count = 0;
|
||
player->current_index = 0;
|
||
player->first_play = true;
|
||
pthread_mutex_unlock(&player->playlist_mutex);
|
||
}
|
||
|
||
void video_player_set_double_buffer(VideoPlayer *video_player)
|
||
{
|
||
if (!video_player)
|
||
return;
|
||
video_player_clear_playlist(video_player);
|
||
// video_player->media_count = 0;
|
||
// video_player->current_index = 0;
|
||
}
|
||
|
||
void video_player_set_size(VideoPlayer *video_player, e_player_area area)
|
||
{
|
||
if (!video_player)
|
||
return;
|
||
if (area.width > GET_SCR_WIDTH())
|
||
{
|
||
area.width = GET_SCR_WIDTH();
|
||
}
|
||
if (area.height > GET_SCR_HEIGHT())
|
||
{
|
||
area.height = GET_SCR_HEIGHT();
|
||
}
|
||
|
||
video_player->area = area;
|
||
video_player_set_hole(video_player);
|
||
}
|
||
|
||
// ======================== 播放控制 ========================
|
||
// 获取毫秒时间戳
|
||
long long get_current_time_ms()
|
||
{
|
||
struct timespec ts;
|
||
// 获取单调时钟(自系统启动后流逝的时间,不受系统时间修改影响)
|
||
if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
|
||
{
|
||
LOGE("clock_gettime failed");
|
||
return 0;
|
||
}
|
||
return (long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; // 转换为毫秒
|
||
}
|
||
|
||
int video_player_play(VideoPlayer *player)
|
||
{
|
||
if (!player)
|
||
return -1;
|
||
// 确保有可播放的内容
|
||
pthread_mutex_lock(&player->playlist_mutex);
|
||
bool has_media = (player->media_count > 0);
|
||
pthread_mutex_unlock(&player->playlist_mutex);
|
||
|
||
if (!has_media)
|
||
{
|
||
LOGW("播放列表为空");
|
||
return -1;
|
||
}
|
||
|
||
if (player->thread_running)
|
||
{
|
||
LOGW("播放线程已在运行");
|
||
return -1;
|
||
}
|
||
player->stop_requested = false;
|
||
player->first_play = true;
|
||
|
||
// player->actual_play_ms = 0; // 重置播放耗时统计
|
||
if (pthread_create(&player->player_thread, NULL, player_thread_func, player) == 0)
|
||
{
|
||
player->thread_running = true;
|
||
LOGI("播放线程已启动");
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
LOGE("播放线程创建失败");
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
void video_player_next(VideoPlayer *player)
|
||
{
|
||
if (!player)
|
||
return;
|
||
|
||
pthread_mutex_lock(&player->playlist_mutex);
|
||
if (player->media_count > 0)
|
||
{
|
||
player->current_index = (player->current_index + 1) % player->media_count;
|
||
}
|
||
pthread_mutex_unlock(&player->playlist_mutex);
|
||
}
|
||
|
||
void video_player_set_hole(VideoPlayer *video_player)
|
||
{
|
||
if (!video_player)
|
||
return;
|
||
|
||
// 直接使用视频区域,不做坐标转换
|
||
// 因为 QuaPlayer 库已经处理了旋转后的实际显示位置
|
||
int x = video_player->area.x;
|
||
int y = video_player->area.y;
|
||
int width = video_player->area.width;
|
||
int height = video_player->area.height;
|
||
|
||
if (video_player->display_idx == 0)
|
||
{
|
||
LOGD("设置视频挖洞区域。x=%d,y=%d,w=%d,h=%d", x, y, width, height);
|
||
fbdev_set_hole(x, y, width, height);
|
||
}
|
||
else
|
||
{
|
||
LOGD("设置视频挖洞区域。x=%d,y=%d,w=%d,h=%d", x, y, width, height);
|
||
fbdev2_set_hole(x, y, width, height);
|
||
}
|
||
}
|
||
|
||
// ======================== 内部实现 ========================
|
||
static void *player_thread_func(void *arg)
|
||
{
|
||
VideoPlayer *player = (VideoPlayer *)arg;
|
||
|
||
while (!player->stop_requested)
|
||
{
|
||
pthread_mutex_lock(&player->playlist_mutex);
|
||
if (player->media_count == 0)
|
||
{
|
||
pthread_mutex_unlock(&player->playlist_mutex);
|
||
LOGW("播放列表为空,停止播放");
|
||
break;
|
||
}
|
||
|
||
int index = player->current_index % player->media_count;
|
||
|
||
MediaPath *media_path = player->media_paths[index];
|
||
|
||
// char file_path[MAX_URL_LEN];
|
||
// strncpy(file_path, player->media_paths[index], MAX_URL_LEN);
|
||
pthread_mutex_unlock(&player->playlist_mutex);
|
||
if (!media_path)
|
||
{
|
||
player->current_index = (player->current_index + 1) % player->media_count;
|
||
continue;
|
||
}
|
||
|
||
// LOGI("开始播放第 %d 个文件: %s", index, file_path);
|
||
is_stop = QUA_FALSE;
|
||
int ret = play_video(player, media_path);
|
||
if (player->first_play)
|
||
{
|
||
player->first_play = false;
|
||
}
|
||
// player->start_time_ms=0; // 重置播放开始时间
|
||
if (ret != 0)
|
||
{
|
||
retry_count++;
|
||
if (retry_count > retry_max)
|
||
{
|
||
// if(player->media_count==1){
|
||
// LOGE("播放失败,超过最大重试次数%d:%s",retry_count,file_path);
|
||
// break;
|
||
// }
|
||
LOGE("播放失败,超过最大重试次数%d:%s", retry_count, media_path->path);
|
||
break;
|
||
}
|
||
// else{
|
||
LOGE("播放失败,等待1秒后重试%d:%s", retry_count, media_path->path);
|
||
// sleep(2);
|
||
// }
|
||
sleep(1);
|
||
// continue;
|
||
}
|
||
else
|
||
{
|
||
retry_count = 0;
|
||
// LOGD("视频播放完成,理论时长:% lldms,实际耗时:% lldms(显示器 % d)",
|
||
// player->video_duration_ms, player->actual_play_ms, player->display_idx);
|
||
}
|
||
|
||
// 播放完成后切换下一个(根据双屏冗余调整切换时机)
|
||
// #if DISP_COUNT == 2
|
||
// // 双屏模式:确保所有设备都播放完成后再切换(等待冗余时间)
|
||
// if (sync_play){
|
||
// long long wait_switch_ms = (player->video_duration_ms + DECODE_REDUNDANCY_MS) - player->actual_play_ms;
|
||
// if (wait_switch_ms > 0)
|
||
// {
|
||
// LOGD("双屏模式等待冗余时间:% lldms(显示器 % d)", wait_switch_ms, player->display_idx);
|
||
// usleep(wait_switch_ms * MS_TO_USEC);
|
||
// }
|
||
// }
|
||
// #endif
|
||
// 播放完成或失败后切换下一个
|
||
player->current_index = (player->current_index + 1) % player->media_count;
|
||
// LOGI("开始切换下一个视频");
|
||
}
|
||
|
||
player->thread_running = false;
|
||
LOGW("播放线程退出(显示器 % d)", player->display_idx);
|
||
return NULL;
|
||
}
|
||
|
||
// 计算显示区域,根据视频比例和屏幕比例,计算显示区域的大小和位置。视频大小必须为8的倍数
|
||
static void calculate_display_rect(int img_width, int img_height, lv_area_t *area, qua_rect_t *rect, bool auto_center)
|
||
{
|
||
int is_screen_landscape = GET_SCR_WIDTH() > GET_SCR_HEIGHT();
|
||
int is_image_landscape = img_width > img_height;
|
||
int is_adaptive = (is_image_landscape != is_screen_landscape);
|
||
// LV_LOG_USER("area [%d %d] area rect size [%d %d]", area->x1, area->y1, area->x2, area->y2);
|
||
// LV_LOG_USER("img_width: %d, img_height:%d\n", img_width, img_height);
|
||
rect->x = area->x1;
|
||
rect->y = area->y1;
|
||
rect->width = area->x2 - area->x1 + 1;
|
||
rect->height = area->y2 - area->y1 + 1;
|
||
|
||
if (auto_center)
|
||
{
|
||
if (true)
|
||
{
|
||
float screen_ratio = (float)GET_SCR_WIDTH() / GET_SCR_HEIGHT();
|
||
float image_ratio = (float)img_width / img_height;
|
||
// LV_LOG_USER("screen_ratio =%f,image_ratio=%d", screen_ratio, image_ratio);
|
||
if (screen_ratio > image_ratio)
|
||
{
|
||
rect->width = GET_SCR_HEIGHT() * img_width / img_height;
|
||
rect->height = GET_SCR_HEIGHT();
|
||
rect->x = (GET_SCR_WIDTH() - rect->width) / 2;
|
||
rect->y = 0;
|
||
if (rect->x > 0 && rect->x % 2 > 0)
|
||
rect->x += 1;
|
||
}
|
||
else
|
||
{
|
||
rect->width = GET_SCR_WIDTH();
|
||
rect->height = GET_SCR_WIDTH() * img_height / img_width;
|
||
rect->x = 0;
|
||
rect->y = (GET_SCR_HEIGHT() - rect->height) / 2;
|
||
if (rect->y > 0 && rect->y % 2 > 0)
|
||
rect->y += 1;
|
||
// rect->height = (area->y2- area->y1)/ 4 *4;
|
||
// LV_LOG_USER("#######");
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
rect->x = area->x1 / 2 * 2;
|
||
rect->y = area->y1 / 2 * 2;
|
||
rect->width = area->x2 / 8 * 8;
|
||
rect->height = area->y2 / 8 * 8;
|
||
// LV_LOG_USER("@@@@@@@@@");
|
||
}
|
||
rect->width = rect->width / 8 * 8;
|
||
rect->height = rect->height / 8 * 8;
|
||
LOGD("display rect [%d %d %d %d]", rect->x, rect->y, rect->width, rect->height);
|
||
}
|
||
|
||
static int play_video(VideoPlayer *video_player, const MediaPath *media_path)
|
||
{
|
||
|
||
void *player = NULL;
|
||
QUA_S32 ret = 0;
|
||
QUA_BOOL pos_cb = QUA_TRUE;
|
||
if (!video_player->player)
|
||
{
|
||
if (video_player->display_idx == 0)
|
||
{
|
||
player = qua_mm_player_create("e_video_play");
|
||
}
|
||
else
|
||
{
|
||
player = qua_mm_player_create("e_video_play1");
|
||
}
|
||
if (player)
|
||
{
|
||
qua_mm_player_set_parameter(player, KEY_PARAMETER_VO_DISPLAY_ID, (QUA_VOID_PTR)video_player->display);
|
||
qua_mm_player_set_parameter(player, KEY_PARAMETER_VO_ROTATE, (QUA_VOID_PTR)&g_screen_rotation);
|
||
xos_player_set_volume(player, 0);
|
||
qua_mm_player_set_loop(player, QUA_FALSE);
|
||
video_player->player = player;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
player = video_player->player;
|
||
// LOGI("reset player");
|
||
qua_mm_player_reset(player);
|
||
}
|
||
|
||
if (!player)
|
||
{
|
||
LOGE("创建播放器失败");
|
||
return -1;
|
||
}
|
||
ret = qua_mm_player_set_data_source(player, media_path->path);
|
||
if (ret != 0)
|
||
{
|
||
LOGE("设置播放源失败 %s", media_path->path);
|
||
goto fail;
|
||
}
|
||
|
||
qua_size_t size;
|
||
qua_mm_player_get_parameter(player, KEY_PARAMETER_VIDEO_SIZE, (QUA_VOID_PTR)&size);
|
||
// LOGI("获取到视频分辨率 %dx%d", size.width, size.height);
|
||
if (size.width * size.height > 1920 * 1080)
|
||
{
|
||
LOGE("视频分辨率超过1920*1080");
|
||
goto fail;
|
||
}
|
||
|
||
e_player_area video_area = video_player->area;
|
||
lv_area_t area = {0};
|
||
area.x1 = video_area.x;
|
||
area.x2 = video_area.width;
|
||
area.y1 = video_area.y;
|
||
area.y2 = video_area.height;
|
||
|
||
qua_rect_t rect = {0};
|
||
calculate_display_rect(size.width, size.height, &area, &rect, false);
|
||
|
||
qua_rect_t chn_rect;
|
||
chn_rect.x = 0;
|
||
chn_rect.y = 0;
|
||
chn_rect.width = GET_SCR_WIDTH();
|
||
chn_rect.height = GET_SCR_HEIGHT();
|
||
qua_mm_player_set_parameter(player, KEY_PARAMETER_VO_DISPLAY_RECT, (QUA_VOID_PTR)&chn_rect);
|
||
qua_mm_player_set_parameter(player, KEY_PARAMETER_VO_CHN_RECT, (QUA_VOID_PTR)&rect);
|
||
|
||
ret = qua_mm_player_prepare(player);
|
||
if (ret != 0)
|
||
{
|
||
LOGE("准备播放失败. ret=%d", ret);
|
||
goto fail;
|
||
}
|
||
|
||
ret = qua_mm_player_set_callback(player, my_play_callback, video_player);
|
||
if (ret != 0)
|
||
{
|
||
LOGE("设置回调失败. ret=%d", ret);
|
||
goto fail;
|
||
}
|
||
|
||
// qua_mm_player_set_parameter(player, KEY_PARAMETER_SET_CURRENT_POSITION_CALLBACK, (QUA_VOID *)&pos_cb);
|
||
// 获取视频时长并计算同步起始时间
|
||
long long video_duration_us = qua_mm_player_get_durationUs(player); // 转换为毫秒
|
||
video_player->video_duration_ms = video_duration_us / 1000; // 转换为毫秒
|
||
LOGD("获取视频时长:% lldms(显示器 % d)", video_player->video_duration_ms, video_player->display_idx);
|
||
// 同步播放处理
|
||
// QUA_S32 qua_mm_player_seek(QUA_VOID* player, QUA_U64 timeUs);
|
||
long long play_position_ms = 0;
|
||
if (sync_play && video_player->media_count == 1)
|
||
{
|
||
// long long currentTime=get_current_time_ms();
|
||
// long long minuteMillis= video_player->video_duration_ms+1000;
|
||
// if (video_player->first_play)
|
||
// {
|
||
// // 首次开机时 系统时间尚未校时,因此可能计算的位置不对
|
||
// long long viode_duration_ms = video_player->video_duration_ms + 600;
|
||
// long long positionMs = get_current_time_ms() % viode_duration_ms; // 当前应该播放的位置(毫秒)
|
||
// qua_mm_player_seek(player, positionMs * 1000); // 转换为微秒
|
||
// play_position_ms = positionMs; // 使播放器提前结束,防止下次播放时错过开始时间
|
||
// }
|
||
// else
|
||
// {
|
||
// long long viode_duration_ms= video_player->video_duration_ms+2000;
|
||
long long viode_duration_ms = video_player->video_duration_ms + 1000;
|
||
// long long positionMs = get_current_time_ms() % viode_duration_ms;// 当前应该播放的位置(毫秒)
|
||
|
||
long long t_remainder = get_current_time_ms() % viode_duration_ms;
|
||
LOGI("距离视频开始:%lld", t_remainder);
|
||
|
||
long long tolerance = 300;
|
||
while (!is_stop && !video_player->stop_requested)
|
||
{
|
||
long long currentTime = get_current_time_ms();
|
||
long long remainder = currentTime % viode_duration_ms;
|
||
if (remainder <= tolerance || (viode_duration_ms - remainder) <= tolerance)
|
||
{
|
||
break; // 开始播放
|
||
}
|
||
usleep(50 * MS_TO_USEC);
|
||
}
|
||
//}
|
||
}
|
||
|
||
video_player_set_hole(video_player);
|
||
|
||
ret = qua_mm_player_start(player);
|
||
if (ret != 0)
|
||
{
|
||
LOGE("播放器启动失败,ret=% d(显示器 % d)", ret, video_player->display_idx);
|
||
goto fail;
|
||
}
|
||
|
||
if (play_position_ms > 0)
|
||
{
|
||
video_player->last_play_time_ms = get_current_time_ms() - play_position_ms;
|
||
}
|
||
else
|
||
{
|
||
video_player->last_play_time_ms = get_current_time_ms(); // 记录播放开始时间
|
||
}
|
||
// 计算结束时间
|
||
// long long end_time = get_current_time_ms() + video_player->video_duration_ms;
|
||
long long end_time = video_player->last_play_time_ms + video_player->video_duration_ms;
|
||
pthread_mutex_lock(&video_player->play_mutex);
|
||
while (get_current_time_ms() < end_time && !video_player->stop_requested)
|
||
{
|
||
int status = pthread_cond_timedwait(&video_player->play_cond, &video_player->play_mutex, (const struct timespec *)&(struct timespec){.tv_sec = end_time / 1000, .tv_nsec = (end_time % 1000) * 1000000});
|
||
if (status == ETIMEDOUT)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
pthread_mutex_unlock(&video_player->play_mutex);
|
||
|
||
// 发送播放通知
|
||
// event_play_start(media_path);
|
||
// usleep(video_duration_us);
|
||
// 使用循环检查方式替代usleep,支持提前退出
|
||
// long long video_duration_ms = video_duration_us / 1000; // 转换为毫秒
|
||
// while (get_current_time_ms() - start_time < video_player->video_duration_ms&&!is_stop) {
|
||
// if (video_player->stop_requested) {
|
||
// LOGI("播放被提前终止(显示器 %d)", video_player->display_idx);
|
||
// qua_mm_player_stop(player);
|
||
// return 0;
|
||
// }
|
||
// usleep(20 * 1000); // 20ms检查一次
|
||
// }
|
||
|
||
// 如果播放器中途被结束,则跳过
|
||
if (video_player->stop_requested)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
// LOGI("视频播放结束01");
|
||
// LOGI("~~~~~~~~~~~~~~~~视频播放结束~~~~~~~~~~~~~~~~");
|
||
ret = qua_mm_player_stop(player);
|
||
if (ret != 0)
|
||
{
|
||
LOGE("停止播放失败 ");
|
||
goto fail;
|
||
}
|
||
qua_mm_player_reset(player);
|
||
|
||
return 0;
|
||
|
||
fail:
|
||
if (player)
|
||
{
|
||
qua_mm_player_destroy(player);
|
||
player = NULL;
|
||
video_player->player = NULL;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
static void xos_player_set_volume(void *player, int volume)
|
||
{
|
||
// LV_LOG_USER("player volume is %d\n", volume);
|
||
qua_mm_player_set_volume(player, volume);
|
||
}
|
||
|
||
/**
|
||
* 视频播放器消息回调
|
||
*/
|
||
static int my_play_callback(void *user, int evt, void *info)
|
||
{
|
||
VideoPlayer *video_player = (VideoPlayer *)user;
|
||
if (evt == PLAYER_EVT_PLAYBACK_COMPLETE)
|
||
{
|
||
LOGD("收到播放完成回调(显示器 % d)", video_player->display_idx);
|
||
is_stop = QUA_TRUE;
|
||
// pthread_mutex_lock(&video_player->is_stop_mutex);
|
||
// video_player->is_stop = QUA_TRUE;
|
||
// pthread_mutex_unlock(&video_player->is_stop_mutex);
|
||
}
|
||
else if (evt == PLAYER_EVT_CURRENT_POSITION)
|
||
{
|
||
// long pos = (long long)(*(QUA_U64 *)info) / USEC_TO_SECONDS;
|
||
// LOGI("video callback event POSITION %d", pos);
|
||
}
|
||
else
|
||
{
|
||
LOGD("收到播放器事件:% d(显示器 % d)", evt, video_player->display_idx);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static void play_error(VideoPlayer *video_player, const int code)
|
||
{
|
||
LOGE("播放器出现异常,错误码: % d(显示器 % d)", code, video_player->display_idx);
|
||
}
|
||
|
||
static void dis_player_hole(int display_idx)
|
||
{
|
||
// 视频播放时,设置挖洞区域。
|
||
if (display_idx == 0)
|
||
{
|
||
fbdev_set_hole(0, 0, 0, 0);
|
||
}
|
||
else
|
||
{
|
||
fbdev2_set_hole(0, 0, 0, 0);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 检查播放器是否正常
|
||
*/
|
||
void video_check_play_state(VideoPlayer *player)
|
||
{
|
||
if (!player)
|
||
{
|
||
return;
|
||
}
|
||
if (player->thread_running)
|
||
{
|
||
LOGI("播放线程正在运行中...");
|
||
return;
|
||
}
|
||
// 判断播放列表是否为空、
|
||
pthread_mutex_lock(&player->playlist_mutex);
|
||
bool has_media = (player->media_count > 0);
|
||
pthread_mutex_unlock(&player->playlist_mutex);
|
||
|
||
if (!has_media)
|
||
{
|
||
LOGD("播放线程退出,播放列表为空");
|
||
return;
|
||
}
|
||
|
||
if (player->stop_requested)
|
||
{
|
||
LOGD("播放线程退出,用户主动调用退出");
|
||
return;
|
||
}
|
||
|
||
// 检查线程是否已结束
|
||
void *value = pthread_getspecific(player->player_thread);
|
||
if (value == (void *)1)
|
||
{
|
||
LOGI("播放器线程已退出");
|
||
// 计算距离上次播放时间
|
||
long long last_play_time_ms = player->last_play_time_ms;
|
||
long long current_time_ms = get_current_time_ms();
|
||
// 距离最后一次播放时间
|
||
long long time_diff_ms = current_time_ms - last_play_time_ms;
|
||
|
||
// 判断播放器是否停止;最后播放时间-当前播放视频的总时长+5秒。超过则说明播放意外退出。重新开始播放
|
||
// 如果距离上次播放时间小于视频总时长+5秒,则等待5秒后重试
|
||
if (time_diff_ms > (player->video_duration_ms + 5000))
|
||
{
|
||
// 尝试开始播放
|
||
LOGI("播放线程退出,开始尝试恢复播放");
|
||
video_player_play(player);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LOGI("播放器线程尚未退出");
|
||
}
|
||
} |