📄 sync.cpp
字号:
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is MPEG4IP.
*
* The Initial Developer of the Original Code is Cisco Systems Inc.
* Portions created by Cisco Systems Inc. are
* Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
*
* Contributor(s):
* Bill May wmay@cisco.com
*/
/*
* sync.cpp - provide sync thread implementations of CPlayerSession
*/
#include <stdlib.h>
#include "player_session.h"
#include "player_media.h"
#include <SDL.h>
#include <SDL_thread.h>
#include "player_util.h"
#include "audio.h"
#include "video.h"
//#define DEBUG_SYNC_STATE 1
//#define DEBUG_SYNC_MSGS 1
//#define DEBUG_SYNC_SDL_EVENTS 1
#ifdef _WIN32
DEFINE_MESSAGE_MACRO(sync_message, "avsync")
#else
#define sync_message(loglevel, fmt...) message(loglevel, "avsync", fmt)
#endif
/*
* Sync thread states. General state machine looks like:
* INIT -> WAIT_SYNC -> WAIT_AUDIO -> PLAYING -> DONE -> EXIT
* PAUSE is entered when a PAUSE command is sent. PAUSE exits into
* WAIT_SYNC. EXIT is entered when a QUIT command is received.
*/
enum {
SYNC_STATE_INIT = 0,
SYNC_STATE_WAIT_SYNC = 1,
SYNC_STATE_WAIT_AUDIO = 2,
SYNC_STATE_PLAYING = 3,
SYNC_STATE_PAUSED = 4,
SYNC_STATE_DONE = 5,
SYNC_STATE_EXIT = 6,
};
#ifdef DEBUG_SYNC_STATE
const char *sync_state[] = {
"Init",
"Wait Sync",
"Wait Audio",
"Playing",
"Paused",
"Done",
"Exit"
};
#endif
/*
* 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;
while (SDL_PollEvent(&event) == 1) {
switch (event.type) {
case SDL_QUIT:
m_master_msg_queue->send_message(MSG_RECEIVED_QUIT,
NULL,
0,
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:
if (m_video_sync &&
m_video_sync->get_fullscreen() != 0) {
m_video_sync->set_fullscreen(0);
m_video_sync->do_video_resize();
}
break;
case SDLK_RETURN:
if ((event.key.keysym.mod & (KMOD_LALT | KMOD_RALT)) != 0) {
// alt-enter - full screen
if (m_video_sync &&
m_video_sync->get_fullscreen() == 0) {
m_video_sync->set_fullscreen(1);
m_video_sync->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;
default:
break;
}
}
}
/*
* 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:
state = SYNC_STATE_WAIT_SYNC;
break;
case MSG_STOP_THREAD:
state = SYNC_STATE_EXIT;
break;
case MSG_SYNC_RESIZE_SCREEN:
if (m_video_sync) {
m_video_sync->do_video_resize();
}
break;
default:
sync_message(LOG_ERR, "Sync thread received message %d",
newmsg->get_value());
break;
}
delete newmsg;
newmsg = NULL;
}
return (state);
}
/***************************************************************************
* Sync thread state handlers
***************************************************************************/
/*
* sync_thread_init - wait until all the sync routines are initialized.
*/
int CPlayerSession::sync_thread_init (void)
{
int ret = 1;
for (CPlayerMedia *mptr = m_my_media;
mptr != NULL && ret == 1;
mptr = mptr->get_next()) {
if (mptr->is_video()) {
ret = m_video_sync->initialize_video(m_session_name,
m_screen_pos_x,
m_screen_pos_y);
} else {
ret = m_audio_sync->initialize_audio(m_video_sync != NULL);
}
}
if (ret == -1) {
sync_message(LOG_CRIT, "Fatal error while initializing hardware");
if (m_video_sync != NULL) {
m_video_sync->flush_sync_buffers();
}
if (m_audio_sync != NULL) {
m_audio_sync->flush_sync_buffers();
}
m_master_msg_queue->send_message(MSG_RECEIVED_QUIT,
NULL,
0,
m_master_msg_queue_sem);
m_hardware_error = 1;
return (SYNC_STATE_DONE);
}
if (ret == 1) {
return (SYNC_STATE_WAIT_SYNC);
}
CMsg *newmsg;
newmsg = m_sync_thread_msg_queue.get_message();
if (newmsg && newmsg->get_value() == MSG_STOP_THREAD) {
return (SYNC_STATE_EXIT);
}
if (newmsg && newmsg->get_value() == MSG_PAUSE_SESSION) {
m_sync_pause_done = 1;
}
SDL_Delay(100);
return (SYNC_STATE_INIT);
}
/*
* 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...
int vsynced = 1, asynced = 1;
uint64_t astart, vstart;
if (m_video_sync) {
vsynced = m_video_sync->is_video_ready(vstart);
}
if (m_audio_sync) {
asynced = m_audio_sync->is_audio_ready(astart);
}
if (vsynced == 1 && 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 %llu", astart);
m_waiting_for_audio = 1;
state = SYNC_STATE_WAIT_AUDIO;
m_audio_sync->play_audio();
} else if (m_video_sync) {
/*
* 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 "LLU " "LLU, m_current_time, vstart);
} else {
SDL_Delay(10);
}
}
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 %llu", m_current_time);
return (SYNC_STATE_PLAYING);
}
}
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 video_status = 0;
int have_audio_eof = 0, have_video_eof = 0;
state = process_msg_queue(SYNC_STATE_PLAYING);
if (state == SYNC_STATE_PLAYING) {
get_current_time();
if (m_audio_sync) {
audio_resync_time = m_audio_sync->check_audio_sync(m_current_time,
have_audio_eof);
}
if (m_video_sync) {
video_status = m_video_sync->play_video_at(m_current_time,
have_video_eof);
}
int delay = 9;
int wait_for_signal = 0;
if (m_video_sync && m_audio_sync) {
if (have_video_eof && have_audio_eof) {
return (SYNC_STATE_DONE);
}
if (video_status > 0 || audio_resync_time != 0) {
if (audio_resync_time != 0) {
int64_t diff = audio_resync_time - m_current_time;
delay = (int)min(diff, video_status);
}
if (delay < 9) {
wait_for_signal = 0;
} else {
wait_for_signal = 1;
}
} else {
wait_for_signal = 0;
}
} else if (m_video_sync) {
if (have_video_eof == 1) {
return (SYNC_STATE_DONE);
}
if (video_status >= 9) {
wait_for_signal = 1;
delay = (int)video_status;
} else {
wait_for_signal = 0;
}
} else {
// audio only
if (have_audio_eof == 1) {
return (SYNC_STATE_DONE);
}
if (audio_resync_time != 0) {
if (audio_resync_time - m_current_time > 10) {
wait_for_signal = 1;
delay = (int)(audio_resync_time - m_current_time);
} else {
wait_for_signal = 0;
}
} else {
wait_for_signal = 1;
}
}
//player_debug_message("w %d d %d", wait_for_signal, delay);
if (wait_for_signal) {
if (delay > 9) {
delay = 9;
}
//player_debug_message("sw %d", delay);
SDL_SemWaitTimeout(m_sync_sem, delay);
}
}
return (state);
}
/*
* sync_thread_pause - wait for messages to continue or finish
*/
int CPlayerSession::sync_thread_paused (void)
{
int state;
state = process_msg_queue(SYNC_STATE_PAUSED);
if (state == SYNC_STATE_PAUSED) {
SDL_SemWaitTimeout(m_sync_sem, 10);
}
return (state);
}
/*
* sync_thread_pause - wait for messages to exit, pretty much.
*/
int CPlayerSession::sync_thread_done (void)
{
int state;
state = process_msg_queue(SYNC_STATE_DONE);
if (state == SYNC_STATE_DONE) {
SDL_SemWaitTimeout(m_sync_sem, 10);
}
return (state);
}
/*
* 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_DONE:
newstate = sync_thread_done();
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:
m_session_state = SESSION_BUFFERING;
break;
case SYNC_STATE_WAIT_AUDIO:
break;
case SYNC_STATE_PLAYING:
m_session_state = SESSION_PLAYING;
break;
case SYNC_STATE_PAUSED:
if (m_video_sync != NULL)
m_video_sync->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:
if (m_video_sync && m_video_sync->get_fullscreen() != 0) {
m_video_sync->set_fullscreen(0);
m_video_sync->do_video_resize();
}
m_master_msg_queue->send_message(MSG_SESSION_FINISHED,
NULL,
0,
m_master_msg_queue_sem);
m_session_state = SESSION_DONE;
break;
case SYNC_STATE_EXIT:
if (m_video_sync != NULL)
m_video_sync->flush_sync_buffers();
if (m_audio_sync != NULL)
m_audio_sync->flush_sync_buffers();
break;
}
}
return (state);
}
int c_sync_thread (void *data)
{
CPlayerSession *p;
int state = SYNC_STATE_INIT;
p = (CPlayerSession *)data;
do {
state = p->sync_thread(state);
} while (state != SYNC_STATE_EXIT);
return (0);
}
/* end sync.cpp */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -