Logging improvements to allow prettier displays in the server GUI.
[dcpomatic.git] / src / lib / ffmpeg_content.cc
index eab11023618e182b20d71a9357ff9580fe8886f0..2c2d36a986c6eb3b725b40c9222e38d5f9e043d6 100644 (file)
 #include <libcxml/cxml.h>
 extern "C" {
 #include <libavformat/avformat.h>
+#include <libavutil/pixdesc.h>
 }
+#include <libxml++/libxml++.h>
 #include <boost/foreach.hpp>
+#include <iostream>
 
 #include "i18n.h"
 
-#define LOG_GENERAL(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+#define LOG_GENERAL(...) film->log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
 
 using std::string;
 using std::vector;
 using std::list;
 using std::cout;
 using std::pair;
+using std::make_pair;
 using boost::shared_ptr;
 using boost::dynamic_pointer_cast;
+using boost::optional;
 
 int const FFmpegContentProperty::SUBTITLE_STREAMS = 100;
 int const FFmpegContentProperty::SUBTITLE_STREAM = 101;
-int const FFmpegContentProperty::AUDIO_STREAMS = 102;
-int const FFmpegContentProperty::FILTERS = 103;
-
-FFmpegContent::FFmpegContent (shared_ptr<const Film> f, boost::filesystem::path p)
-       : Content (f, p)
-       , VideoContent (f, p)
-       , AudioContent (f, p)
-       , SubtitleContent (f, p)
+int const FFmpegContentProperty::FILTERS = 102;
+
+FFmpegContent::FFmpegContent (shared_ptr<const Film> film, boost::filesystem::path p)
+       : Content (film, p)
+       , VideoContent (film, p)
+       , AudioContent (film, p)
+       , SubtitleContent (film, p)
 {
 
 }
 
-FFmpegContent::FFmpegContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version, list<string>& notes)
-       : Content (f, node)
-       , VideoContent (f, node, version)
-       , AudioContent (f, node)
-       , SubtitleContent (f, node, version)
+FFmpegContent::FFmpegContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, int version, list<string>& notes)
+       : Content (film, node)
+       , VideoContent (film, node, version)
+       , AudioContent (film, node)
+       , SubtitleContent (film, node, version)
 {
        list<cxml::NodePtr> c = node->node_children ("SubtitleStream");
        for (list<cxml::NodePtr>::const_iterator i = c.begin(); i != c.end(); ++i) {
@@ -80,6 +84,10 @@ FFmpegContent::FFmpegContent (shared_ptr<const Film> f, cxml::ConstNodePtr node,
        c = node->node_children ("AudioStream");
        for (list<cxml::NodePtr>::const_iterator i = c.begin(); i != c.end(); ++i) {
                _audio_streams.push_back (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream (*i, version)));
+               if (version < 11 && !(*i)->optional_node_child ("Selected")) {
+                       /* This is an old file and this stream is not selected, so un-map it */
+                       _audio_streams.back()->set_mapping (AudioMapping (_audio_streams.back()->channels (), MAX_DCP_AUDIO_CHANNELS));
+               }
        }
 
        c = node->node_children ("Filter");
@@ -92,14 +100,26 @@ FFmpegContent::FFmpegContent (shared_ptr<const Film> f, cxml::ConstNodePtr node,
                }
        }
 
-       _first_video = node->optional_number_child<double> ("FirstVideo");
+       optional<ContentTime::Type> const f = node->optional_number_child<ContentTime::Type> ("FirstVideo");
+       if (f) {
+               _first_video = ContentTime (f.get ());
+       }
+
+       _color_range = static_cast<AVColorRange> (node->optional_number_child<int>("ColorRange").get_value_or (AVCOL_RANGE_UNSPECIFIED));
+       _color_primaries = static_cast<AVColorPrimaries> (node->optional_number_child<int>("ColorPrimaries").get_value_or (AVCOL_PRI_UNSPECIFIED));
+       _color_trc = static_cast<AVColorTransferCharacteristic> (
+               node->optional_number_child<int>("ColorTransferCharacteristic").get_value_or (AVCOL_TRC_UNSPECIFIED)
+               );
+       _colorspace = static_cast<AVColorSpace> (node->optional_number_child<int>("Colorspace").get_value_or (AVCOL_SPC_UNSPECIFIED));
+       _bits_per_pixel = node->optional_number_child<int> ("BitsPerPixel");
+
 }
 
-FFmpegContent::FFmpegContent (shared_ptr<const Film> f, vector<boost::shared_ptr<Content> > c)
-       : Content (f, c)
-       , VideoContent (f, c)
-       , AudioContent (f, c)
-       , SubtitleContent (f, c)
+FFmpegContent::FFmpegContent (shared_ptr<const Film> film, vector<boost::shared_ptr<Content> > c)
+       : Content (film, c)
+       , VideoContent (film, c)
+       , AudioContent (film, c)
+       , SubtitleContent (film, c)
 {
        shared_ptr<FFmpegContent> ref = dynamic_pointer_cast<FFmpegContent> (c[0]);
        DCPOMATIC_ASSERT (ref);
@@ -147,6 +167,14 @@ FFmpegContent::as_xml (xmlpp::Node* node) const
        if (_first_video) {
                node->add_child("FirstVideo")->add_child_text (raw_convert<string> (_first_video.get().get()));
        }
+
+       node->add_child("ColorRange")->add_child_text (raw_convert<string> (_color_range));
+       node->add_child("ColorPrimaries")->add_child_text (raw_convert<string> (_color_primaries));
+       node->add_child("ColorTransferCharacteristic")->add_child_text (raw_convert<string> (_color_trc));
+       node->add_child("Colorspace")->add_child_text (raw_convert<string> (_colorspace));
+       if (_bits_per_pixel) {
+               node->add_child("BitsPerPixel")->add_child_text (raw_convert<string> (_bits_per_pixel.get ()));
+       }
 }
 
 void
@@ -169,21 +197,27 @@ FFmpegContent::examine (shared_ptr<Job> job)
                if (!_subtitle_streams.empty ()) {
                        _subtitle_stream = _subtitle_streams.front ();
                }
-               
+
                _audio_streams = examiner->audio_streams ();
 
                if (!_audio_streams.empty ()) {
                        AudioMapping m = _audio_streams.front()->mapping ();
-                       m.make_default ();
+                       film->make_audio_mapping_default (m);
                        _audio_streams.front()->set_mapping (m);
                }
 
                _first_video = examiner->first_video ();
+
+               _color_range = examiner->color_range ();
+               _color_primaries = examiner->color_primaries ();
+               _color_trc = examiner->color_trc ();
+               _colorspace = examiner->colorspace ();
+               _bits_per_pixel = examiner->bits_per_pixel ();
        }
 
        signal_changed (FFmpegContentProperty::SUBTITLE_STREAMS);
        signal_changed (FFmpegContentProperty::SUBTITLE_STREAM);
-       signal_changed (FFmpegContentProperty::AUDIO_STREAMS);
+       signal_changed (AudioContentProperty::AUDIO_STREAMS);
 }
 
 string
@@ -211,7 +245,7 @@ FFmpegContent::technical_summary () const
        }
 
        string filt = Filter::ffmpeg_string (_filters);
-       
+
        return Content::technical_summary() + " - "
                + VideoContent::technical_summary() + " - "
                + AudioContent::technical_summary() + " - "
@@ -249,7 +283,7 @@ FFmpegContent::full_length () const
        shared_ptr<const Film> film = _film.lock ();
        DCPOMATIC_ASSERT (film);
        FrameRateChange const frc (video_frame_rate (), film->video_frame_rate ());
-       return DCPTime::from_frames (rint (video_length_after_3d_combine() * frc.factor()), film->video_frame_rate());
+       return DCPTime::from_frames (llrint (video_length_after_3d_combine() * frc.factor()), film->video_frame_rate());
 }
 
 void
@@ -268,7 +302,8 @@ FFmpegContent::identifier () const
 {
        SafeStringStream s;
 
-       s << VideoContent::identifier();
+       s << VideoContent::identifier() << "_"
+         << SubtitleContent::identifier();
 
        boost::mutex::scoped_lock lm (_mutex);
 
@@ -295,7 +330,13 @@ FFmpegContent::subtitles_during (ContentTimePeriod period, bool starting) const
 }
 
 bool
-FFmpegContent::has_subtitles () const
+FFmpegContent::has_text_subtitles () const
+{
+       return false;
+}
+
+bool
+FFmpegContent::has_image_subtitles () const
 {
        return !subtitle_streams().empty ();
 }
@@ -318,8 +359,106 @@ vector<AudioStreamPtr>
 FFmpegContent::audio_streams () const
 {
        boost::mutex::scoped_lock lm (_mutex);
-       
+
        vector<AudioStreamPtr> s;
        copy (_audio_streams.begin(), _audio_streams.end(), back_inserter (s));
        return s;
 }
+
+void
+FFmpegContent::add_properties (list<pair<string, string> >& p) const
+{
+       VideoContent::add_properties (p);
+
+       if (_bits_per_pixel) {
+               int const sub = 219 * pow (2, _bits_per_pixel.get() - 8);
+               int const total = pow (2, _bits_per_pixel.get());
+
+               switch (_color_range) {
+               case AVCOL_RANGE_UNSPECIFIED:
+                       p.push_back (make_pair (_("Colour range"), _("Unspecified")));
+                       break;
+               case AVCOL_RANGE_MPEG:
+                       p.push_back (make_pair (_("Colour range"), String::compose (_("Limited (%1-%2)"), (total - sub) / 2, (total + sub) / 2)));
+                       break;
+               case AVCOL_RANGE_JPEG:
+                       p.push_back (make_pair (_("Colour range"), String::compose (_("Full (0-%1)"), total)));
+                       break;
+               default:
+                       DCPOMATIC_ASSERT (false);
+               }
+       } else {
+               switch (_color_range) {
+               case AVCOL_RANGE_UNSPECIFIED:
+                       p.push_back (make_pair (_("Colour range"), _("Unspecified")));
+                       break;
+               case AVCOL_RANGE_MPEG:
+                       p.push_back (make_pair (_("Colour range"), _("Limited")));
+                       break;
+               case AVCOL_RANGE_JPEG:
+                       p.push_back (make_pair (_("Colour range"), _("Full")));
+                       break;
+               default:
+                       DCPOMATIC_ASSERT (false);
+               }
+       }
+
+       char const * primaries[] = {
+               _("Unspecified"),
+               _("BT709"),
+               _("Unspecified"),
+               _("Unspecified"),
+               _("BT470M"),
+               _("BT470BG"),
+               _("SMPTE 170M (BT601)"),
+               _("SMPTE 240M"),
+               _("Film"),
+               _("BT2020")
+       };
+
+       DCPOMATIC_ASSERT (AVCOL_PRI_NB == 10);
+       p.push_back (make_pair (_("Colour primaries"), primaries[_color_primaries]));
+
+       char const * transfers[] = {
+               _("Unspecified"),
+               _("BT709"),
+               _("Unspecified"),
+               _("Unspecified"),
+               _("Gamma 22 (BT470M)"),
+               _("Gamma 28 (BT470BG)"),
+               _("SMPTE 170M (BT601)"),
+               _("SMPTE 240M"),
+               _("Linear"),
+               _("Logarithmic (100:1 range)"),
+               _("Logarithmic (316:1 range)"),
+               _("IEC61966-2-4"),
+               _("BT1361 extended colour gamut"),
+               _("IEC61966-2-1 (sRGB or sYCC)"),
+               _("BT2020 for a 10-bit system"),
+               _("BT2020 for a 12-bit system")
+       };
+
+       DCPOMATIC_ASSERT (AVCOL_TRC_NB == 16);
+       p.push_back (make_pair (_("Colour transfer characteristic"), transfers[_color_trc]));
+
+       char const * spaces[] = {
+               _("RGB / sRGB (IEC61966-2-1)"),
+               _("BT709"),
+               _("Unspecified"),
+               _("Unspecified"),
+               _("FCC"),
+               _("BT470BG (BT601-6)"),
+               _("SMPTE 170M (BT601-6)"),
+               _("SMPTE 240M"),
+               _("YCOCG"),
+               _("BT2020 non-constant luminance"),
+               _("BT2020 constant luminance"),
+       };
+
+       DCPOMATIC_ASSERT (AVCOL_SPC_NB == 11);
+       p.push_back (make_pair (_("Colourspace"), spaces[_colorspace]));
+
+       if (_bits_per_pixel) {
+               p.push_back (make_pair (_("Bits per pixel"), raw_convert<string> (_bits_per_pixel.get ())));
+       }
+}