diff --git a/Changelog b/Changelog index 02dd77b235..623d10ade1 100644 --- a/Changelog +++ b/Changelog @@ -18,6 +18,7 @@ version : - nvenc level and tier options - chorus filter - Canopus HQ/HQA decoder +- Automatically rotate videos based on metadata in ffmpeg version 2.6: diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index c66cb16d64..7cd81b080a 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -462,6 +462,9 @@ Technical note -- attachments are implemented as codec extradata, so this option can actually be used to extract extradata from any stream, not just attachments. +@item -noautorotate +Disable automatically rotating video based on file metadata. + @end table @section Video Options diff --git a/ffmpeg.c b/ffmpeg.c index 7e5b16d0ec..8c288ae366 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -3647,6 +3647,8 @@ static int process_input(int file_index) if (av_packet_get_side_data(&pkt, src_sd->type, NULL)) continue; + if (ist->autorotate && src_sd->type == AV_PKT_DATA_DISPLAYMATRIX) + continue; dst_data = av_packet_new_side_data(&pkt, src_sd->type, src_sd->size); if (!dst_data) diff --git a/ffmpeg.h b/ffmpeg.h index 71061daff6..800bd0ab0e 100644 --- a/ffmpeg.h +++ b/ffmpeg.h @@ -122,6 +122,8 @@ typedef struct OptionsContext { int nb_hwaccels; SpecifierOpt *hwaccel_devices; int nb_hwaccel_devices; + SpecifierOpt *autorotate; + int nb_autorotate; /* output options */ StreamMap *stream_maps; @@ -276,6 +278,7 @@ typedef struct InputStream { int top_field_first; int guess_layout_max; + int autorotate; int resample_height; int resample_width; int resample_pix_fmt; diff --git a/ffmpeg_filter.c b/ffmpeg_filter.c index 264840b2e9..6a891d3d10 100644 --- a/ffmpeg_filter.c +++ b/ffmpeg_filter.c @@ -31,6 +31,7 @@ #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/channel_layout.h" +#include "libavutil/display.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" @@ -339,6 +340,28 @@ static int insert_trim(int64_t start_time, int64_t duration, return 0; } +static int insert_filter(AVFilterContext **last_filter, int *pad_idx, + const char *filter_name, const char *args) +{ + AVFilterGraph *graph = (*last_filter)->graph; + AVFilterContext *ctx; + int ret; + + ret = avfilter_graph_create_filter(&ctx, + avfilter_get_by_name(filter_name), + filter_name, args, NULL, graph); + if (ret < 0) + return ret; + + ret = avfilter_link(*last_filter, *pad_idx, ctx, 0); + if (ret < 0) + return ret; + + *last_filter = ctx; + *pad_idx = 0; + return 0; +} + static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) { char *pix_fmts; @@ -676,6 +699,26 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, return ret; last_filter = ifilter->filter; + if (ist->autorotate) { + uint8_t* displaymatrix = av_stream_get_side_data(ist->st, + AV_PKT_DATA_DISPLAYMATRIX, NULL); + if (displaymatrix) { + double rot = av_display_rotation_get((int32_t*) displaymatrix); + if (rot < -135 || rot > 135) { + ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL); + if (ret < 0) + return ret; + ret = insert_filter(&last_filter, &pad_idx, "hflip", NULL); + } else if (rot < -45) { + ret = insert_filter(&last_filter, &pad_idx, "transpose", "dir=clock"); + } else if (rot > 45) { + ret = insert_filter(&last_filter, &pad_idx, "transpose", "dir=cclock"); + } + if (ret < 0) + return ret; + } + } + if (ist->framerate.num) { AVFilterContext *setpts; diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c index 75c80e792e..f59b93bba6 100644 --- a/ffmpeg_opt.c +++ b/ffmpeg_opt.c @@ -602,6 +602,9 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) ist->ts_scale = 1.0; MATCH_PER_STREAM_OPT(ts_scale, dbl, ist->ts_scale, ic, st); + ist->autorotate = 1; + MATCH_PER_STREAM_OPT(autorotate, i, ist->autorotate, ic, st); + MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, ic, st); if (codec_tag) { uint32_t tag = strtol(codec_tag, &next, 0); @@ -3069,6 +3072,9 @@ const OptionDef options[] = { #if HAVE_VDPAU_X11 { "vdpau_api_ver", HAS_ARG | OPT_INT | OPT_EXPERT, { &vdpau_api_ver }, "" }, #endif + { "autorotate", HAS_ARG | OPT_BOOL | OPT_SPEC | + OPT_EXPERT | OPT_INPUT, { .off = OFFSET(autorotate) }, + "automatically insert correct rotate filters" }, /* audio options */ { "aframes", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_frames },