Correctly spot that a DCP with unencrypted picture but encrypted sound/subtitle needs...
[dcpomatic.git] / src / lib / player.cc
index 6d155836fbd47f4055cdbf4832883c1392ec7f2a..69306e3817e94fd378a17ea862e525a19293419f 100644 (file)
@@ -1,38 +1,27 @@
 /*
     Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
 
-    This program is free software; you can redistribute it and/or modify
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
 
-    This program is distributed in the hope that it will be useful,
+    DCP-o-matic is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 
 #include "player.h"
 #include "film.h"
-#include "ffmpeg_decoder.h"
-#include "video_decoder.h"
-#include "audio_decoder.h"
 #include "audio_buffers.h"
-#include "audio_content.h"
-#include "ffmpeg_content.h"
-#include "image_decoder.h"
 #include "content_audio.h"
-#include "image_content.h"
-#include "subtitle_content.h"
-#include "text_subtitle_decoder.h"
-#include "text_subtitle_content.h"
-#include "video_mxf_decoder.h"
-#include "video_mxf_content.h"
 #include "dcp_content.h"
 #include "job.h"
 #include "image.h"
 #include "content_video.h"
 #include "player_video.h"
 #include "frame_rate_change.h"
-#include "dcp_content.h"
-#include "dcp_decoder.h"
-#include "dcp_subtitle_content.h"
-#include "dcp_subtitle_decoder.h"
 #include "audio_processor.h"
 #include "playlist.h"
 #include "referenced_reel_asset.h"
+#include "decoder_factory.h"
+#include "decoder.h"
+#include "video_decoder.h"
+#include "audio_decoder.h"
+#include "subtitle_content.h"
+#include "subtitle_decoder.h"
+#include "ffmpeg_content.h"
+#include "audio_content.h"
+#include "content_subtitle.h"
+#include "dcp_decoder.h"
+#include "image_decoder.h"
 #include <dcp/reel.h>
 #include <dcp/reel_sound_asset.h>
 #include <dcp/reel_subtitle_asset.h>
@@ -119,7 +115,6 @@ Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist
 void
 Player::setup_pieces ()
 {
-       list<shared_ptr<Piece> > old_pieces = _pieces;
        _pieces.clear ();
 
        BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) {
@@ -128,65 +123,8 @@ Player::setup_pieces ()
                        continue;
                }
 
-               shared_ptr<Decoder> decoder;
-               optional<FrameRateChange> frc;
-
-               /* FFmpeg */
-               shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (i);
-               if (fc) {
-                       decoder.reset (new FFmpegDecoder (fc, _film->log(), _fast));
-                       frc = FrameRateChange (fc->active_video_frame_rate(), _film->video_frame_rate());
-               }
-
-               shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
-               if (dc) {
-                       decoder.reset (new DCPDecoder (dc, _film->log(), _fast));
-                       frc = FrameRateChange (dc->active_video_frame_rate(), _film->video_frame_rate());
-               }
-
-               /* ImageContent */
-               shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (i);
-               if (ic) {
-                       /* See if we can re-use an old ImageDecoder */
-                       for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
-                               shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
-                               if (imd && imd->content() == ic) {
-                                       decoder = imd;
-                               }
-                       }
-
-                       if (!decoder) {
-                               decoder.reset (new ImageDecoder (ic, _film->log()));
-                       }
-
-                       frc = FrameRateChange (ic->active_video_frame_rate(), _film->video_frame_rate());
-               }
-
-               /* It's questionable whether subtitle content should have a video frame rate; perhaps
-                  it should be assumed that any subtitle content has been prepared at the same rate
-                  as simultaneous video content (like we do with audio).
-               */
-
-               /* TextSubtitleContent */
-               shared_ptr<const TextSubtitleContent> rc = dynamic_pointer_cast<const TextSubtitleContent> (i);
-               if (rc) {
-                       decoder.reset (new TextSubtitleDecoder (rc));
-                       frc = FrameRateChange (rc->active_video_frame_rate(), _film->video_frame_rate());
-               }
-
-               /* DCPSubtitleContent */
-               shared_ptr<const DCPSubtitleContent> dsc = dynamic_pointer_cast<const DCPSubtitleContent> (i);
-               if (dsc) {
-                       decoder.reset (new DCPSubtitleDecoder (dsc));
-                       frc = FrameRateChange (dsc->active_video_frame_rate(), _film->video_frame_rate());
-               }
-
-               /* VideoMXFContent */
-               shared_ptr<const VideoMXFContent> vmc = dynamic_pointer_cast<const VideoMXFContent> (i);
-               if (vmc) {
-                       decoder.reset (new VideoMXFDecoder (vmc, _film->log()));
-                       frc = FrameRateChange (vmc->active_video_frame_rate(), _film->video_frame_rate());
-               }
+               shared_ptr<Decoder> decoder = decoder_factory (i, _film->log());
+               FrameRateChange frc (i->active_video_frame_rate(), _film->video_frame_rate());
 
                if (!decoder) {
                        /* Not something that we can decode; e.g. Atmos content */
@@ -201,7 +139,16 @@ Player::setup_pieces ()
                        decoder->audio->set_ignore ();
                }
 
-               _pieces.push_back (shared_ptr<Piece> (new Piece (i, decoder, frc.get ())));
+               if (decoder->audio && _fast) {
+                       decoder->audio->set_fast ();
+               }
+
+               shared_ptr<DCPDecoder> dcp = dynamic_pointer_cast<DCPDecoder> (decoder);
+               if (dcp && _play_referenced) {
+                       dcp->set_decode_referenced ();
+               }
+
+               _pieces.push_back (shared_ptr<Piece> (new Piece (i, decoder, frc)));
        }
 
        _have_valid_pieces = true;
@@ -222,29 +169,47 @@ Player::playlist_content_changed (weak_ptr<Content> w, int property, bool freque
                property == ContentProperty::TRIM_END ||
                property == ContentProperty::PATH ||
                property == VideoContentProperty::FRAME_TYPE ||
-               property == DCPContentProperty::CAN_BE_PLAYED ||
+               property == DCPContentProperty::NEEDS_ASSETS ||
+               property == DCPContentProperty::NEEDS_KDM ||
                property == SubtitleContentProperty::COLOUR ||
                property == SubtitleContentProperty::OUTLINE ||
-               property == SubtitleContentProperty::OUTLINE_COLOUR ||
-               property == FFmpegContentProperty::SUBTITLE_STREAM
+               property == SubtitleContentProperty::SHADOW ||
+               property == SubtitleContentProperty::EFFECT_COLOUR ||
+               property == FFmpegContentProperty::SUBTITLE_STREAM ||
+               property == VideoContentProperty::COLOUR_CONVERSION
                ) {
 
                _have_valid_pieces = false;
                Changed (frequent);
 
+       } else if (
+               property == SubtitleContentProperty::LINE_SPACING ||
+               property == SubtitleContentProperty::OUTLINE_WIDTH ||
+               property == SubtitleContentProperty::Y_SCALE ||
+               property == SubtitleContentProperty::FADE_IN ||
+               property == SubtitleContentProperty::FADE_OUT
+               ) {
+
+               /* These changes just need the pieces' decoders to be reset.
+                  It's quite possible that other changes could be handled by
+                  this branch rather than the _have_valid_pieces = false branch
+                  above.  This would make things a lot faster.
+               */
+
+               reset_pieces ();
+               Changed (frequent);
+
        } else if (
                property == ContentProperty::VIDEO_FRAME_RATE ||
                property == SubtitleContentProperty::USE ||
                property == SubtitleContentProperty::X_OFFSET ||
                property == SubtitleContentProperty::Y_OFFSET ||
                property == SubtitleContentProperty::X_SCALE ||
-               property == SubtitleContentProperty::Y_SCALE ||
                property == SubtitleContentProperty::FONTS ||
                property == VideoContentProperty::CROP ||
                property == VideoContentProperty::SCALE ||
                property == VideoContentProperty::FADE_IN ||
-               property == VideoContentProperty::FADE_OUT ||
-               property == VideoContentProperty::COLOUR_CONVERSION
+               property == VideoContentProperty::FADE_OUT
                ) {
 
                Changed (frequent);
@@ -376,7 +341,7 @@ Player::get_video (DCPTime time, bool accurate)
 
        /* Text subtitles (rendered to an image) */
        if (!ps.text.empty ()) {
-               list<PositionImage> s = render_subtitles (ps.text, ps.fonts, _video_container_size);
+               list<PositionImage> s = render_subtitles (ps.text, ps.fonts, _video_container_size, time);
                copy (s.begin (), s.end (), back_inserter (sub_images));
        }
 
@@ -436,12 +401,12 @@ Player::get_video (DCPTime time, bool accurate)
                                                        shared_ptr<PlayerVideo> (
                                                                new PlayerVideo (
                                                                        i->image,
-                                                                       content_video_to_dcp (piece, i->frame),
+                                                                       time,
                                                                        piece->content->video->crop (),
-                                                                       piece->content->video->fade (i->frame),
+                                                                       piece->content->video->fade (i->frame.index()),
                                                                        image_size,
                                                                        _video_container_size,
-                                                                       i->eyes,
+                                                                       i->frame.eyes(),
                                                                        i->part,
                                                                        piece->content->video->colour_conversion ()
                                                                        )
@@ -629,7 +594,7 @@ Player::get_subtitles (DCPTime time, DCPTime length, bool starting, bool burnt,
 {
        list<shared_ptr<Piece> > subs = overlaps (time, time + length, has_subtitle);
 
-       PlayerSubtitles ps (time, length);
+       PlayerSubtitles ps (time);
 
        for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
                if (!(*j)->content->subtitle->use () || (!_always_burn_subtitles && (burnt != (*j)->content->subtitle->burn ()))) {
@@ -687,7 +652,7 @@ Player::get_subtitles (DCPTime time, DCPTime length, bool starting, bool burnt,
                                }
                                s.set_in (dcp::Time(content_subtitle_to_dcp (*j, ts.period().from).seconds(), 1000));
                                s.set_out (dcp::Time(content_subtitle_to_dcp (*j, ts.period().to).seconds(), 1000));
-                               ps.text.push_back (s);
+                               ps.text.push_back (SubtitleString (s, (*j)->content->subtitle->outline_width()));
                                ps.add_fonts ((*j)->content->subtitle->fonts ());
                        }
                }
@@ -768,15 +733,24 @@ Player::get_reel_assets ()
 
                scoped_ptr<DCPDecoder> decoder;
                try {
-                       decoder.reset (new DCPDecoder (j, _film->log(), false));
+                       decoder.reset (new DCPDecoder (j, _film->log()));
                } catch (...) {
                        return a;
                }
 
                int64_t offset = 0;
                BOOST_FOREACH (shared_ptr<dcp::Reel> k, decoder->reels()) {
+
+                       DCPOMATIC_ASSERT (j->video_frame_rate ());
+                       double const cfr = j->video_frame_rate().get();
+                       Frame const trim_start = j->trim_start().frames_round (cfr);
+                       Frame const trim_end = j->trim_end().frames_round (cfr);
+
                        DCPTime const from = i->position() + DCPTime::from_frames (offset, _film->video_frame_rate());
                        if (j->reference_video ()) {
+                               DCPOMATIC_ASSERT (k->main_picture ());
+                               k->main_picture()->set_entry_point (trim_start);
+                               k->main_picture()->set_duration (k->main_picture()->intrinsic_duration() - trim_start - trim_end);
                                a.push_back (
                                        ReferencedReelAsset (
                                                k->main_picture (),
@@ -786,6 +760,9 @@ Player::get_reel_assets ()
                        }
 
                        if (j->reference_audio ()) {
+                               DCPOMATIC_ASSERT (k->main_sound ());
+                               k->main_sound()->set_entry_point (trim_start);
+                               k->main_sound()->set_duration (k->main_sound()->intrinsic_duration() - trim_start - trim_end);
                                a.push_back (
                                        ReferencedReelAsset (
                                                k->main_sound (),
@@ -796,6 +773,8 @@ Player::get_reel_assets ()
 
                        if (j->reference_subtitle ()) {
                                DCPOMATIC_ASSERT (k->main_subtitle ());
+                               k->main_subtitle()->set_entry_point (trim_start);
+                               k->main_subtitle()->set_duration (k->main_subtitle()->intrinsic_duration() - trim_start - trim_end);
                                a.push_back (
                                        ReferencedReelAsset (
                                                k->main_subtitle (),
@@ -828,3 +807,11 @@ Player::overlaps (DCPTime from, DCPTime to, boost::function<bool (Content *)> va
 
        return overlaps;
 }
+
+void
+Player::reset_pieces ()
+{
+       BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
+               i->decoder->reset ();
+       }
+}