X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fplayer.cc;h=640253c6d97da8e80ca4bca5b604a5ab3f989b78;hb=5ae794047e5ceee4502e59211f0083a60224f4bd;hp=9f9f8db2e3374b369f7c206e480310b9d278dd01;hpb=2cf3da72a017eebf741dfb9a5ec158df94a4e7b7;p=dcpomatic.git diff --git a/src/lib/player.cc b/src/lib/player.cc index 9f9f8db2e..640253c6d 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2014 Carl Hetherington + Copyright (C) 2013-2015 Carl Hetherington This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,8 +17,6 @@ */ -#include -#include #include "player.h" #include "film.h" #include "ffmpeg_decoder.h" @@ -31,13 +29,13 @@ #include "subtitle_content.h" #include "subrip_decoder.h" #include "subrip_content.h" +#include "dcp_content.h" #include "playlist.h" #include "job.h" #include "image.h" -#include "image_proxy.h" +#include "raw_image_proxy.h" #include "ratio.h" #include "log.h" -#include "scaler.h" #include "render_subtitles.h" #include "config.h" #include "content_video.h" @@ -45,6 +43,13 @@ #include "frame_rate_change.h" #include "dcp_content.h" #include "dcp_decoder.h" +#include "dcp_subtitle_content.h" +#include "dcp_subtitle_decoder.h" +#include +#include +#include + +#include "i18n.h" #define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL); @@ -66,7 +71,7 @@ Player::Player (shared_ptr f, shared_ptr p) : _film (f) , _playlist (p) , _have_valid_pieces (false) - , _approximate_size (false) + , _ignore_video (false) { _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this)); _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3)); @@ -124,7 +129,7 @@ Player::setup_pieces () shared_ptr dc = dynamic_pointer_cast (*i); if (dc) { - decoder.reset (new DCPDecoder (dc, _film->log ())); + decoder.reset (new DCPDecoder (dc)); frc = FrameRateChange (dc->video_frame_rate(), _film->video_frame_rate()); } @@ -160,6 +165,18 @@ Player::setup_pieces () frc = best_overlap_frc; } + /* DCPSubtitleContent */ + shared_ptr dsc = dynamic_pointer_cast (*i); + if (dsc) { + decoder.reset (new DCPSubtitleDecoder (dsc)); + frc = best_overlap_frc; + } + + shared_ptr vd = dynamic_pointer_cast (decoder); + if (vd && _ignore_video) { + vd->set_ignore_video (); + } + _pieces.push_back (shared_ptr (new Piece (*i, decoder, frc.get ()))); } @@ -180,27 +197,30 @@ Player::content_changed (weak_ptr w, int property, bool frequent) property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END || property == ContentProperty::PATH || - property == VideoContentProperty::VIDEO_FRAME_TYPE + property == VideoContentProperty::VIDEO_FRAME_TYPE || + property == DCPContentProperty::CAN_BE_PLAYED ) { _have_valid_pieces = false; Changed (frequent); } else if ( - property == SubtitleContentProperty::SUBTITLE_USE || + property == SubtitleContentProperty::USE_SUBTITLES || property == SubtitleContentProperty::SUBTITLE_X_OFFSET || property == SubtitleContentProperty::SUBTITLE_Y_OFFSET || - property == SubtitleContentProperty::SUBTITLE_SCALE || + property == SubtitleContentProperty::SUBTITLE_X_SCALE || + property == SubtitleContentProperty::SUBTITLE_Y_SCALE || property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_SCALE || - property == VideoContentProperty::VIDEO_FRAME_RATE + property == VideoContentProperty::VIDEO_FRAME_RATE || + property == VideoContentProperty::VIDEO_FADE_IN || + property == VideoContentProperty::VIDEO_FADE_OUT ) { Changed (frequent); } } -/** @param already_resampled true if this data has already been through the chain up to the resampler */ void Player::playlist_changed () { @@ -225,7 +245,7 @@ Player::film_changed (Film::Property p) last time we were run. */ - if (p == Film::SCALER || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) { + if (p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) { Changed (false); } } @@ -249,8 +269,8 @@ Player::transform_image_subtitles (list subs) const * rect.x * _video_container_size.width and rect.y * _video_container_size.height. * * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be - * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and - * (height_before_subtitle_scale * (1 - subtitle_scale) / 2). + * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and + * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2). * * Combining these two translations gives these expressions. */ @@ -259,7 +279,7 @@ Player::transform_image_subtitles (list subs) const PositionImage ( i->image->scale ( scaled_size, - Scaler::from_id ("bicubic"), + dcp::YUV_TO_RGB_REC601, i->image->pixel_format (), true ), @@ -274,23 +294,17 @@ Player::transform_image_subtitles (list subs) const return all; } -void -Player::set_approximate_size () -{ - _approximate_size = true; -} - shared_ptr Player::black_player_video_frame (DCPTime time) const { return shared_ptr ( new PlayerVideo ( - shared_ptr (new RawImageProxy (_black_image, _film->log ())), + shared_ptr (new RawImageProxy (_black_image)), time, Crop (), + optional (), _video_container_size, _video_container_size, - Scaler::from_id ("bicubic"), EYES_BOTH, PART_WHOLE, Config::instance()->colour_conversions().front().conversion @@ -305,7 +319,7 @@ Player::get_video (DCPTime time, bool accurate) if (!_have_valid_pieces) { setup_pieces (); } - + list > ov = overlaps ( time, time + DCPTime::from_frames (1, _film->video_frame_rate ()) @@ -321,9 +335,9 @@ Player::get_video (DCPTime time, bool accurate) shared_ptr piece = ov.back (); shared_ptr decoder = dynamic_pointer_cast (piece->decoder); - assert (decoder); + DCPOMATIC_ASSERT (decoder); shared_ptr content = dynamic_pointer_cast (piece->content); - assert (content); + DCPOMATIC_ASSERT (content); list content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate); if (content_video.empty ()) { @@ -332,11 +346,7 @@ Player::get_video (DCPTime time, bool accurate) } dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size ()); - if (_approximate_size) { - image_size.width &= ~3; - image_size.height &= ~3; - } - + for (list::const_iterator i = content_video.begin(); i != content_video.end(); ++i) { pvf.push_back ( shared_ptr ( @@ -344,9 +354,9 @@ Player::get_video (DCPTime time, bool accurate) i->image, content_video_to_dcp (piece, i->frame), content->crop (), + content->fade (i->frame), image_size, _video_container_size, - _film->scaler(), i->eyes, i->part, content->colour_conversion () @@ -366,9 +376,11 @@ Player::get_video (DCPTime time, bool accurate) list c = transform_image_subtitles (ps.image); copy (c.begin(), c.end(), back_inserter (sub_images)); - /* Text subtitles (rendered to images) */ - sub_images.push_back (render_subtitles (ps.text, _video_container_size)); - + /* Text subtitles (rendered to an image) */ + if (!ps.text.empty ()) { + sub_images.push_back (render_subtitles (ps.text, _video_container_size)); + } + if (!sub_images.empty ()) { for (list >::const_iterator i = pvf.begin(); i != pvf.end(); ++i) { (*i)->set_subtitle (merge (sub_images)); @@ -398,9 +410,9 @@ Player::get_audio (DCPTime time, DCPTime length, bool accurate) for (list >::iterator i = ov.begin(); i != ov.end(); ++i) { shared_ptr content = dynamic_pointer_cast ((*i)->content); - assert (content); + DCPOMATIC_ASSERT (content); shared_ptr decoder = dynamic_pointer_cast ((*i)->decoder); - assert (decoder); + DCPOMATIC_ASSERT (decoder); if (content->audio_frame_rate() == 0) { /* This AudioContent has no audio (e.g. if it is an FFmpegContent with no @@ -411,19 +423,24 @@ Player::get_audio (DCPTime time, DCPTime length, bool accurate) /* The time that we should request from the content */ DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0); + AudioFrame request_frames = length_frames; DCPTime offset; if (request < DCPTime ()) { /* We went off the start of the content, so we will need to offset the stuff we get back. */ offset = -request; + request_frames += request.frames (_film->audio_frame_rate ()); + if (request_frames < 0) { + request_frames = 0; + } request = DCPTime (); } AudioFrame const content_frame = dcp_to_content_audio (*i, request); /* Audio from this piece's decoder (which might be more or less than what we asked for) */ - shared_ptr all = decoder->get_audio (content_frame, length_frames, accurate); + shared_ptr all = decoder->get_audio (content_frame, request_frames, accurate); /* Gain */ if (content->audio_gain() != 0) { @@ -455,7 +472,7 @@ Player::get_audio (DCPTime time, DCPTime length, bool accurate) all->audio.get(), content_frame - all->frame, offset.frames (_film->audio_frame_rate()), - min (AudioFrame (all->audio->frames()), length_frames) - offset.frames (_film->audio_frame_rate ()) + min (AudioFrame (all->audio->frames()), request_frames) ); } @@ -471,13 +488,13 @@ Player::dcp_to_content_video (shared_ptr piece, DCPTime t) const s = DCPTime (min (piece->content->length_after_trim().get(), s.get())); /* Convert this to the content frame */ - return DCPTime (s + piece->content->trim_start()).frames (_film->video_frame_rate()) * piece->frc.factor (); + return DCPTime (s + piece->content->trim_start()).frames (_film->video_frame_rate()) / piece->frc.factor (); } DCPTime Player::content_video_to_dcp (shared_ptr piece, VideoFrame f) const { - DCPTime t = DCPTime::from_frames (f / piece->frc.factor (), _film->video_frame_rate()) - piece->content->trim_start () + piece->content->position (); + DCPTime t = DCPTime::from_frames (f * piece->frc.factor (), _film->video_frame_rate()) - piece->content->trim_start () + piece->content->position (); if (t < DCPTime ()) { t = DCPTime (); } @@ -530,7 +547,7 @@ Player::get_subtitles (DCPTime time, DCPTime length, bool starting) for (list >::const_iterator j = subs.begin(); j != subs.end(); ++j) { shared_ptr subtitle_content = dynamic_pointer_cast ((*j)->content); - if (!subtitle_content->subtitle_use ()) { + if (!subtitle_content->use_subtitles ()) { continue; } @@ -547,21 +564,54 @@ Player::get_subtitles (DCPTime time, DCPTime length, bool starting) i->sub.rectangle.y += subtitle_content->subtitle_y_offset (); /* Apply content's subtitle scale */ - i->sub.rectangle.width *= subtitle_content->subtitle_scale (); - i->sub.rectangle.height *= subtitle_content->subtitle_scale (); + i->sub.rectangle.width *= subtitle_content->subtitle_x_scale (); + i->sub.rectangle.height *= subtitle_content->subtitle_y_scale (); /* Apply a corrective translation to keep the subtitle centred after that scale */ - i->sub.rectangle.x -= i->sub.rectangle.width * (subtitle_content->subtitle_scale() - 1); - i->sub.rectangle.y -= i->sub.rectangle.height * (subtitle_content->subtitle_scale() - 1); + i->sub.rectangle.x -= i->sub.rectangle.width * (subtitle_content->subtitle_x_scale() - 1); + i->sub.rectangle.y -= i->sub.rectangle.height * (subtitle_content->subtitle_y_scale() - 1); ps.image.push_back (i->sub); } list text = subtitle_decoder->get_text_subtitles (ContentTimePeriod (from, to), starting); - for (list::const_iterator i = text.begin(); i != text.end(); ++i) { - copy (i->subs.begin(), i->subs.end(), back_inserter (ps.text)); + BOOST_FOREACH (ContentTextSubtitle& ts, text) { + BOOST_FOREACH (dcp::SubtitleString& s, ts.subs) { + s.set_v_position (s.v_position() + subtitle_content->subtitle_y_offset ()); + s.set_size (s.size() * max (subtitle_content->subtitle_x_scale(), subtitle_content->subtitle_y_scale())); + ps.text.push_back (s); + } } } return ps; } + +list > +Player::get_subtitle_fonts () +{ + if (!_have_valid_pieces) { + setup_pieces (); + } + + list > fonts; + BOOST_FOREACH (shared_ptr& p, _pieces) { + shared_ptr sc = dynamic_pointer_cast (p->content); + if (sc) { + /* XXX: things may go wrong if there are duplicate font IDs + with different font files. + */ + list > f = sc->fonts (); + copy (f.begin(), f.end(), back_inserter (fonts)); + } + } + + return fonts; +} + +/** Set this player never to produce any video data */ +void +Player::set_ignore_video () +{ + _ignore_video = true; +}