Auto-rotate videos.
authorCarl Hetherington <cth@carlh.net>
Fri, 8 Jun 2018 16:07:24 +0000 (17:07 +0100)
committerCarl Hetherington <cth@carlh.net>
Fri, 8 Jun 2018 16:07:24 +0000 (17:07 +0100)
ChangeLog
src/lib/ffmpeg_content.cc
src/lib/ffmpeg_examiner.cc
src/lib/ffmpeg_examiner.h
src/lib/filter.cc

index abe807c276bbf65a93b0569b02a7c7e3dd85f2f1..842ac5a2afe68af1041cbff0652113868f725fd8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,7 @@
 2018-06-08  Carl Hetherington  <cth@carlh.net>
 
-       * Add support for vertical and horizontal flipping of video
-       sources.
+       * Add support for rotation and flipping of video sources,
+       and auto-detect when this is necessary (#966).
 
 2018-06-05  Carl Hetherington  <cth@carlh.net>
 
index 2233f5f8ca24969c5949c58bda0283c1a9468c10..cbeebd8178f0ba7c1e3f84a93b3230ae03e44f27 100644 (file)
@@ -274,6 +274,18 @@ FFmpegContent::examine (shared_ptr<Job> job)
                        _color_trc = examiner->color_trc ();
                        _colorspace = examiner->colorspace ();
                        _bits_per_pixel = examiner->bits_per_pixel ();
+
+                       if (examiner->rotation()) {
+                               double rot = *examiner->rotation ();
+                               if (fabs (rot - 180) < 1.0) {
+                                       _filters.push_back (Filter::from_id ("vflip"));
+                                       _filters.push_back (Filter::from_id ("hflip"));
+                               } else if (fabs (rot - 90) < 1.0) {
+                                       _filters.push_back (Filter::from_id ("90clock"));
+                               } else if (fabs (rot - 270) < 1.0) {
+                                       _filters.push_back (Filter::from_id ("90anticlock"));
+                               }
+                       }
                }
 
                if (!examiner->audio_streams().empty ()) {
index 4223a4badaa80713c8fa28db1b0fec9f21b6f3d7..4205d3d23532c676aaea5873af3c60790fee98cf 100644 (file)
@@ -23,6 +23,8 @@ extern "C" {
 #include <libavformat/avformat.h>
 #include <libavutil/pixfmt.h>
 #include <libavutil/pixdesc.h>
+#include <libavutil/eval.h>
+#include <libavutil/display.h>
 }
 #include "ffmpeg_examiner.h"
 #include "ffmpeg_content.h"
@@ -139,6 +141,31 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c, shared_ptr<Jo
                        break;
                }
        }
+
+
+       if (_video_stream) {
+               /* This code taken from get_rotation() in ffmpeg:cmdutils.c */
+               AVStream* stream = _format_context->streams[*_video_stream];
+               AVDictionaryEntry* rotate_tag = av_dict_get (stream->metadata, "rotate", 0, 0);
+               uint8_t* displaymatrix = av_stream_get_side_data (stream, AV_PKT_DATA_DISPLAYMATRIX, 0);
+               _rotation = 0;
+
+               if (rotate_tag && *rotate_tag->value && strcmp(rotate_tag->value, "0")) {
+                       char *tail;
+                       _rotation = av_strtod (rotate_tag->value, &tail);
+                       if (*tail) {
+                               _rotation = 0;
+                       }
+               }
+
+               if (displaymatrix && !_rotation) {
+                       _rotation = - av_display_rotation_get ((int32_t*) displaymatrix);
+               }
+
+               _rotation = *_rotation - 360 * floor (*_rotation / 360 + 0.9 / 360);
+
+               DCPOMATIC_ASSERT (fabs (*_rotation - 90 * round (*_rotation / 90)) < 2);
+       }
 }
 
 void
index 5cd70d979cd6722f22fdebc3a22bef7f28a64c12..67fdcfae0baa65986a407f2adfd2b237828875da 100644 (file)
@@ -71,6 +71,10 @@ public:
 
        boost::optional<int> bits_per_pixel () const;
 
+       boost::optional<double> rotation () const {
+               return _rotation;
+       }
+
 private:
        void video_packet (AVCodecContext *);
        void audio_packet (AVCodecContext *, boost::shared_ptr<FFmpegAudioStream>);
@@ -88,6 +92,8 @@ private:
        Frame _video_length;
        bool _need_video_length;
 
+       boost::optional<double> _rotation;
+
        struct SubtitleStart
        {
                SubtitleStart (std::string id_, bool image_, ContentTime time_)
index 626d35f23e9b1535b2cd2948e79ed3d48812f99e..2b1447a28f1e189474c2c2832933f0ab3f402158 100644 (file)
@@ -27,6 +27,7 @@ extern "C" {
 #include <libavfilter/avfilter.h>
 }
 #include <boost/foreach.hpp>
+#include <iostream>
 
 #include "i18n.h"
 
@@ -63,23 +64,31 @@ Filter::setup_filters ()
 {
        /* Note: "none" is a magic id name, so don't use it here */
 
-       maybe_add (N_("vflip"),     _("Vertical flip"),                    _("Orientation"),     N_("vflip"));
-       maybe_add (N_("hflip"),     _("Horizontal flip"),                  _("Orientation"),     N_("hflip"));
-       maybe_add (N_("mcdeint"),   _("Motion compensating deinterlacer"), _("De-interlacing"),  N_("mcdeint"));
-       maybe_add (N_("kerndeint"), _("Kernel deinterlacer"),              _("De-interlacing"),  N_("kerndeint"));
-       maybe_add (N_("yadif"),     _("Yet Another Deinterlacing Filter"), _("De-interlacing"),  N_("yadif"));
-       maybe_add (N_("gradfun"),   _("Gradient debander"),                _("Misc"),            N_("gradfun"));
-       maybe_add (N_("unsharp"),   _("Unsharp mask and Gaussian blur"),   _("Misc"),            N_("unsharp"));
-       maybe_add (N_("denoise3d"), _("3D denoiser"),                      _("Noise reduction"), N_("denoise3d"));
-       maybe_add (N_("hqdn3d"),    _("High quality 3D denoiser"),         _("Noise reduction"), N_("hqdn3d"));
-       maybe_add (N_("telecine"),  _("Telecine filter"),                  _("Misc"),            N_("telecine"));
-       maybe_add (N_("ow"),        _("Overcomplete wavelet denoiser"),    _("Noise reduction"), N_("mp=ow"));
+       maybe_add (N_("vflip"),       _("Vertical flip"),                    _("Orientation"),     N_("vflip"));
+       maybe_add (N_("hflip"),       _("Horizontal flip"),                  _("Orientation"),     N_("hflip"));
+       maybe_add (N_("90clock"),     _("Rotate 90 degrees clockwise"),      _("Orientation"),     N_("transpose=dir=clock"));
+       maybe_add (N_("90anticlock"), _("Rotate 90 degrees anti-clockwise"), _("Orientation"),     N_("transpose=dir=cclock"));
+       maybe_add (N_("mcdeint"),     _("Motion compensating deinterlacer"), _("De-interlacing"),  N_("mcdeint"));
+       maybe_add (N_("kerndeint"),   _("Kernel deinterlacer"),              _("De-interlacing"),  N_("kerndeint"));
+       maybe_add (N_("yadif"),       _("Yet Another Deinterlacing Filter"), _("De-interlacing"),  N_("yadif"));
+       maybe_add (N_("gradfun"),     _("Gradient debander"),                _("Misc"),            N_("gradfun"));
+       maybe_add (N_("unsharp"),     _("Unsharp mask and Gaussian blur"),   _("Misc"),            N_("unsharp"));
+       maybe_add (N_("denoise3d"),   _("3D denoiser"),                      _("Noise reduction"), N_("denoise3d"));
+       maybe_add (N_("hqdn3d"),      _("High quality 3D denoiser"),         _("Noise reduction"), N_("hqdn3d"));
+       maybe_add (N_("telecine"),    _("Telecine filter"),                  _("Misc"),            N_("telecine"));
+       maybe_add (N_("ow"),          _("Overcomplete wavelet denoiser"),    _("Noise reduction"), N_("mp=ow"));
 }
 
 void
 Filter::maybe_add (string i, string n, string c, string f)
 {
-       if (avfilter_get_by_name (i.c_str())) {
+       string check_name = f;
+       size_t end = check_name.find("=");
+       if (end != string::npos) {
+               check_name = check_name.substr (0, end);
+       }
+
+       if (avfilter_get_by_name (check_name.c_str())) {
                _filters.push_back (new Filter (i, n, c, f));
        }
 }