void CPlayerSession::display_status (void) { sync_message(LOG_DEBUG, "*******************************************************************"); sync_message(LOG_DEBUG, "current time "U64, m_current_time); CPlayerMedia *p = m_my_media; while (p != NULL) { p->display_status(); p = p->get_next(); } }
/* * audio_is_ready - when the audio indicates that it's ready, it will * send a latency number, and a play time */ void CPlayerSession::audio_is_ready (uint64_t latency, uint64_t time) { m_start = get_time_of_day(); sync_message(LOG_DEBUG, "Aisready "U64, m_start); m_start -= time; m_latency = latency; sync_message(LOG_DEBUG, "Audio is ready "U64" - latency "U64, time, latency); sync_message(LOG_DEBUG, "m_start is "X64, m_start); m_waiting_for_audio = 0; SDL_SemPost(m_sync_sem); }
void CPlayerSession::adjust_start_time (int64_t time) { m_start -= time; #if 0 sync_message(LOG_INFO, "Adjusting start time "D64 " to " U64, time, get_current_time()); #endif SDL_SemPost(m_sync_sem); }
/* * process_msg_queue - receive messages for the sync task. Most are * state changes. */ int CPlayerSession::process_msg_queue (int state) { CMsg *newmsg; while ((newmsg = m_sync_thread_msg_queue.get_message()) != NULL) { #ifdef DEBUG_SYNC_MSGS sync_message(LOG_DEBUG, "Sync thread msg %d", newmsg->get_value()); #endif switch (newmsg->get_value()) { case MSG_PAUSE_SESSION: state = SYNC_STATE_PAUSED; break; case MSG_START_SESSION: if (state != SYNC_STATE_INIT) state = SYNC_STATE_WAIT_SYNC; break; case MSG_STOP_THREAD: state = SYNC_STATE_EXIT; break; case MSG_SYNC_RESIZE_SCREEN: { CVideoSync *vs = m_video_list; while (vs != NULL) { vs->set_screen_size(m_screen_scale); if (m_video_count == 1) { vs->set_fullscreen(m_fullscreen); } vs->do_video_resize(m_pixel_width, m_pixel_height, m_max_width, m_max_height); vs = vs->GetNextVideo(); } } break; default: sync_message(LOG_ERR, "Sync thread received message %d", newmsg->get_value()); break; } delete newmsg; newmsg = NULL; } return (state); }
/* * sync_thread_wait_audio - wait until the audio thread starts and signals * us. */ int CPlayerSession::sync_thread_wait_audio (void) { int state; state = process_msg_queue(SYNC_STATE_WAIT_AUDIO); if (state == SYNC_STATE_WAIT_AUDIO) { if (m_waiting_for_audio != 0) { SDL_SemWaitTimeout(m_sync_sem, 10); } else { // make sure we set the current time get_current_time(); sync_message(LOG_DEBUG, "Current time is "U64, m_current_time); return (SYNC_STATE_PLAYING); } } return (state); }
/* * process_sdl_events - process the sdl event queue. This will allow the * video window to capture things like close, key strokes. */ void CPlayerSession::process_sdl_events (void) { SDL_Event event; CVideoSync *vs; while (SDL_PollEvent(&event) == 1) { switch (event.type) { case SDL_QUIT: m_master_msg_queue->send_message(MSG_RECEIVED_QUIT, m_master_msg_queue_sem); #ifdef DEBUG_SYNC_SDL_EVENTS sync_message(LOG_DEBUG, "Quit event detected"); #endif break; case SDL_KEYDOWN: #ifdef DEBUG_SYNC_SDL_EVENTS sync_message(LOG_DEBUG, "Pressed %x %s", event.key.keysym.mod, SDL_GetKeyName(event.key.keysym.sym)); #endif switch (event.key.keysym.sym) { case SDLK_ESCAPE: vs = m_video_list; while (vs != NULL) { if (vs->get_fullscreen() != 0) { vs->set_fullscreen(0); vs->do_video_resize(); } vs = vs->GetNextVideo(); } break; case SDLK_RETURN: if ((event.key.keysym.mod & (KMOD_ALT | KMOD_META)) != 0) { // alt-enter - full screen if (m_video_list && m_video_count == 1 && m_video_list->get_fullscreen() == 0) { m_video_list->set_fullscreen(1); m_video_list->do_video_resize(); } } break; default: break; } sdl_event_msg_t msg; msg.mod = event.key.keysym.mod; msg.sym = event.key.keysym.sym; m_master_msg_queue->send_message(MSG_SDL_KEY_EVENT, (unsigned char *)&msg, sizeof(msg), m_master_msg_queue_sem); break; case SDL_MOUSEBUTTONUP: if (event.button.which == 0 && m_mouse_click_callback != NULL) { (m_mouse_click_callback)(m_mouse_click_callback_ud, event.button.x, event.button.y); } else { sdl_mouse_msg_t mouse_msg; mouse_msg.button = event.button.which; mouse_msg.x = event.button.x; mouse_msg.y = event.button.y; m_master_msg_queue->send_message(MSG_SDL_MOUSE_EVENT, (uint8_t *)&mouse_msg, sizeof(mouse_msg), m_master_msg_queue_sem); } break; default: break; } } }
/* * sync_thread - call the correct handler, and wait for the state changes. * Each handler should only return the new state... */ int CPlayerSession::sync_thread (int state) { int newstate = 0; process_sdl_events(); switch (state) { case SYNC_STATE_INIT: m_session_state = SESSION_BUFFERING; newstate = sync_thread_init(); break; case SYNC_STATE_WAIT_SYNC: newstate = sync_thread_wait_sync(); break; case SYNC_STATE_WAIT_AUDIO: newstate = sync_thread_wait_audio(); break; case SYNC_STATE_PLAYING: newstate = sync_thread_playing(); break; case SYNC_STATE_PAUSED: newstate = sync_thread_paused(); break; case SYNC_STATE_AUDIO_RESYNC: newstate = sync_thread_audio_resync(); break; case SYNC_STATE_DONE: newstate = sync_thread_done(); break; case SYNC_STATE_WAIT_AUDIO_READY: newstate = sync_thread_wait_audio_ready(); break; case SYNC_STATE_WAIT_TIMED_INIT: newstate = sync_thread_wait_timed_init(); break; case SYNC_STATE_EXIT: newstate = SYNC_STATE_EXIT; break; } #ifdef DEBUG_SYNC_STATE if (state != newstate) sync_message(LOG_INFO, "sync changed state %s to %s", sync_state[state], sync_state[newstate]); #endif if (state != newstate) { state = newstate; switch (state) { case SYNC_STATE_WAIT_SYNC: case SYNC_STATE_WAIT_AUDIO_READY: m_session_state = SESSION_BUFFERING; break; case SYNC_STATE_WAIT_AUDIO: case SYNC_STATE_AUDIO_RESYNC: break; case SYNC_STATE_WAIT_TIMED_INIT: case SYNC_STATE_PLAYING: m_session_state = SESSION_PLAYING; break; case SYNC_STATE_PAUSED: { for (CTimedSync *ts = m_timed_sync_list; ts != NULL; ts = ts->GetNext()) { ts->flush_sync_buffers(); } if (m_audio_sync != NULL) m_audio_sync->flush_sync_buffers(); m_session_state = SESSION_PAUSED; m_sync_pause_done = 1; break; } case SYNC_STATE_DONE: { CVideoSync *vs = m_video_list; while (vs != NULL) { if (vs->get_fullscreen() != 0) { vs->set_fullscreen(0); vs->do_video_resize(); } vs = vs->GetNextVideo(); } m_master_msg_queue->send_message(MSG_SESSION_FINISHED, m_master_msg_queue_sem); m_session_state = SESSION_DONE; break; } case SYNC_STATE_EXIT: { for (CTimedSync *ts = m_timed_sync_list; ts != NULL; ts = ts->GetNext()) { ts->flush_sync_buffers(); } if (m_audio_sync != NULL) m_audio_sync->flush_sync_buffers(); break; } } } return (state); }
int CPlayerSession::sync_thread_audio_resync (void) { int state; uint64_t audio_resync_time = 0; int64_t wait_audio_time; bool video_have_next_time = false; uint64_t video_next_time; bool have_audio_eof = false; bool need_audio_resync = false; bool have_video_eof = false; bool need_audio_restart = false; state = process_msg_queue(SYNC_STATE_AUDIO_RESYNC); if (state == SYNC_STATE_AUDIO_RESYNC) { get_current_time(); need_audio_resync = m_audio_sync->check_audio_sync(m_current_time, audio_resync_time, wait_audio_time, have_audio_eof, need_audio_restart); if (need_audio_restart) { // this occurs when the time is in the past, but we're continuing // video return SYNC_STATE_WAIT_SYNC; } if (need_audio_resync == false) { // video continued, so, restart audio sync_message(LOG_ERR, "resync but no audio resync"); return SYNC_STATE_PLAYING; } have_video_eof = true; video_next_time = MAX_UINT64; for (CTimedSync *ts = m_timed_sync_list; ts != NULL; ts = ts->GetNext()) { uint64_t cmp; bool have_eof; if (ts->play_at(m_current_time, true, cmp, have_eof)) { video_have_next_time = true; video_next_time = MIN(cmp, video_next_time); have_video_eof &= have_eof; } } sync_message(LOG_DEBUG, "audio resync - "U64" have_next %d "U64, m_current_time, video_have_next_time, video_next_time); sync_message(LOG_DEBUG, "audio time "U64, audio_resync_time); if (have_audio_eof && have_video_eof) { return SYNC_STATE_DONE; } if (video_have_next_time == false) { SDL_SemWaitTimeout(m_sync_sem, 10); } else { int64_t diff = video_next_time - audio_resync_time; if (diff >= TO_D64(-5000) && diff <= TO_D64(5000)) { return SYNC_STATE_WAIT_SYNC; } diff = video_next_time - m_current_time; if (diff < 0 || diff > TO_D64(1000)) { // drop this frame return SYNC_STATE_WAIT_SYNC; } if (diff > 9) { diff = 9; } if (diff >= 9) { int delay = (int)diff; SDL_SemWaitTimeout(m_sync_sem, delay); } } } return state; }
/* * sync_thread_playing - do the work of displaying the video and making * sure we're in sync. */ int CPlayerSession::sync_thread_playing (void) { int state; uint64_t audio_resync_time = 0; int64_t wait_audio_time; bool video_have_next_time = false; uint64_t video_next_time; bool have_audio_eof = false; bool need_audio_restart = false; bool need_audio_resync = false; bool have_video_eof = false; state = process_msg_queue(SYNC_STATE_PLAYING); if (state == SYNC_STATE_PLAYING) { get_current_time(); if (m_audio_sync) { need_audio_resync = m_audio_sync->check_audio_sync(m_current_time, audio_resync_time, wait_audio_time, have_audio_eof, need_audio_restart); if (need_audio_resync) { if (m_timed_sync_list) { // wait for video to play out if (config.GetBoolValue(CONFIG_SHORT_VIDEO_RENDER)) { // flush timed sync for (CTimedSync *ts = m_timed_sync_list; ts != NULL; ts = ts->GetNext()) { ts->flush_sync_buffers(); ts->flush_decode_buffers(); } return SYNC_STATE_WAIT_AUDIO_READY; } return SYNC_STATE_AUDIO_RESYNC; } else { // skip right to the audio return SYNC_STATE_WAIT_SYNC; } } } video_next_time = MAX_UINT64; if (m_timed_sync_list != NULL) { have_video_eof = true; for (CTimedSync *ts = m_timed_sync_list; ts != NULL; ts = ts->GetNext()) { uint64_t cmp; bool have_eof = false; if (ts->play_at(m_current_time, false, cmp, have_eof)) { video_have_next_time = true; #if 0 sync_message(LOG_DEBUG, "return "U64 " "U64 " %u", cmp, m_current_time, have_eof); #endif video_next_time = MIN(cmp, video_next_time); } have_video_eof &= have_eof; } } int delay = 0; bool have_delay = false; if (m_timed_sync_list && m_audio_sync) { if (have_video_eof && have_audio_eof) { return (SYNC_STATE_DONE); } if (video_have_next_time) { have_delay = true; int64_t video_wait_time = video_next_time - m_current_time; delay = (int)video_wait_time; } } else if (m_timed_sync_list) { if (have_video_eof) { sync_message(LOG_DEBUG, "have video eof %d", have_video_eof); return (SYNC_STATE_DONE); } if (video_have_next_time) { have_delay = true; delay = (int)(video_next_time - m_current_time); } } else { // audio only if (have_audio_eof) { return (SYNC_STATE_DONE); } if (audio_resync_time != 0) { have_delay = true; delay = (int)(audio_resync_time - m_current_time); } } if (have_delay) { #if 0 if (delay >= 9) { delay = 9; SDL_SemWaitTimeout(m_sync_sem, delay); } #else //sync_message(LOG_DEBUG, "delay %d", delay); if (delay >= 10) { delay = 10; } SDL_SemWaitTimeout(m_sync_sem, delay); #endif } else { SDL_SemWaitTimeout(m_sync_sem, 10); } } return (state); }
/* * sync_thread_wait_sync - wait until all decoding threads have put * data into the sync classes. */ int CPlayerSession::sync_thread_wait_sync (void) { int state; state = process_msg_queue(SYNC_STATE_WAIT_SYNC); if (state == SYNC_STATE_WAIT_SYNC) { // We're not synced. See if the video is ready, and if the audio // is ready. Then start them going... bool vsynced = true; int asynced = 1; uint64_t astart = 0, vstart = MAX_UINT64; for (CTimedSync *ts = m_timed_sync_list; ts != NULL; ts = ts->GetNext()) { uint64_t cmp; if (ts->active_at_start()) { if (ts->is_ready(cmp)) { vstart = MIN(cmp, vstart); } else { vsynced = false; } } } if (m_audio_sync) { asynced = m_audio_sync->is_audio_ready(astart); } if (vsynced && asynced == 1) { /* * Audio and video are synced. */ if (m_audio_sync) { /* * If we have audio, we use that for syncing. Start it up */ m_first_time_played = astart; m_current_time = astart; sync_message(LOG_DEBUG, "Astart is "U64, astart); if (m_timed_sync_list) sync_message(LOG_DEBUG, "Vstart is "U64, vstart); m_waiting_for_audio = 1; state = SYNC_STATE_WAIT_AUDIO; m_audio_sync->play_audio(); } else if (m_timed_sync_list) { /* * Video only - set up the start time based on the video time * returned */ m_first_time_played = vstart; m_current_time = vstart; m_waiting_for_audio = 0; m_start = get_time_of_day(); m_start -= m_current_time; state = SYNC_STATE_PLAYING; } sync_message(LOG_DEBUG, "Resynced at time "U64 " "U64, m_current_time, vstart); } else { SDL_Delay(10); } } return (state); }
/* * sync_thread_init - wait until all the sync routines are initialized. */ int CPlayerSession::sync_thread_init (void) { int ret = 1; uint failed = 0; CTimedSync *ts; bool all_timed_inited, audio_inited = false, any_timed_inited; if (m_audio_sync != NULL) { ret = m_audio_sync->initialize_audio(m_timed_sync_list != NULL); if (ret <= 0) { //sync_message(LOG_DEBUG, "audio sync failed init"); audio_inited = false; failed |= 1; } else { audio_inited = true; //sync_message(LOG_DEBUG, "audio sync init"); if (config.GetBoolValue(CONFIG_SHORT_VIDEO_RENDER)) return (SYNC_STATE_WAIT_AUDIO_READY); } } all_timed_inited = initialize_timed_sync(failed, any_timed_inited); if ((m_audio_sync == NULL || audio_inited) && all_timed_inited) { return (SYNC_STATE_WAIT_SYNC); } if ((m_audio_sync != NULL && audio_inited) || any_timed_inited) { m_init_tries_made_with_no_media++; if (m_init_tries_made_with_no_media > 5 * 100) { sync_message(LOG_CRIT, "One media is not initializing; it might not be receiving correctly"); if ((failed & 0x2) != 0 ) { sync_message(LOG_INFO, "video failed"); } if ((failed & 0x4) != 0 ) { sync_message(LOG_INFO, "timed text failed"); } if ((failed & 0x1) != 0 ) { sync_message(LOG_INFO, "audio failed"); } ret = -1; } } else { m_init_tries_made_with_no_media++; if (m_init_tries_made_with_no_media > 30 * 100) { sync_message(LOG_CRIT, "No media has been initialized or received"); ret = -1; } } if (ret == -1) { sync_message(LOG_CRIT, "Fatal error while initializing hardware"); ts = m_timed_sync_list; while (ts != NULL) { ts->flush_sync_buffers(); ts = ts->GetNext(); } if (m_audio_sync != NULL) { m_audio_sync->flush_sync_buffers(); } m_master_msg_queue->send_message(MSG_RECEIVED_QUIT, m_master_msg_queue_sem); m_hardware_error = 1; return (SYNC_STATE_DONE); } CMsg *newmsg; newmsg = m_sync_thread_msg_queue.get_message(); if (newmsg != NULL) { int value = newmsg->get_value(); delete newmsg; if (value == MSG_STOP_THREAD) { return (SYNC_STATE_EXIT); } if (value == MSG_PAUSE_SESSION) { m_sync_pause_done = 1; } } SDL_SemWaitTimeout(m_sync_sem, 10); return (SYNC_STATE_INIT); }