/** Get the subtitles that should be burnt into a given period.
* @param period Period of interest.
- * @param always_burn_subtitles Always burn subtitles even if their content is not set to burn.
+ * @param always_burn_captions Always burn captions even if their content is not set to burn.
*/
list<PlayerCaption>
-ActiveCaptions::get_burnt (DCPTimePeriod period, bool always_burn_subtitles) const
+ActiveCaptions::get_burnt (DCPTimePeriod period, bool always_burn_captions) const
{
list<PlayerCaption> ps;
continue;
}
- if (!piece->content->caption->use() || (!always_burn_subtitles && !piece->content->caption->burn())) {
+ if (!piece->content->caption->use() || (!always_burn_captions && !piece->content->caption->burn())) {
/* Not burning this piece */
continue;
}
class ActiveCaptions : public boost::noncopyable
{
public:
- std::list<PlayerCaption> get_burnt (DCPTimePeriod period, bool always_burn_subtitles) const;
+ std::list<PlayerCaption> get_burnt (DCPTimePeriod period, bool always_burn_captions) const;
void clear_before (DCPTime time);
void clear ();
void add_from (boost::weak_ptr<Piece> piece, PlayerCaption ps, DCPTime from);
break;
}
- _player->set_always_burn_subtitles (true);
+ _player->set_always_burn_captions (CAPTION_OPEN);
_player->set_play_referenced ();
int const ch = film->audio_channels ();
, _have_valid_pieces (false)
, _ignore_video (false)
, _ignore_subtitle (false)
- , _always_burn_subtitles (false)
, _fast (false)
, _play_referenced (false)
, _audio_merger (_film->audio_frame_rate())
_ignore_subtitle = true;
}
-/** Set whether or not this player should always burn text subtitles into the image,
+/** Set a type of caption that this player should always burn into the image,
* regardless of the content settings.
- * @param burn true to always burn subtitles, false to obey content settings.
+ * @param type type of captions to burn.
*/
void
-Player::set_always_burn_subtitles (bool burn)
+Player::set_always_burn_captions (CaptionType type)
{
- _always_burn_subtitles = burn;
+ _always_burn_captions = type;
}
/** Sets up the player to be faster, possibly at the expense of quality */
}
optional<PositionImage>
-Player::subtitles_for_frame (DCPTime time) const
+Player::captions_for_frame (DCPTime time) const
{
- list<PositionImage> subtitles;
+ list<PositionImage> captions;
int const vfr = _film->video_frame_rate();
- BOOST_FOREACH (PlayerCaption i, _active_captions[CAPTION_OPEN].get_burnt (DCPTimePeriod(time, time + DCPTime::from_frames(1, vfr)), _always_burn_subtitles)) {
-
- /* Image subtitles */
- list<PositionImage> c = transform_bitmap_captions (i.image);
- copy (c.begin(), c.end(), back_inserter (subtitles));
-
- /* Text subtitles (rendered to an image) */
- if (!i.text.empty ()) {
- list<PositionImage> s = render_text (i.text, i.fonts, _video_container_size, time, vfr);
- copy (s.begin(), s.end(), back_inserter (subtitles));
+ for (int i = 0; i < CAPTION_COUNT; ++i) {
+ bool const always = _always_burn_captions && *_always_burn_captions == i;
+ BOOST_FOREACH (
+ PlayerCaption j,
+ _active_captions[i].get_burnt(DCPTimePeriod(time, time + DCPTime::from_frames(1, vfr)), always)
+ ) {
+
+ /* Image subtitles */
+ list<PositionImage> c = transform_bitmap_captions (j.image);
+ copy (c.begin(), c.end(), back_inserter (captions));
+
+ /* Text subtitles (rendered to an image) */
+ if (!j.text.empty ()) {
+ list<PositionImage> s = render_text (j.text, j.fonts, _video_container_size, time, vfr);
+ copy (s.begin(), s.end(), back_inserter (captions));
+ }
}
}
- if (subtitles.empty ()) {
+ if (captions.empty ()) {
return optional<PositionImage> ();
}
- return merge (subtitles);
+ return merge (captions);
}
void
pair<PlayerCaption, DCPTime> from = _active_captions[type].add_to (wp, dcp_to);
- if (piece->content->caption->use() && !_always_burn_subtitles && !piece->content->caption->burn()) {
+ bool const always = _always_burn_captions && *_always_burn_captions == type;
+ if (piece->content->caption->use() && !always && !piece->content->caption->burn()) {
Caption (from.first, type, DCPTimePeriod (from.second, dcp_to));
}
}
}
}
- optional<PositionImage> subtitles = subtitles_for_frame (time);
- if (subtitles) {
- pv->set_subtitle (subtitles.get ());
+ optional<PositionImage> captions = captions_for_frame (time);
+ if (captions) {
+ pv->set_caption (captions.get ());
}
Video (pv, time);
void set_video_container_size (dcp::Size);
void set_ignore_video ();
void set_ignore_subtitle ();
- void set_always_burn_subtitles (bool burn);
+ void set_always_burn_captions (CaptionType type);
void set_fast ();
void set_play_referenced ();
void set_dcp_decode_reduction (boost::optional<int> reduction);
std::pair<boost::shared_ptr<AudioBuffers>, DCPTime> discard_audio (
boost::shared_ptr<const AudioBuffers> audio, DCPTime time, DCPTime discard_to
) const;
- boost::optional<PositionImage> subtitles_for_frame (DCPTime time) const;
+ boost::optional<PositionImage> captions_for_frame (DCPTime time) const;
void emit_video (boost::shared_ptr<PlayerVideo> pv, DCPTime time);
void do_emit_video (boost::shared_ptr<PlayerVideo> pv, DCPTime time);
void emit_audio (boost::shared_ptr<AudioBuffers> data, DCPTime time);
bool _ignore_video;
/** true if the player should ignore all audio; i.e. never produce any */
bool _ignore_subtitle;
- /** true if the player should always burn subtitles into the video regardless
- of content settings
+ /** Type of captions that the player should always burn into the video regardless
+ of content settings.
*/
- bool _always_burn_subtitles;
+ boost::optional<CaptionType> _always_burn_captions;
/** true if we should try to be fast rather than high quality */
bool _fast;
/** true if we should `play' (i.e output) referenced DCP data (e.g. for preview) */
image->read_from_socket (socket);
- _subtitle = PositionImage (image, Position<int> (node->number_child<int> ("SubtitleX"), node->number_child<int> ("SubtitleY")));
+ _caption = PositionImage (image, Position<int> (node->number_child<int> ("SubtitleX"), node->number_child<int> ("SubtitleY")));
}
}
void
-PlayerVideo::set_subtitle (PositionImage image)
+PlayerVideo::set_caption (PositionImage image)
{
- _subtitle = image;
+ _caption = image;
}
/** Create an image for this frame.
total_crop, _inter_size, _out_size, yuv_to_rgb, pixel_format (_in->pixel_format()), aligned, fast
);
- if (_subtitle) {
- out->alpha_blend (Image::ensure_aligned (_subtitle->image), _subtitle->position);
+ if (_caption) {
+ out->alpha_blend (Image::ensure_aligned (_caption->image), _caption->position);
}
if (_fade) {
if (_colour_conversion) {
_colour_conversion.get().as_xml (node);
}
- if (_subtitle) {
- node->add_child ("SubtitleWidth")->add_child_text (raw_convert<string> (_subtitle->image->size().width));
- node->add_child ("SubtitleHeight")->add_child_text (raw_convert<string> (_subtitle->image->size().height));
- node->add_child ("SubtitleX")->add_child_text (raw_convert<string> (_subtitle->position.x));
- node->add_child ("SubtitleY")->add_child_text (raw_convert<string> (_subtitle->position.y));
+ if (_caption) {
+ node->add_child ("SubtitleWidth")->add_child_text (raw_convert<string> (_caption->image->size().width));
+ node->add_child ("SubtitleHeight")->add_child_text (raw_convert<string> (_caption->image->size().height));
+ node->add_child ("SubtitleX")->add_child_text (raw_convert<string> (_caption->position.x));
+ node->add_child ("SubtitleY")->add_child_text (raw_convert<string> (_caption->position.y));
}
}
PlayerVideo::send_binary (shared_ptr<Socket> socket) const
{
_in->send_binary (socket);
- if (_subtitle) {
- _subtitle->image->write_to_socket (socket);
+ if (_caption) {
+ _caption->image->write_to_socket (socket);
}
}
return false;
}
- return _crop == Crop () && _out_size == j2k->size() && !_subtitle && !_fade && !_colour_conversion;
+ return _crop == Crop () && _out_size == j2k->size() && !_caption && !_fade && !_colour_conversion;
}
Data
return false;
}
- if ((!_subtitle && other->_subtitle) || (_subtitle && !other->_subtitle)) {
- /* One has a subtitle and the other doesn't */
+ if ((!_caption && other->_caption) || (_caption && !other->_caption)) {
+ /* One has a caption and the other doesn't */
return false;
}
- if (_subtitle && other->_subtitle && !_subtitle->same (other->_subtitle.get ())) {
- /* They both have subtitles but they are different */
+ if (_caption && other->_caption && !_caption->same (other->_caption.get ())) {
+ /* They both have captions but they are different */
return false;
}
return _in->memory_used();
}
-/** @return Shallow copy of this; _in and _subtitle are shared between the original and the copy */
+/** @return Shallow copy of this; _in and _caption are shared between the original and the copy */
shared_ptr<PlayerVideo>
PlayerVideo::shallow_copy () const
{
boost::shared_ptr<PlayerVideo> shallow_copy () const;
- void set_subtitle (PositionImage);
+ void set_caption (PositionImage);
void prepare ();
boost::shared_ptr<Image> image (dcp::NoteHandler note, boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const;
Eyes _eyes;
Part _part;
boost::optional<ColourConversion> _colour_conversion;
- boost::optional<PositionImage> _subtitle;
+ boost::optional<PositionImage> _caption;
/** Content that we came from. This is so that reset_metadata() can work */
boost::weak_ptr<Content> _content;
/** Video frame that we came from. Again, this is for reset_metadata() */
: wxPanel (p)
, _panel (new wxPanel (this))
, _outline_content (0)
- , _left_eye (new wxRadioButton (this, wxID_ANY, _("Left eye"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP))
- , _right_eye (new wxRadioButton (this, wxID_ANY, _("Right eye")))
+ , _eye (0)
, _jump_to_selected (0)
, _slider (new wxSlider (this, wxID_ANY, 0, 0, 4096))
, _rewind_button (new wxButton (this, wxID_ANY, wxT("|<")))
wxBoxSizer* view_options = new wxBoxSizer (wxHORIZONTAL);
if (outline_content) {
_outline_content = new wxCheckBox (this, wxID_ANY, _("Outline content"));
- view_options->Add (_outline_content, 0, wxRIGHT, DCPOMATIC_SIZER_GAP);
+ view_options->Add (_outline_content, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP);
}
- view_options->Add (_left_eye, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP);
- view_options->Add (_right_eye, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP);
+
+ _eye = new wxChoice (this, wxID_ANY);
+ _eye->Append (_("Left"));
+ _eye->Append (_("Right"));
+ _eye->SetSelection (0);
+ view_options->Add (_eye, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP);
+
if (jump_to_selected) {
_jump_to_selected = new wxCheckBox (this, wxID_ANY, _("Jump to selected content"));
- view_options->Add (_jump_to_selected, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP);
+ view_options->Add (_jump_to_selected, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP);
}
+
+ _captions = new wxChoice (this, wxID_ANY);
+ _captions->Append (_("Open captions (subtitles)"));
+ _captions->Append (_("Closed captions"));
+ _captions->SetSelection (0);
+ view_options->Add (_captions, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP);
+
_v_sizer->Add (view_options, 0, wxALL, DCPOMATIC_SIZER_GAP);
wxBoxSizer* h_sizer = new wxBoxSizer (wxHORIZONTAL);
if (_outline_content) {
_outline_content->Bind (wxEVT_CHECKBOX, boost::bind (&FilmViewer::refresh_panel, this));
}
- _left_eye->Bind (wxEVT_RADIOBUTTON, boost::bind (&FilmViewer::slow_refresh, this));
- _right_eye->Bind (wxEVT_RADIOBUTTON, boost::bind (&FilmViewer::slow_refresh, this));
+ _eye->Bind (wxEVT_CHOICE, boost::bind (&FilmViewer::slow_refresh, this));
+ _captions->Bind (wxEVT_CHOICE, boost::bind (&FilmViewer::captions_changed, this));
_slider->Bind (wxEVT_SCROLL_THUMBTRACK, boost::bind (&FilmViewer::slider_moved, this, false));
_slider->Bind (wxEVT_SCROLL_PAGEUP, boost::bind (&FilmViewer::slider_moved, this, true));
_slider->Bind (wxEVT_SCROLL_PAGEDOWN, boost::bind (&FilmViewer::slider_moved, this, true));
return;
}
- /* Always burn in subtitles, even if content is set not to, otherwise we won't see them
- in the preview.
- */
- _player->set_always_burn_subtitles (true);
+ /* Start off burning in subtitles, as that's the initial setting of the dropdown */
+ _player->set_always_burn_captions (CAPTION_OPEN);
_player->set_play_referenced ();
_film->Changed.connect (boost::bind (&FilmViewer::film_changed, this, _1));
_player_video = _butler->get_video ();
} while (
_film->three_d() &&
- ((_left_eye->GetValue() && _player_video.first->eyes() == EYES_RIGHT) || (_right_eye->GetValue() && _player_video.first->eyes() == EYES_LEFT))
+ ((_eye->GetSelection() == 0 && _player_video.first->eyes() == EYES_RIGHT) || (_eye->GetSelection() == 1 && _player_video.first->eyes() == EYES_LEFT))
);
_butler->rethrow ();
_jump_to_selected->Enable (c);
}
- _left_eye->Enable (c && _film->three_d ());
- _right_eye->Enable (c && _film->three_d ());
+ _eye->Enable (c && _film->three_d ());
}
void
}
}
+void
+FilmViewer::captions_changed ()
+{
+ switch (_captions->GetSelection()) {
+ case 0:
+ _player->set_always_burn_captions (CAPTION_OPEN);
+ break;
+ case 1:
+ _player->set_always_burn_captions (CAPTION_CLOSED);
+ break;
+ }
+
+ slow_refresh ();
+}
+
/** Re-get the current frame slowly by seeking */
void
FilmViewer::slow_refresh ()
void slider_released ();
void play_clicked ();
void timer ();
+ void captions_changed ();
void calculate_sizes ();
void check_play_state ();
void active_jobs_changed (boost::optional<std::string>);
/** The area that we put our image in */
wxPanel* _panel;
wxCheckBox* _outline_content;
- wxRadioButton* _left_eye;
- wxRadioButton* _right_eye;
+ wxChoice* _eye;
wxCheckBox* _jump_to_selected;
+ wxChoice* _captions;
wxSlider* _slider;
wxButton* _rewind_button;
wxButton* _back_button;
)
);
- pvf->set_subtitle (PositionImage (sub_image, Position<int> (50, 60)));
+ pvf->set_caption (PositionImage (sub_image, Position<int> (50, 60)));
shared_ptr<DCPVideo> frame (
new DCPVideo (
)
);
- pvf->set_subtitle (PositionImage (sub_image, Position<int> (50, 60)));
+ pvf->set_caption (PositionImage (sub_image, Position<int> (50, 60)));
shared_ptr<DCPVideo> frame (
new DCPVideo (
shared_ptr<Player> player (new Player (film, film->playlist()));
player->set_fast ();
- player->set_always_burn_subtitles (true);
+ player->set_always_burn_captions (CAPTION_OPEN);
player->set_play_referenced ();
shared_ptr<Butler> butler (new Butler (player, film->log(), AudioMapping(), 2));
shared_ptr<Player> player (new Player (film, film->playlist()));
player->set_fast ();
- player->set_always_burn_subtitles (true);
+ player->set_always_burn_captions (CAPTION_OPEN);
player->set_play_referenced ();
shared_ptr<Butler> butler (new Butler (player, film->log(), AudioMapping(), 2));