#define SDL_AUDIO_BUFFER_SIZE 1024
/* no AV sync correction is done if below the minimum AV sync threshold */
-#define AV_SYNC_THRESHOLD_MIN 0.01
+#define AV_SYNC_THRESHOLD_MIN 0.04
/* AV sync correction is done if above the maximum AV sync threshold */
#define AV_SYNC_THRESHOLD_MAX 0.1
/* If a frame duration is longer than this, it will not be duplicated to compensate AV sync */
int step;
#if CONFIG_AVFILTER
+ int vfilter_idx;
AVFilterContext *in_video_filter; // the first filter in the video chain
AVFilterContext *out_video_filter; // the last filter in the video chain
AVFilterContext *in_audio_filter; // the first filter in the audio chain
static int64_t cursor_last_shown;
static int cursor_hidden = 0;
#if CONFIG_AVFILTER
-static char *vfilters = NULL;
+static const char **vfilters_list = NULL;
+static int nb_vfilters = 0;
static char *afilters = NULL;
#endif
+static int autorotate = 1;
/* current context */
static int is_full_screen;
static SDL_Surface *screen;
+#if CONFIG_AVFILTER
+static int opt_add_vfilter(void *optctx, const char *opt, const char *arg)
+{
+ GROW_ARRAY(vfilters_list, nb_vfilters);
+ vfilters_list[nb_vfilters - 1] = arg;
+ return 0;
+}
+#endif
+
static inline
int cmp_audio_fmts(enum AVSampleFormat fmt1, int64_t channel_count1,
enum AVSampleFormat fmt2, int64_t channel_count2)
/* to be more precise, we take into account the time spent since
the last buffer computation */
if (audio_callback_time) {
- time_diff = av_gettime() - audio_callback_time;
+ time_diff = av_gettime_relative() - audio_callback_time;
delay -= (time_diff * s->audio_tgt.freq) / 1000000;
}
av_lockmgr_register(NULL);
uninit_opts();
#if CONFIG_AVFILTER
- av_freep(&vfilters);
+ av_freep(&vfilters_list);
#endif
avformat_network_deinit();
if (show_status)
if (c->paused) {
return c->pts;
} else {
- double time = av_gettime() / 1000000.0;
+ double time = av_gettime_relative() / 1000000.0;
return c->pts_drift + time - (time - c->last_updated) * (1.0 - c->speed);
}
}
static void set_clock(Clock *c, double pts, int serial)
{
- double time = av_gettime() / 1000000.0;
+ double time = av_gettime_relative() / 1000000.0;
set_clock_at(c, pts, serial, time);
}
static void stream_toggle_pause(VideoState *is)
{
if (is->paused) {
- is->frame_timer += av_gettime() / 1000000.0 + is->vidclk.pts_drift - is->vidclk.pts;
+ is->frame_timer += av_gettime_relative() / 1000000.0 + is->vidclk.pts_drift - is->vidclk.pts;
if (is->read_pause_return != AVERROR(ENOSYS)) {
is->vidclk.paused = 0;
}
check_external_clock_speed(is);
if (!display_disable && is->show_mode != SHOW_MODE_VIDEO && is->audio_st) {
- time = av_gettime() / 1000000.0;
+ time = av_gettime_relative() / 1000000.0;
if (is->force_refresh || is->last_vis_time + rdftspeed < time) {
video_display(is);
is->last_vis_time = time;
}
if (lastvp->serial != vp->serial && !redisplay)
- is->frame_timer = av_gettime() / 1000000.0;
+ is->frame_timer = av_gettime_relative() / 1000000.0;
if (is->paused)
goto display;
else
delay = compute_target_delay(last_duration, is);
- time= av_gettime()/1000000.0;
+ time= av_gettime_relative()/1000000.0;
if (time < is->frame_timer + delay && !redisplay) {
*remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
return;
int aqsize, vqsize, sqsize;
double av_diff;
- cur_time = av_gettime();
+ cur_time = av_gettime_relative();
if (!last_time || (cur_time - last_time) >= 30000) {
aqsize = 0;
vqsize = 0;
char sws_flags_str[128];
char buffersrc_args[256];
int ret;
- AVFilterContext *filt_src = NULL, *filt_out = NULL, *filt_crop;
+ AVFilterContext *filt_src = NULL, *filt_out = NULL, *last_filter = NULL;
AVCodecContext *codec = is->video_st->codec;
AVRational fr = av_guess_frame_rate(is->ic, is->video_st, NULL);
if ((ret = av_opt_set_int_list(filt_out, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0)
goto fail;
+ last_filter = filt_out;
+
+/* Note: this macro adds a filter before the lastly added filter, so the
+ * processing order of the filters is in reverse */
+#define INSERT_FILT(name, arg) do { \
+ AVFilterContext *filt_ctx; \
+ \
+ ret = avfilter_graph_create_filter(&filt_ctx, \
+ avfilter_get_by_name(name), \
+ "ffplay_" name, arg, NULL, graph); \
+ if (ret < 0) \
+ goto fail; \
+ \
+ ret = avfilter_link(filt_ctx, 0, last_filter, 0); \
+ if (ret < 0) \
+ goto fail; \
+ \
+ last_filter = filt_ctx; \
+} while (0)
+
/* SDL YUV code is not handling odd width/height for some driver
* combinations, therefore we crop the picture to an even width/height. */
- if ((ret = avfilter_graph_create_filter(&filt_crop,
- avfilter_get_by_name("crop"),
- "ffplay_crop", "floor(in_w/2)*2:floor(in_h/2)*2", NULL, graph)) < 0)
- goto fail;
- if ((ret = avfilter_link(filt_crop, 0, filt_out, 0)) < 0)
- goto fail;
+ INSERT_FILT("crop", "floor(in_w/2)*2:floor(in_h/2)*2");
+
+ if (autorotate) {
+ AVDictionaryEntry *rotate_tag = av_dict_get(is->video_st->metadata, "rotate", NULL, 0);
+ if (rotate_tag && *rotate_tag->value && strcmp(rotate_tag->value, "0")) {
+ if (!strcmp(rotate_tag->value, "90")) {
+ INSERT_FILT("transpose", "clock");
+ } else if (!strcmp(rotate_tag->value, "180")) {
+ INSERT_FILT("hflip", NULL);
+ INSERT_FILT("vflip", NULL);
+ } else if (!strcmp(rotate_tag->value, "270")) {
+ INSERT_FILT("transpose", "cclock");
+ } else {
+ char rotate_buf[64];
+ snprintf(rotate_buf, sizeof(rotate_buf), "%s*PI/180", rotate_tag->value);
+ INSERT_FILT("rotate", rotate_buf);
+ }
+ }
+ }
- if ((ret = configure_filtergraph(graph, vfilters, filt_src, filt_crop)) < 0)
+ if ((ret = configure_filtergraph(graph, vfilters, filt_src, last_filter)) < 0)
goto fail;
is->in_video_filter = filt_src;
int last_h = 0;
enum AVPixelFormat last_format = -2;
int last_serial = -1;
+ int last_vfilter_idx = 0;
#endif
for (;;) {
if ( last_w != frame->width
|| last_h != frame->height
|| last_format != frame->format
- || last_serial != serial) {
+ || last_serial != serial
+ || last_vfilter_idx != is->vfilter_idx) {
av_log(NULL, AV_LOG_DEBUG,
"Video frame changed from size:%dx%d format:%s serial:%d to size:%dx%d format:%s serial:%d\n",
last_w, last_h,
(const char *)av_x_if_null(av_get_pix_fmt_name(frame->format), "none"), serial);
avfilter_graph_free(&graph);
graph = avfilter_graph_alloc();
- if ((ret = configure_video_filters(graph, is, vfilters, frame)) < 0) {
+ if ((ret = configure_video_filters(graph, is, vfilters_list ? vfilters_list[is->vfilter_idx] : NULL, frame)) < 0) {
SDL_Event event;
event.type = FF_QUIT_EVENT;
event.user.data1 = is;
last_h = frame->height;
last_format = frame->format;
last_serial = serial;
+ last_vfilter_idx = is->vfilter_idx;
frame_rate = filt_out->inputs[0]->frame_rate;
}
goto the_end;
while (ret >= 0) {
- is->frame_last_returned_time = av_gettime() / 1000000.0;
+ is->frame_last_returned_time = av_gettime_relative() / 1000000.0;
ret = av_buffersink_get_frame_flags(filt_out, frame, 0);
if (ret < 0) {
break;
}
- is->frame_last_filter_delay = av_gettime() / 1000000.0 - is->frame_last_returned_time;
+ is->frame_last_filter_delay = av_gettime_relative() / 1000000.0 - is->frame_last_returned_time;
if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0)
is->frame_last_filter_delay = 0;
tb = filt_out->inputs[0]->time_base;
VideoState *is = opaque;
int audio_size, len1;
- audio_callback_time = av_gettime();
+ audio_callback_time = av_gettime_relative();
while (len > 0) {
if (is->audio_buf_index >= is->audio_buf_size) {
SDL_AudioSpec wanted_spec, spec;
const char *env;
static const int next_nb_channels[] = {0, 0, 1, 6, 2, 6, 4, 6};
+ static const int next_sample_rates[] = {0, 44100, 48000, 96000, 192000};
+ int next_sample_rate_idx = FF_ARRAY_ELEMS(next_sample_rates) - 1;
env = SDL_getenv("SDL_AUDIO_CHANNELS");
if (env) {
wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
}
- wanted_spec.channels = av_get_channel_layout_nb_channels(wanted_channel_layout);
+ wanted_nb_channels = av_get_channel_layout_nb_channels(wanted_channel_layout);
+ wanted_spec.channels = wanted_nb_channels;
wanted_spec.freq = wanted_sample_rate;
if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) {
av_log(NULL, AV_LOG_ERROR, "Invalid sample rate or channel count!\n");
return -1;
}
+ while (next_sample_rate_idx && next_sample_rates[next_sample_rate_idx] >= wanted_spec.freq)
+ next_sample_rate_idx--;
wanted_spec.format = AUDIO_S16SYS;
wanted_spec.silence = 0;
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
wanted_spec.callback = sdl_audio_callback;
wanted_spec.userdata = opaque;
while (SDL_OpenAudio(&wanted_spec, &spec) < 0) {
- av_log(NULL, AV_LOG_WARNING, "SDL_OpenAudio (%d channels): %s\n", wanted_spec.channels, SDL_GetError());
+ av_log(NULL, AV_LOG_WARNING, "SDL_OpenAudio (%d channels, %d Hz): %s\n",
+ wanted_spec.channels, wanted_spec.freq, SDL_GetError());
wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
if (!wanted_spec.channels) {
- av_log(NULL, AV_LOG_ERROR,
- "No more channel combinations to try, audio open failed\n");
- return -1;
+ wanted_spec.freq = next_sample_rates[next_sample_rate_idx--];
+ wanted_spec.channels = wanted_nb_channels;
+ if (!wanted_spec.freq) {
+ av_log(NULL, AV_LOG_ERROR,
+ "No more combinations to try, audio open failed\n");
+ return -1;
+ }
}
wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels);
}
if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {
AVStream *st = ic->streams[st_index[AVMEDIA_TYPE_VIDEO]];
AVCodecContext *avctx = st->codec;
- VideoPicture vp = {.width = avctx->width, .height = avctx->height, .sar = av_guess_sample_aspect_ratio(ic, st, NULL)};
+ VideoPicture vp = {0};
+ vp.width = avctx->width;
+ vp.height = avctx->height;
+ vp.sar = av_guess_sample_aspect_ratio(ic, st, NULL);
if (vp.width)
set_default_window_size(&vp);
}
double remaining_time = 0.0;
SDL_PumpEvents();
while (!SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) {
- if (!cursor_hidden && av_gettime() - cursor_last_shown > CURSOR_HIDE_DELAY) {
+ if (!cursor_hidden && av_gettime_relative() - cursor_last_shown > CURSOR_HIDE_DELAY) {
SDL_ShowCursor(0);
cursor_hidden = 1;
}
stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE);
break;
case SDLK_w:
+#if CONFIG_AVFILTER
+ if (cur_stream->show_mode == SHOW_MODE_VIDEO && cur_stream->vfilter_idx < nb_vfilters - 1) {
+ if (++cur_stream->vfilter_idx >= nb_vfilters)
+ cur_stream->vfilter_idx = 0;
+ } else {
+ cur_stream->vfilter_idx = 0;
+ toggle_audio_display(cur_stream);
+ }
+#else
toggle_audio_display(cur_stream);
+#endif
break;
case SDLK_PAGEUP:
if (cur_stream->ic->nb_chapters <= 1) {
SDL_ShowCursor(1);
cursor_hidden = 0;
}
- cursor_last_shown = av_gettime();
+ cursor_last_shown = av_gettime_relative();
if (event.type == SDL_MOUSEBUTTONDOWN) {
x = event.button.x;
} else {
{ "infbuf", OPT_BOOL | OPT_EXPERT, { &infinite_buffer }, "don't limit the input buffer size (useful with realtime streams)", "" },
{ "window_title", OPT_STRING | HAS_ARG, { &window_title }, "set window title", "window title" },
#if CONFIG_AVFILTER
- { "vf", OPT_STRING | HAS_ARG, { &vfilters }, "set video filters", "filter_graph" },
+ { "vf", OPT_EXPERT | HAS_ARG, { .func_arg = opt_add_vfilter }, "set video filters", "filter_graph" },
{ "af", OPT_STRING | HAS_ARG, { &afilters }, "set audio filters", "filter_graph" },
#endif
{ "rdftspeed", OPT_INT | HAS_ARG| OPT_AUDIO | OPT_EXPERT, { &rdftspeed }, "rdft speed", "msecs" },
{ "acodec", HAS_ARG | OPT_STRING | OPT_EXPERT, { &audio_codec_name }, "force audio decoder", "decoder_name" },
{ "scodec", HAS_ARG | OPT_STRING | OPT_EXPERT, { &subtitle_codec_name }, "force subtitle decoder", "decoder_name" },
{ "vcodec", HAS_ARG | OPT_STRING | OPT_EXPERT, { &video_codec_name }, "force video decoder", "decoder_name" },
+ { "autorotate", OPT_BOOL, { &autorotate }, "automatically rotate video", "" },
{ NULL, },
};
"v cycle video channel\n"
"t cycle subtitle channel in the current program\n"
"c cycle program\n"
- "w show audio waves\n"
+ "w cycle video filters or show modes\n"
"s activate frame-step mode\n"
"left/right seek backward/forward 10 seconds\n"
"down/up seek backward/forward 1 minute\n"
flags &= ~SDL_INIT_AUDIO;
if (display_disable)
SDL_putenv(dummy_videodriver); /* For the event queue, we always need a video driver. */
-#if !defined(__MINGW32__) && !defined(__APPLE__)
+#if !defined(_WIN32) && !defined(__APPLE__)
flags |= SDL_INIT_EVENTTHREAD; /* Not supported on Windows or Mac OS X */
#endif
if (SDL_Init (flags)) {