Commit b853cfe7 authored by Marton Balint's avatar Marton Balint

ffplay: remove refresh thread to achieve more precise frame delay

We use a refresh loop which displays the frames and also does the polling for
pending events in a non-blocking way. If we know the required delay until the
next frame, then exactly that amount of sleeping will be done. After each
handled event we check if we have to display a frame which kind of makes
displaying the frame a high priority event.

This improves greatly the smoothness of the video output especially with 50fps
content.
Signed-off-by: 's avatarMarton Balint <cus@passwd.hu>
parent c3fb20ba
...@@ -86,6 +86,9 @@ const int program_birth_year = 2003; ...@@ -86,6 +86,9 @@ const int program_birth_year = 2003;
/* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */ /* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */
#define AUDIO_DIFF_AVG_NB 20 #define AUDIO_DIFF_AVG_NB 20
/* polls for possible required screen refresh at least this often, should be less than 1/fps */
#define REFRESH_RATE 0.01
/* NOTE: the size must be big enough to compensate the hardware audio buffersize size */ /* NOTE: the size must be big enough to compensate the hardware audio buffersize size */
/* TODO: We assume that a decoded and resampled frame fits into this buffer */ /* TODO: We assume that a decoded and resampled frame fits into this buffer */
#define SAMPLE_ARRAY_SIZE (8 * 65536) #define SAMPLE_ARRAY_SIZE (8 * 65536)
...@@ -149,7 +152,6 @@ enum { ...@@ -149,7 +152,6 @@ enum {
typedef struct VideoState { typedef struct VideoState {
SDL_Thread *read_tid; SDL_Thread *read_tid;
SDL_Thread *video_tid; SDL_Thread *video_tid;
SDL_Thread *refresh_tid;
AVInputFormat *iformat; AVInputFormat *iformat;
int no_background; int no_background;
int abort_request; int abort_request;
...@@ -210,6 +212,7 @@ typedef struct VideoState { ...@@ -210,6 +212,7 @@ typedef struct VideoState {
int rdft_bits; int rdft_bits;
FFTSample *rdft_data; FFTSample *rdft_data;
int xpos; int xpos;
double last_vis_time;
SDL_Thread *subtitle_tid; SDL_Thread *subtitle_tid;
int subtitle_stream; int subtitle_stream;
...@@ -256,7 +259,6 @@ typedef struct VideoState { ...@@ -256,7 +259,6 @@ typedef struct VideoState {
FrameBuffer *buffer_pool; FrameBuffer *buffer_pool;
#endif #endif
int refresh;
int last_video_stream, last_audio_stream, last_subtitle_stream; int last_video_stream, last_audio_stream, last_subtitle_stream;
SDL_cond *continue_read_thread; SDL_cond *continue_read_thread;
...@@ -305,7 +307,7 @@ static enum ShowMode show_mode = SHOW_MODE_NONE; ...@@ -305,7 +307,7 @@ static enum ShowMode show_mode = SHOW_MODE_NONE;
static const char *audio_codec_name; static const char *audio_codec_name;
static const char *subtitle_codec_name; static const char *subtitle_codec_name;
static const char *video_codec_name; static const char *video_codec_name;
static int rdftspeed = 20; double rdftspeed = 0.02;
static int64_t cursor_last_shown; static int64_t cursor_last_shown;
static int cursor_hidden = 0; static int cursor_hidden = 0;
#if CONFIG_AVFILTER #if CONFIG_AVFILTER
...@@ -319,7 +321,6 @@ static int64_t audio_callback_time; ...@@ -319,7 +321,6 @@ static int64_t audio_callback_time;
static AVPacket flush_pkt; static AVPacket flush_pkt;
#define FF_ALLOC_EVENT (SDL_USEREVENT) #define FF_ALLOC_EVENT (SDL_USEREVENT)
#define FF_REFRESH_EVENT (SDL_USEREVENT + 1)
#define FF_QUIT_EVENT (SDL_USEREVENT + 2) #define FF_QUIT_EVENT (SDL_USEREVENT + 2)
static SDL_Surface *screen; static SDL_Surface *screen;
...@@ -972,7 +973,6 @@ static void stream_close(VideoState *is) ...@@ -972,7 +973,6 @@ static void stream_close(VideoState *is)
/* XXX: use a special url_shutdown call to abort parse cleanly */ /* XXX: use a special url_shutdown call to abort parse cleanly */
is->abort_request = 1; is->abort_request = 1;
SDL_WaitThread(is->read_tid, NULL); SDL_WaitThread(is->read_tid, NULL);
SDL_WaitThread(is->refresh_tid, NULL);
packet_queue_destroy(&is->videoq); packet_queue_destroy(&is->videoq);
packet_queue_destroy(&is->audioq); packet_queue_destroy(&is->audioq);
packet_queue_destroy(&is->subtitleq); packet_queue_destroy(&is->subtitleq);
...@@ -1077,23 +1077,6 @@ static void video_display(VideoState *is) ...@@ -1077,23 +1077,6 @@ static void video_display(VideoState *is)
video_image_display(is); video_image_display(is);
} }
static int refresh_thread(void *opaque)
{
VideoState *is= opaque;
while (!is->abort_request) {
SDL_Event event;
event.type = FF_REFRESH_EVENT;
event.user.data1 = opaque;
if (!is->refresh && (!is->paused || is->force_refresh)) {
is->refresh = 1;
SDL_PushEvent(&event);
}
//FIXME ideally we should wait the correct time but SDLs event passing is so slow it would be silly
av_usleep(is->audio_st && is->show_mode != SHOW_MODE_VIDEO ? rdftspeed*1000 : 5000);
}
return 0;
}
/* get the current audio clock value */ /* get the current audio clock value */
static double get_audio_clock(VideoState *is) static double get_audio_clock(VideoState *is)
{ {
...@@ -1300,7 +1283,7 @@ static void update_video_pts(VideoState *is, double pts, int64_t pos, int serial ...@@ -1300,7 +1283,7 @@ static void update_video_pts(VideoState *is, double pts, int64_t pos, int serial
} }
/* called to display each frame */ /* called to display each frame */
static void video_refresh(void *opaque) static void video_refresh(void *opaque, double *remaining_time)
{ {
VideoState *is = opaque; VideoState *is = opaque;
VideoPicture *vp; VideoPicture *vp;
...@@ -1311,8 +1294,14 @@ static void video_refresh(void *opaque) ...@@ -1311,8 +1294,14 @@ static void video_refresh(void *opaque)
if (!is->paused && get_master_sync_type(is) == AV_SYNC_EXTERNAL_CLOCK && is->realtime) if (!is->paused && get_master_sync_type(is) == AV_SYNC_EXTERNAL_CLOCK && is->realtime)
check_external_clock_speed(is); check_external_clock_speed(is);
if (!display_disable && is->show_mode != SHOW_MODE_VIDEO && is->audio_st) if (!display_disable && is->show_mode != SHOW_MODE_VIDEO && is->audio_st) {
time = av_gettime() / 1000000.0;
if (is->force_refresh || is->last_vis_time + rdftspeed < time) {
video_display(is); video_display(is);
is->last_vis_time = time;
}
*remaining_time = FFMIN(*remaining_time, is->last_vis_time + rdftspeed - time);
}
if (is->video_st) { if (is->video_st) {
if (is->force_refresh) if (is->force_refresh)
...@@ -1348,8 +1337,10 @@ retry: ...@@ -1348,8 +1337,10 @@ retry:
delay = compute_target_delay(is->frame_last_duration, is); delay = compute_target_delay(is->frame_last_duration, is);
time= av_gettime()/1000000.0; time= av_gettime()/1000000.0;
if (time < is->frame_timer + delay) if (time < is->frame_timer + delay) {
*remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
return; return;
}
if (delay > 0) if (delay > 0)
is->frame_timer += delay * FFMAX(1, floor((time-is->frame_timer) / delay)); is->frame_timer += delay * FFMAX(1, floor((time-is->frame_timer) / delay));
...@@ -2667,8 +2658,6 @@ static int read_thread(void *arg) ...@@ -2667,8 +2658,6 @@ static int read_thread(void *arg)
if (is->show_mode == SHOW_MODE_NONE) if (is->show_mode == SHOW_MODE_NONE)
is->show_mode = ret >= 0 ? SHOW_MODE_VIDEO : SHOW_MODE_RDFT; is->show_mode = ret >= 0 ? SHOW_MODE_VIDEO : SHOW_MODE_RDFT;
is->refresh_tid = SDL_CreateThread(refresh_thread, is);
if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) { if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) {
stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]); stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]);
} }
...@@ -2956,6 +2945,24 @@ static void toggle_audio_display(VideoState *is) ...@@ -2956,6 +2945,24 @@ static void toggle_audio_display(VideoState *is)
bgcolor, 1); bgcolor, 1);
} }
static void refresh_loop_wait_event(VideoState *is, SDL_Event *event) {
int got_event;
double remaining_time;
do {
if (!cursor_hidden && av_gettime() - cursor_last_shown > CURSOR_HIDE_DELAY) {
SDL_ShowCursor(0);
cursor_hidden = 1;
}
remaining_time = REFRESH_RATE;
if (is->show_mode != SHOW_MODE_NONE && (!is->paused || is->force_refresh))
video_refresh(is, &remaining_time);
SDL_PumpEvents();
got_event = SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS);
if (!got_event)
av_usleep((int64_t)(remaining_time * 1000000.0));
} while (!got_event);
}
/* handle an event sent by the GUI */ /* handle an event sent by the GUI */
static void event_loop(VideoState *cur_stream) static void event_loop(VideoState *cur_stream)
{ {
...@@ -2964,7 +2971,7 @@ static void event_loop(VideoState *cur_stream) ...@@ -2964,7 +2971,7 @@ static void event_loop(VideoState *cur_stream)
for (;;) { for (;;) {
double x; double x;
SDL_WaitEvent(&event); refresh_loop_wait_event(cur_stream, &event);
switch (event.type) { switch (event.type) {
case SDL_KEYDOWN: case SDL_KEYDOWN:
if (exit_on_keydown) { if (exit_on_keydown) {
...@@ -3102,14 +3109,6 @@ static void event_loop(VideoState *cur_stream) ...@@ -3102,14 +3109,6 @@ static void event_loop(VideoState *cur_stream)
case FF_ALLOC_EVENT: case FF_ALLOC_EVENT:
alloc_picture(event.user.data1); alloc_picture(event.user.data1);
break; break;
case FF_REFRESH_EVENT:
if (!cursor_hidden && av_gettime() - cursor_last_shown > CURSOR_HIDE_DELAY) {
SDL_ShowCursor(0);
cursor_hidden = 1;
}
video_refresh(event.user.data1);
cur_stream->refresh = 0;
break;
default: default:
break; break;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment