From 60b505349fcc21cfc8bbed0de75b21755d520038 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 9 May 2015 22:47:37 +0100 Subject: 9a5d37cbc5be0bae532baafeb8df0616e10f3824 from master; add UTF-8 content type to KDM emails. --- src/lib/kdm.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/lib') diff --git a/src/lib/kdm.cc b/src/lib/kdm.cc index 3f88bbd9d..8949736f8 100644 --- a/src/lib/kdm.cc +++ b/src/lib/kdm.cc @@ -253,6 +253,8 @@ email_kdms ( if (!Config::instance()->kdm_bcc().empty ()) { quickmail_add_bcc (mail, Config::instance()->kdm_bcc().c_str ()); } + + quickmail_add_header (mail, "Content-Type: text/plain; charset=UTF-8"); string body = Config::instance()->kdm_email().c_str(); boost::algorithm::replace_all (body, "$CPL_NAME", film->dcp_name ()); -- cgit v1.2.3 From 8d20417855c563afa08bda167ccf8ab8609208fa Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 9 May 2015 22:59:07 +0100 Subject: Apply a modified version of 7c76bbb0c989cb5e5e552f28668a985243438cab from master; when we push an encoded frame's data to disk because we can't write it yet, the idea i to set encoded in the QueueItem to 0 to say that the data has already been pushed to disk. This was not working because we were resetting encoded in a copy of the QueueItem, and hence not affecting the QueueItem held in _queue. This meant that the same frame could be pushed to disk several times. Use the iterator instead of a copy so that the QueueItem in the list is modified. --- ChangeLog | 3 +++ TO_PORT | 4 +--- src/lib/writer.cc | 15 +++++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) (limited to 'src/lib') diff --git a/ChangeLog b/ChangeLog index 17a774ab5..4a2a6bedd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2015-05-09 Carl Hetherington + * Efficiency fix for cases where there is a lot of processing + power. + * Add UTF-8 content type to KDM emails to try to fix #549. * Disable OK until a download succeeds in the certificate diff --git a/TO_PORT b/TO_PORT index 1379650b3..9f7668d51 100644 --- a/TO_PORT +++ b/TO_PORT @@ -1,8 +1,6 @@ -1d63d0309d071254fcf4da65d3710e94fadd38e8 -0c0211871d0be5b3409adfc88d2979ca5b439b0a -wscript/cscript etc. cleanups 2a3bfb06c68afd1aa4daaa14ece050689ea47927 f248f57f745176349a5ac938842216954aab477e 3eabb0b4eb8a2303ce4f7aa5ed2fd77645d803a8 07d0be5e167b326caefb8e9981d99faf3823b15b 0b3fb0ba4c841de950e6f62ddd3992925ca781c6 +wscript/cscript etc. cleanups diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 31c265e2f..1a11a482b 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -366,21 +366,24 @@ try } DCPOMATIC_ASSERT (i != _queue.rend()); - QueueItem qi = *i; - ++_pushed_to_disk; - lock.unlock (); + /* i is valid here, even though we don't hold a lock on the mutex, + since list iterators are unaffected by insertion and only this + thread could erase the last item in the list. + */ + LOG_GENERAL ( "Writer full (awaiting %1 [last eye was %2]); pushes %3 to disk", _last_written_frame + 1, - _last_written_eyes, qi.frame + _last_written_eyes, i->frame ); - qi.encoded->write (_film, qi.frame, qi.eyes); + i->encoded->write (_film, i->frame, i->eyes); + lock.lock (); - qi.encoded.reset (); + i->encoded.reset (); --_queued_full_in_memory; } -- cgit v1.2.3 From fc96a4b3d6985f28db6bc0e9418e98cc5bec87e3 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 10 May 2015 00:15:44 +0100 Subject: 7fd73c0cf1f723896826c77fec3720c5c404d4e8 from master; tidy audio analysis dialogue and add overall peak. --- TO_PORT | 1 - src/lib/analyse_audio_job.cc | 14 ++++++++- src/lib/analyse_audio_job.h | 3 ++ src/lib/audio_analysis.cc | 16 ++++++++++ src/lib/audio_analysis.h | 18 ++++++++++- src/wx/audio_dialog.cc | 73 +++++++++++++++++++++++++++++++++++--------- src/wx/audio_dialog.h | 6 +++- src/wx/audio_panel.cc | 2 +- src/wx/film_viewer.cc | 11 +------ src/wx/wx_util.cc | 14 +++++++++ src/wx/wx_util.h | 2 ++ 11 files changed, 130 insertions(+), 30 deletions(-) (limited to 'src/lib') diff --git a/TO_PORT b/TO_PORT index 20d9c0951..40b6d3cf5 100644 --- a/TO_PORT +++ b/TO_PORT @@ -1,4 +1,3 @@ -f248f57f745176349a5ac938842216954aab477e 3eabb0b4eb8a2303ce4f7aa5ed2fd77645d803a8 07d0be5e167b326caefb8e9981d99faf3823b15b 0b3fb0ba4c841de950e6f62ddd3992925ca781c6 diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc index 079fe884e..cdf623876 100644 --- a/src/lib/analyse_audio_job.cc +++ b/src/lib/analyse_audio_job.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-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 @@ -39,6 +39,8 @@ AnalyseAudioJob::AnalyseAudioJob (shared_ptr f, shared_ptrlength().seconds()); } + _analysis->set_peak (_overall_peak, DCPTime::from_frames (_overall_peak_frame, _film->audio_frame_rate ())); _analysis->write (content->audio_analysis_path ()); set_progress (1); @@ -101,6 +104,15 @@ AnalyseAudioJob::analyse (shared_ptr b) _current[j][AudioPoint::RMS] += pow (s, 2); _current[j][AudioPoint::PEAK] = max (_current[j][AudioPoint::PEAK], fabsf (s)); + float const as = fabs (s); + + _current[j][AudioPoint::PEAK] = max (_current[j][AudioPoint::PEAK], as); + + if (as > _overall_peak) { + _overall_peak = as; + _overall_peak_frame = _done + i; + } + if ((_done % _samples_per_point) == 0) { _current[j][AudioPoint::RMS] = sqrt (_current[j][AudioPoint::RMS] / _samples_per_point); _analysis->add_point (j, _current[j]); diff --git a/src/lib/analyse_audio_job.h b/src/lib/analyse_audio_job.h index 6f64dd272..0f9605eed 100644 --- a/src/lib/analyse_audio_job.h +++ b/src/lib/analyse_audio_job.h @@ -52,6 +52,9 @@ private: int64_t _samples_per_point; std::vector _current; + float _overall_peak; + AudioFrame _overall_peak_frame; + boost::shared_ptr _analysis; static const int _num_points; diff --git a/src/lib/audio_analysis.cc b/src/lib/audio_analysis.cc index 19a0d876e..ee34b0d80 100644 --- a/src/lib/audio_analysis.cc +++ b/src/lib/audio_analysis.cc @@ -22,6 +22,7 @@ #include "cross.h" #include #include +#include #include #include #include @@ -115,6 +116,17 @@ AudioAnalysis::AudioAnalysis (boost::filesystem::path filename) } } + /* These may not exist in old analysis files, so be careful + about reading them. + */ + + float peak; + DCPTime::Type peak_time; + if (fscanf (f, "%f%" SCNd64, &peak, &peak_time) == 2) { + _peak = peak; + _peak_time = DCPTime (peak_time); + } + fclose (f); } @@ -164,6 +176,10 @@ AudioAnalysis::write (boost::filesystem::path filename) } } + if (_peak) { + fprintf (f, "%f%" PRId64, _peak.get (), _peak_time.get().get ()); + } + fclose (f); boost::filesystem::rename (tmp, filename); } diff --git a/src/lib/audio_analysis.h b/src/lib/audio_analysis.h index 865d64781..1872c57ad 100644 --- a/src/lib/audio_analysis.h +++ b/src/lib/audio_analysis.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2014 Carl Hetherington + Copyright (C) 2012-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 @@ -24,7 +24,9 @@ #ifndef DCPOMATIC_AUDIO_ANALYSIS_H #define DCPOMATIC_AUDIO_ANALYSIS_H +#include "types.h" #include +#include #include /** @class AudioPoint @@ -69,15 +71,29 @@ public: AudioAnalysis (boost::filesystem::path); void add_point (int c, AudioPoint const & p); + void set_peak (float peak, DCPTime time) { + _peak = peak; + _peak_time = time; + } AudioPoint get_point (int c, int p) const; int points (int c) const; int channels () const; + boost::optional peak () const { + return _peak; + } + + boost::optional peak_time () const { + return _peak_time; + } + void write (boost::filesystem::path); private: std::vector > _data; + boost::optional _peak; + boost::optional _peak_time; }; #endif diff --git a/src/wx/audio_dialog.cc b/src/wx/audio_dialog.cc index 1d41fc185..1ce0f38a8 100644 --- a/src/wx/audio_dialog.cc +++ b/src/wx/audio_dialog.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 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 @@ -29,31 +29,43 @@ using boost::shared_ptr; using boost::bind; using boost::optional; -AudioDialog::AudioDialog (wxWindow* parent) +AudioDialog::AudioDialog (wxWindow* parent, shared_ptr film) : wxDialog (parent, wxID_ANY, _("Audio"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE) + , _film (film) , _plot (0) { + wxFont subheading_font (*wxNORMAL_FONT); + subheading_font.SetWeight (wxFONTWEIGHT_BOLD); + wxBoxSizer* sizer = new wxBoxSizer (wxHORIZONTAL); + + wxBoxSizer* left = new wxBoxSizer (wxVERTICAL); _plot = new AudioPlot (this); - sizer->Add (_plot, 1, wxALL | wxEXPAND, 12); + left->Add (_plot, 1, wxALL | wxEXPAND, 12); + _peak_time = new wxStaticText (this, wxID_ANY, wxT ("")); + left->Add (_peak_time, 0, wxALL, 12); + + sizer->Add (left, 1, wxALL, 12); - wxBoxSizer* side = new wxBoxSizer (wxVERTICAL); + wxBoxSizer* right = new wxBoxSizer (wxVERTICAL); { wxStaticText* m = new wxStaticText (this, wxID_ANY, _("Channels")); - side->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16); + m->SetFont (subheading_font); + right->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 16); } for (int i = 0; i < MAX_DCP_AUDIO_CHANNELS; ++i) { _channel_checkbox[i] = new wxCheckBox (this, wxID_ANY, std_to_wx (audio_channel_name (i))); - side->Add (_channel_checkbox[i], 1, wxEXPAND | wxALL, 3); + right->Add (_channel_checkbox[i], 0, wxEXPAND | wxALL, 3); _channel_checkbox[i]->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AudioDialog::channel_clicked, this, _1)); } { wxStaticText* m = new wxStaticText (this, wxID_ANY, _("Type")); - side->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16); + m->SetFont (subheading_font); + right->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16); } wxString const types[] = { @@ -63,20 +75,21 @@ AudioDialog::AudioDialog (wxWindow* parent) for (int i = 0; i < AudioPoint::COUNT; ++i) { _type_checkbox[i] = new wxCheckBox (this, wxID_ANY, types[i]); - side->Add (_type_checkbox[i], 1, wxEXPAND | wxALL, 3); + right->Add (_type_checkbox[i], 0, wxEXPAND | wxALL, 3); _type_checkbox[i]->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AudioDialog::type_clicked, this, _1)); } { wxStaticText* m = new wxStaticText (this, wxID_ANY, _("Smoothing")); - side->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16); + m->SetFont (subheading_font); + right->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16); } _smoothing = new wxSlider (this, wxID_ANY, AudioPlot::max_smoothing / 2, 1, AudioPlot::max_smoothing); _smoothing->Bind (wxEVT_SCROLL_THUMBTRACK, boost::bind (&AudioDialog::smoothing_changed, this)); - side->Add (_smoothing, 1, wxEXPAND); + right->Add (_smoothing, 0, wxEXPAND); - sizer->Add (side, 0, wxALL, 12); + sizer->Add (right, 0, wxALL, 12); SetSizer (sizer); sizer->Layout (); @@ -107,14 +120,14 @@ AudioDialog::try_to_load_analysis () if (!boost::filesystem::exists (_content->audio_analysis_path())) { _plot->set_analysis (shared_ptr ()); + _analysis.reset (); _analysis_finished_connection = _content->analyse_audio (bind (&AudioDialog::analysis_finished, this)); return; } - shared_ptr a; - - a.reset (new AudioAnalysis (_content->audio_analysis_path ())); - _plot->set_analysis (a); + _analysis.reset (new AudioAnalysis (_content->audio_analysis_path ())); + _plot->set_analysis (_analysis); + setup_peak_time (); /* Set up some defaults if no check boxes are checked */ @@ -173,6 +186,7 @@ AudioDialog::content_changed (int p) { if (p == AudioContentProperty::AUDIO_GAIN) { _plot->set_gain (_content->audio_gain ()); + setup_peak_time (); } else if (p == AudioContentProperty::AUDIO_MAPPING) { try_to_load_analysis (); } @@ -196,3 +210,32 @@ AudioDialog::smoothing_changed () { _plot->set_smoothing (_smoothing->GetValue ()); } + +void +AudioDialog::setup_peak_time () +{ + if (!_analysis || !_analysis->peak ()) { + return; + } + + shared_ptr film = _film.lock (); + if (!film) { + return; + } + + float peak_dB = 20 * log10 (_analysis->peak().get()) + _content->audio_gain(); + + _peak_time->SetLabel ( + wxString::Format ( + _("Peak is %.2fdB at %s"), + peak_dB, + time_to_timecode (_analysis->peak_time().get(), film->video_frame_rate ()).data () + ) + ); + + if (peak_dB > -3) { + _peak_time->SetForegroundColour (wxColour (255, 0, 0)); + } else { + _peak_time->SetForegroundColour (wxColour (0, 0, 0)); + } +} diff --git a/src/wx/audio_dialog.h b/src/wx/audio_dialog.h index b27785292..aef8ea944 100644 --- a/src/wx/audio_dialog.h +++ b/src/wx/audio_dialog.h @@ -29,7 +29,7 @@ class Film; class AudioDialog : public wxDialog { public: - AudioDialog (wxWindow *); + AudioDialog (wxWindow *, boost::shared_ptr film); void set_content (boost::shared_ptr); @@ -40,9 +40,13 @@ private: void smoothing_changed (); void try_to_load_analysis (); void analysis_finished (); + void setup_peak_time (); boost::shared_ptr _content; + boost::shared_ptr _analysis; + boost::weak_ptr _film; AudioPlot* _plot; + wxStaticText* _peak_time; wxCheckBox* _channel_checkbox[MAX_DCP_AUDIO_CHANNELS]; wxCheckBox* _type_checkbox[AudioPoint::COUNT]; wxSlider* _smoothing; diff --git a/src/wx/audio_panel.cc b/src/wx/audio_panel.cc index 2a41aeb2d..4d783ca9d 100644 --- a/src/wx/audio_panel.cc +++ b/src/wx/audio_panel.cc @@ -217,7 +217,7 @@ AudioPanel::show_clicked () return; } - _audio_dialog = new AudioDialog (this); + _audio_dialog = new AudioDialog (this, _parent->film ()); _audio_dialog->Show (); _audio_dialog->set_content (ac.front ()); } diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 311ec734c..0938d52a4 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -354,16 +354,7 @@ FilmViewer::set_position_text () double const fps = _film->video_frame_rate (); /* Count frame number from 1 ... not sure if this is the best idea */ _frame_number->SetLabel (wxString::Format (wxT("%d"), int (rint (_position.seconds() * fps)) + 1)); - - double w = _position.seconds (); - int const h = (w / 3600); - w -= h * 3600; - int const m = (w / 60); - w -= m * 60; - int const s = floor (w); - w -= s; - int const f = rint (w * fps); - _timecode->SetLabel (wxString::Format (wxT("%02d:%02d:%02d.%02d"), h, m, s, f)); + _timecode->SetLabel (time_to_timecode (_position, fps)); } void diff --git a/src/wx/wx_util.cc b/src/wx/wx_util.cc index e9c07c91f..9c13cfbcb 100644 --- a/src/wx/wx_util.cc +++ b/src/wx/wx_util.cc @@ -330,3 +330,17 @@ context_translation (wxString s) return t; } + +wxString +time_to_timecode (DCPTime t, float fps) +{ + double w = t.seconds (); + int const h = (w / 3600); + w -= h * 3600; + int const m = (w / 60); + w -= m * 60; + int const s = floor (w); + w -= s; + int const f = rint (w * fps); + return wxString::Format (wxT("%02d:%02d:%02d.%02d"), h, m, s, f); +} diff --git a/src/wx/wx_util.h b/src/wx/wx_util.h index dfa0fca5e..f2ab2d8c5 100644 --- a/src/wx/wx_util.h +++ b/src/wx/wx_util.h @@ -24,6 +24,7 @@ #ifndef DCPOMATIC_WX_UTIL_H #define DCPOMATIC_WX_UTIL_H +#include "lib/dcpomatic_time.h" #include #include #include @@ -65,6 +66,7 @@ extern wxString std_to_wx (std::string); extern void dcpomatic_setup_i18n (); extern wxString context_translation (wxString); extern std::string string_client_data (wxClientData* o); +extern wxString time_to_timecode (DCPTime t, float fps); extern void checked_set (wxFilePickerCtrl* widget, std::string value); extern void checked_set (wxSpinCtrl* widget, int value); -- cgit v1.2.3 From 3bd44601d084d7139a81e787bd72fdf80832f2c3 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 10 May 2015 01:59:37 +0100 Subject: Possibly inaccurate port of master; build system cleanups. --- TO_PORT | 1 - cscript | 159 ++++++++---- debian/rules | 14 +- platform/linux/dcpomatic.spec.in | 74 ------ platform/linux/wscript | 10 - src/lib/wscript | 22 +- src/tools/wscript | 23 +- src/wscript | 18 ++ src/wx/wscript | 64 +++-- test/image_test.cc | 61 ----- test/wscript | 18 ++ wscript | 525 ++++++++++++++++++++------------------- 12 files changed, 509 insertions(+), 480 deletions(-) delete mode 100644 platform/linux/dcpomatic.spec.in (limited to 'src/lib') diff --git a/TO_PORT b/TO_PORT index b1dcf5216..e69de29bb 100644 --- a/TO_PORT +++ b/TO_PORT @@ -1 +0,0 @@ -wscript/cscript etc. cleanups diff --git a/cscript b/cscript index 3864fbaa1..5a74e8038 100644 --- a/cscript +++ b/cscript @@ -1,3 +1,21 @@ +# +# Copyright (C) 2012-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 +# 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, +# 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. +# + import glob import shutil import os @@ -26,48 +44,6 @@ deb_depends['12.04'] = {'libc6': '2.15', 'libcurl3': '7.22.0-3ubuntu4', 'libzip2': '0.10-1ubuntu1'} -deb_depends['12.10'] = {'libc6': '2.15', - 'libssh-4': '0.5.2', - 'libboost-filesystem1.49.0': '1.49.0', - 'libboost-thread1.49.0': '1.49.0', - 'libsndfile1': '1.0.25', - 'libmagick++5': '8:6.7.7.10', - 'libxml++2.6-2': '2.34.2', - 'libgtk2.0-0': '2.24.13', - 'libxmlsec1': '1.2.18-2', - 'libxmlsec1-openssl': '1.2.18-2', - 'libboost-date-time1.49.0': '1.49.0', - 'libcurl3': '7.27.0-1ubuntu1', - 'libzip2': '0.10.1-1.1'} - -deb_depends['13.04'] = {'libc6': '2.15', - 'libssh-4': '0.5.2', - 'libboost-filesystem1.49.0': '1.49.0', - 'libboost-thread1.49.0': '1.49.0', - 'libsndfile1': '1.0.25', - 'libmagick++5': '8:6.7.7.10', - 'libxml++2.6-2': '2.34.2', - 'libgtk2.0-0': '2.24.13', - 'libxmlsec1': '1.2.18-2', - 'libxmlsec1-openssl': '1.2.18-2', - 'libboost-date-time1.49.0': '1.49.0', - 'libcurl3': '7.29.0-1ubuntu3', - 'libzip2': '0.10.1-1.1'} - -deb_depends['13.10'] = {'libc6': '2.17-93', - 'libssh-4': '0.5.4', - 'libboost-filesystem1.53.0': '1.53.0', - 'libboost-thread1.53.0': '1.53.0', - 'libsndfile1': '1.0.25', - 'libmagick++5': '8:6.7.7.10', - 'libxml++2.6-2': '2.36.0', - 'libgtk2.0-0': '2.24.20', - 'libxmlsec1': '1.2.18-2', - 'libxmlsec1-openssl': '1.2.18-2', - 'libboost-date-time1.49.0': '1.49.0', - 'libcurl3': '7.29.0-1ubuntu3', - 'libzip2': '0.10.1-1.1'} - deb_depends['14.04'] = {'libc6': '2.19-0ubuntu6', 'libssh-4': '0.6.1-0ubuntu3', 'libboost-filesystem1.54.0': '1.54.0-4ubuntu3', @@ -82,6 +58,20 @@ deb_depends['14.04'] = {'libc6': '2.19-0ubuntu6', 'libcurl3': '7.35.0-1ubuntu2', 'libzip2': '0.10.1-1.2'} +deb_depends['15.04'] = {'libc6': '2.21-0ubuntu4', + 'libssh-4': '0.6.3-3ubuntu3', + 'libboost-filesystem1.55.0': '1.55.0+dfsg-3ubuntu2', + 'libboost-thread1.55.0': '1.55.0+dfsg-3ubuntu2', + 'libsndfile1': '1.0.25-9.1', + 'libmagick++-6.q16-5': '8:6.8.9.9-5', + 'libxml++2.6-2': '2.36.0-2.1', + 'libgtk2.0-0': '2.24.27-0ubuntu1', + 'libxmlsec1': '1.2.20-2ubuntu2', + 'libxmlsec1-openssl': '1.2.20-2ubuntu2', + 'libboost-date-time1.55.0': '1.55.0+dfsg-3ubuntu2', + 'libcurl3': '7.38.0-3ubuntu2', + 'libzip2': '0.11.2-1.2'} + deb_depends['7'] = {'libc6': '2.13', 'libssh-4': '0.5.4', 'libboost-filesystem1.49.0': '1.49.0', @@ -159,24 +149,85 @@ def dependencies(target): ('libdcp', '8e7f9e4'), ('libsub', 'f66b11f')) -def build(target, options): - cmd = './waf configure --prefix=%s' % target.directory +def configure_options(target): + opt = '' if target.debug: - cmd += ' --enable-debug' + opt += ' --enable-debug' if target.platform == 'windows': - cmd += ' --target-windows' + opt += ' --target-windows' elif target.platform == 'linux': - if target.distro == 'debian' or target.distro == 'ubuntu': - cmd += ' --target-debian' - if target.version == 'unstable': - cmd += ' --debian-unstable' - elif target.distro == 'centos': + opt += ' --static-dcpomatic --static-openjpeg --static-wxwidgets --static-ffmpeg --static-dcp --static-sub --static-cxml' + if target.distro == 'centos': + opt += ' --static-xmlsec --static-ssh --disable-tests' if target.version == '6.5': - cmd += ' --target-centos-6 --disable-tests' + opt += ' --static-boost --static-xmlpp' elif target.version == '7': - cmd += ' --target-centos-7 --disable-tests' + opt += ' --workaround-gssapi' - target.command(cmd) + return opt + +def make_spec(filename, version, target): + """Make a .spec file for a RPM build""" + f = open(filename, 'w') + print >>f,'Summary:A program that generates Digital Cinema Packages (DCPs) from video and audio files' + print >>f,'Name:dcpomatic' + print >>f,'Version:%s' % version + print >>f,'Release:1%{?dist}' + print >>f,'License:GPL' + print >>f,'Group:Applications/Multimedia' + print >>f,'URL:http://dcpomatic.com/' + print >>f,'Requires: ImageMagick-c++, glibmm24, libzip' + print >>f,'' + print >>f,'%description' + print >>f,'DCP-o-matic generates Digital Cinema Packages (DCPs) from video and audio ' + print >>f,'files (such as those from DVDs or Blu-Rays) for presentation on DCI-compliant ' + print >>f,'digital projectors.' + print >>f,'' + print >>f,'%files' + print >>f,'%{_bindir}/dcpomatic2' + print >>f,'%{_bindir}/dcpomatic2_batch' + print >>f,'%{_bindir}/dcpomatic2_cli' + print >>f,'%{_bindir}/dcpomatic2_create' + print >>f,'%{_bindir}/dcpomatic2_kdm' + print >>f,'%{_bindir}/dcpomatic2_server' + print >>f,'%{_bindir}/dcpomatic2_server_cli' + print >>f,'%{_datadir}/applications/dcpomatic2.desktop' + print >>f,'%{_datadir}/applications/dcpomatic2_batch.desktop' + print >>f,'%{_datadir}/applications/dcpomatic2_server.desktop' + print >>f,'%{_datadir}/dcpomatic2/taskbar_icon.png' + for r in ['128x128', '22x22', '32x32', '48x48', '64x64']: + print >>f,'%%{_datadir}/icons/hicolor/%s/apps/dcpomatic2.png' % r + for l in ['de_DE', 'es_ES', 'fr_FR', 'it_IT', 'sv_SE', 'nl_NL', 'ru_RU']: + print >>f,'%%{_datadir}/locale/%s/LC_MESSAGES/dcpomatic2.mo' % l + print >>f,'%%{_datadir}/locale/%s/LC_MESSAGES/libdcpomatic2-wx.mo' % l + print >>f,'%%{_datadir}/locale/%s/LC_MESSAGES/libdcpomatic2.mo' % l + print >>f,'' + print >>f,'%prep' + print >>f,'rm -rf $RPM_BUILD_DIR/dcpomatic-%s' % version + print >>f,'tar xjf $RPM_SOURCE_DIR/dcpomatic-%s.tar.bz2' % version + print >>f,'%build' + print >>f,'cd dcpomatic-%s' % version + print >>f,'export PKG_CONFIG_PATH=%s/lib/pkgconfig:/usr/local/lib/pkgconfig' % target.directory + print >>f,'CXXFLAGS="-I%s/include" LDFLAGS="-L%s/lib" ./waf configure --prefix=%%{buildroot}/usr --install-prefix=/usr %s' % (target.directory, target.directory, configure_options(target)) + print >>f,'./waf' + print >>f,'%install' + print >>f,'cd dcpomatic-%s' % version + print >>f,'./waf install' + print >>f,'' + print >>f,'%post' + print >>f,'/bin/touch --no-create %{_datadir}/icons/hicolor &>/dev/null || :' + print >>f,'' + print >>f,'%postun' + print >>f,'if [ $1 -eq 0 ] ; then' + print >>f,' /bin/touch --no-create %{_datadir}/icons/hicolor &>/dev/null' + print >>f,' /usr/bin/gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :' + print >>f,'fi' + print >>f,'' + print >>f,'%posttrans' + print >>f,'/usr/bin/gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :' + +def build(target, options): + target.command('./waf configure --prefix=%s %s' % (target.directory, configure_options(target))) target.command('./waf') if target.platform == 'linux' or target.platform == 'osx': diff --git a/debian/rules b/debian/rules index b51089f8b..3e75b0090 100755 --- a/debian/rules +++ b/debian/rules @@ -14,7 +14,7 @@ override_dh_auto_configure: LINKFLAGS=$(CDIST_LINKFLAGS) CXXFLAGS="$(CXXFLAGS) $(CDIST_CXXFLAGS)" PKG_CONFIG_PATH=$(CDIST_PKG_CONFIG_PATH) \ - ./waf configure --prefix=/usr --target-debian --enable-debug $(CDIST_EXTRA_CONFIGURE) + ./waf configure --prefix=/usr $(CDIST_CONFIGURE) override_dh_auto_build: ./waf build @@ -22,17 +22,17 @@ override_dh_auto_build: override_dh_auto_install: ./waf install --destdir=debian/dcpomatic mkdir -p debian/dcpomatic/usr/share/locale/de/LC_MESSAGES/ - cp -a /usr/local/share/locale/de/LC_MESSAGES/wxstd.mo debian/dcpomatic/usr/share/locale/de/LC_MESSAGES/dcpomatic-wxstd.mo + cp -a $(CDIST_LOCALE_PREFIX)/de/LC_MESSAGES/wxstd.mo debian/dcpomatic/usr/share/locale/de/LC_MESSAGES/dcpomatic-wxstd.mo mkdir -p debian/dcpomatic/usr/share/locale/es/LC_MESSAGES/ - cp -a /usr/local/share/locale/es/LC_MESSAGES/wxstd.mo debian/dcpomatic/usr/share/locale/es/LC_MESSAGES/dcpomatic-wxstd.mo + cp -a $(CDIST_LOCALE_PREFIX)/es/LC_MESSAGES/wxstd.mo debian/dcpomatic/usr/share/locale/es/LC_MESSAGES/dcpomatic-wxstd.mo mkdir -p debian/dcpomatic/usr/share/locale/fr/LC_MESSAGES/ - cp -a /usr/local/share/locale/fr/LC_MESSAGES/wxstd.mo debian/dcpomatic/usr/share/locale/fr/LC_MESSAGES/dcpomatic-wxstd.mo + cp -a $(CDIST_LOCALE_PREFIX)/fr/LC_MESSAGES/wxstd.mo debian/dcpomatic/usr/share/locale/fr/LC_MESSAGES/dcpomatic-wxstd.mo mkdir -p debian/dcpomatic/usr/share/locale/it/LC_MESSAGES/ - cp -a /usr/local/share/locale/it/LC_MESSAGES/wxstd.mo debian/dcpomatic/usr/share/locale/it/LC_MESSAGES/dcpomatic-wxstd.mo + cp -a $(CDIST_LOCALE_PREFIX)/it/LC_MESSAGES/wxstd.mo debian/dcpomatic/usr/share/locale/it/LC_MESSAGES/dcpomatic-wxstd.mo mkdir -p debian/dcpomatic/usr/share/locale/sv/LC_MESSAGES/ - cp -a /usr/local/share/locale/sv/LC_MESSAGES/wxstd.mo debian/dcpomatic/usr/share/locale/sv/LC_MESSAGES/dcpomatic-wxstd.mo + cp -a $(CDIST_LOCALE_PREFIX)/sv/LC_MESSAGES/wxstd.mo debian/dcpomatic/usr/share/locale/sv/LC_MESSAGES/dcpomatic-wxstd.mo mkdir -p debian/dcpomatic/usr/share/locale/nl/LC_MESSAGES/ - cp -a /usr/local/share/locale/nl/LC_MESSAGES/wxstd.mo debian/dcpomatic/usr/share/locale/nl/LC_MESSAGES/dcpomatic-wxstd.mo + cp -a $(CDIST_LOCALE_PREFIX)/nl/LC_MESSAGES/wxstd.mo debian/dcpomatic/usr/share/locale/nl/LC_MESSAGES/dcpomatic-wxstd.mo .PHONY: override_dh_strip override_dh_strip: diff --git a/platform/linux/dcpomatic.spec.in b/platform/linux/dcpomatic.spec.in deleted file mode 100644 index 7f39716d4..000000000 --- a/platform/linux/dcpomatic.spec.in +++ /dev/null @@ -1,74 +0,0 @@ -Summary:A program that generates Digital Cinema Packages (DCPs) from video and audio files -Name:dcpomatic2 -Version:@VERSION@ -Release:1%{?dist} -License:GPL -Group:Applications/Multimedia -URL:http://dcpomatic.com/ -Requires: ImageMagick-c++, glibmm24, libzip - -%description -DCP-o-matic generates Digital Cinema Packages (DCPs) from video and audio -files (such as those from DVDs or Blu-Rays) for presentation on DCI-compliant -digital projectors. - -%files -%{_bindir}/dcpomatic2 -%{_bindir}/dcpomatic2_batch -%{_bindir}/dcpomatic2_cli -%{_bindir}/dcpomatic2_create -%{_bindir}/dcpomatic2_kdm -%{_bindir}/dcpomatic2_server -%{_bindir}/dcpomatic2_server_cli -%{_datadir}/applications/dcpomatic2.desktop -%{_datadir}/applications/dcpomatic2_batch.desktop -%{_datadir}/applications/dcpomatic2_server.desktop -%{_datadir}/dcpomatic2/taskbar_icon.png -%{_datadir}/dcpomatic2/LiberationSans-Regular.ttf -%{_datadir}/icons/hicolor/128x128/apps/dcpomatic2.png -%{_datadir}/icons/hicolor/22x22/apps/dcpomatic2.png -%{_datadir}/icons/hicolor/32x32/apps/dcpomatic2.png -%{_datadir}/icons/hicolor/48x48/apps/dcpomatic2.png -%{_datadir}/icons/hicolor/64x64/apps/dcpomatic2.png -%{_datadir}/locale/de_DE/LC_MESSAGES/dcpomatic2.mo -%{_datadir}/locale/de_DE/LC_MESSAGES/libdcpomatic2-wx.mo -%{_datadir}/locale/de_DE/LC_MESSAGES/libdcpomatic2.mo -%{_datadir}/locale/es_ES/LC_MESSAGES/dcpomatic2.mo -%{_datadir}/locale/es_ES/LC_MESSAGES/libdcpomatic2-wx.mo -%{_datadir}/locale/es_ES/LC_MESSAGES/libdcpomatic2.mo -%{_datadir}/locale/fr_FR/LC_MESSAGES/dcpomatic2.mo -%{_datadir}/locale/fr_FR/LC_MESSAGES/libdcpomatic2-wx.mo -%{_datadir}/locale/fr_FR/LC_MESSAGES/libdcpomatic2.mo -%{_datadir}/locale/it_IT/LC_MESSAGES/dcpomatic2.mo -%{_datadir}/locale/it_IT/LC_MESSAGES/libdcpomatic2-wx.mo -%{_datadir}/locale/it_IT/LC_MESSAGES/libdcpomatic2.mo -%{_datadir}/locale/sv_SE/LC_MESSAGES/dcpomatic2.mo -%{_datadir}/locale/sv_SE/LC_MESSAGES/libdcpomatic2-wx.mo -%{_datadir}/locale/sv_SE/LC_MESSAGES/libdcpomatic2.mo -%{_datadir}/locale/nl_NL/LC_MESSAGES/dcpomatic2.mo -%{_datadir}/locale/nl_NL/LC_MESSAGES/libdcpomatic2-wx.mo -%{_datadir}/locale/nl_NL/LC_MESSAGES/libdcpomatic2.mo - -%prep -rm -rf $RPM_BUILD_DIR/dcpomatic-@VERSION@ -tar xjf $RPM_SOURCE_DIR/dcpomatic-@VERSION@.tar.bz2 -%build -cd dcpomatic-@VERSION@ -export PKG_CONFIG_PATH=@INSTALL_PREFIX@/lib/pkgconfig:/usr/local/lib/pkgconfig -CXXFLAGS="-I@INSTALL_PREFIX@/include" LDFLAGS="-L@INSTALL_PREFIX@/lib" ./waf configure --prefix=%{buildroot}/usr --install-prefix=/usr --target-centos-@CENTOS_VERSION@ --disable-tests -./waf -%install -cd dcpomatic-@VERSION@ -./waf install - -%post -/bin/touch --no-create %{_datadir}/icons/hicolor &>/dev/null || : - -%postun -if [ $1 -eq 0 ] ; then - /bin/touch --no-create %{_datadir}/icons/hicolor &>/dev/null - /usr/bin/gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : -fi - -%posttrans -/usr/bin/gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : diff --git a/platform/linux/wscript b/platform/linux/wscript index 336c1bcb0..5f1dc48a2 100644 --- a/platform/linux/wscript +++ b/platform/linux/wscript @@ -17,14 +17,4 @@ def build(bld): obj.INSTALL_PREFIX = bld.env.INSTALL_PREFIX obj.VERSION = bld.env.VERSION - obj = bld(features='subst') - obj.source = 'dcpomatic.spec.in' - obj.target = 'dcpomatic2.spec' - obj.INSTALL_PREFIX = bld.env.INSTALL_PREFIX - obj.VERSION = bld.env.VERSION - if bld.env.TARGET_CENTOS_6: - obj.CENTOS_VERSION = '6' - elif bld.env.TARGET_CENTOS_7: - obj.CENTOS_VERSION = '7' - bld.install_files('${PREFIX}/share/applications', ['dcpomatic2.desktop', 'dcpomatic2_batch.desktop', 'dcpomatic2_server.desktop']) diff --git a/src/lib/wscript b/src/lib/wscript index 24aa7c134..68897a4c4 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -1,3 +1,21 @@ +# +# Copyright (C) 2012-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 +# 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, +# 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. +# + import os import i18n @@ -102,7 +120,7 @@ sources = """ """ def build(bld): - if bld.env.BUILD_STATIC: + if bld.env.STATIC_DCPOMATIC: obj = bld(features = 'cxx cxxstlib') else: obj = bld(features = 'cxx cxxshlib') @@ -123,7 +141,7 @@ def build(bld): if bld.env.TARGET_WINDOWS: obj.uselib += ' WINSOCK2 BFD DBGHELP IBERTY SHLWAPI MSWSOCK BOOST_LOCALE' - if bld.env.BUILD_STATIC: + if bld.env.STATIC_DCPOMATIC: obj.uselib += ' XMLPP' obj.target = 'dcpomatic2' diff --git a/src/tools/wscript b/src/tools/wscript index 175cebc96..42aea9562 100644 --- a/src/tools/wscript +++ b/src/tools/wscript @@ -1,3 +1,21 @@ +# +# Copyright (C) 2012-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 +# 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, +# 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. +# + import os import glob from waflib import Logs @@ -9,9 +27,8 @@ def configure(conf): conf.env.append_value('LINKFLAGS', ['-mconsole']) def build(bld): - - uselib = 'BOOST_THREAD BOOST_DATETIME BOOST_FILESYSTEM OPENJPEG DCP CXML SNDFILE ZIP XMLPP SSH ' - uselib += 'AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC WXWIDGETS SUB CURL GLIB CAIROMM PANGOMM MAGICK ' + uselib = 'BOOST_THREAD BOOST_DATETIME OPENJPEG DCP XMLSEC CXML XMLPP AVFORMAT AVFILTER AVCODEC ' + uselib += 'AVUTIL SWSCALE POSTPROC CURL BOOST_FILESYSTEM SSH WXWIDGETS' if bld.env.TARGET_WINDOWS: uselib += 'WINSOCK2' diff --git a/src/wscript b/src/wscript index f3a6a8c7b..8ec39963f 100644 --- a/src/wscript +++ b/src/wscript @@ -1,3 +1,21 @@ +# +# Copyright (C) 2012-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 +# 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, +# 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. +# + def configure(conf): conf.recurse('tools') if not conf.env.DISABLE_GUI: diff --git a/src/wx/wscript b/src/wx/wscript index 7b5904b4b..a05774cd2 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -1,4 +1,24 @@ +# +# Copyright (C) 2012-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 +# 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, +# 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. +# + import os +import subprocess +import shlex import glob from waflib import Logs import i18n @@ -64,34 +84,40 @@ sources = """ """ def configure(conf): - args = '--cppflags --cxxflags' - if not conf.env.BUILD_STATIC: - args += ' --libs std,richtext' - - conf.check_cfg(msg='Checking for wxWidgets', package='', path=conf.options.wx_config, args=args, - uselib_store='WXWIDGETS', mandatory=True) + conf.check_cfg(msg='Checking for wxWidgets', + package='', + path='wx-config', + args='--cppflags --cxxflags --libs std,richtext', + uselib_store='WXWIDGETS', + mandatory=True) - if conf.env.BUILD_STATIC: + if conf.options.static_wxwidgets: # wx-config returns its static libraries as full paths, without -l prefixes, which confuses - # check_cfg(), so just hard-code it all. - conf.env.STLIB_WXWIDGETS = ['wx_gtk2u_richtext-3.0', 'wx_gtk2u_xrc-3.0', 'wx_gtk2u_qa-3.0', 'wx_baseu_net-3.0', 'wx_gtk2u_html-3.0', - 'wx_gtk2u_adv-3.0', 'wx_gtk2u_core-3.0', 'wx_baseu_xml-3.0', 'wx_baseu-3.0'] - conf.env.LIB_WXWIDGETS = ['tiff', 'SM', 'dl', 'jpeg', 'png', 'X11', 'expat'] - if conf.env.TARGET_DEBIAN and conf.env.DEBIAN_UNSTABLE: - conf.env.LIB_WXWIDGETS.append('Xext') - conf.env.LIB_WXWIDGETS.append('X11') - - if conf.env.TARGET_CENTOS_7: - conf.env.LIB_WXWIDGETS.append('Xxf86vm') + # check_cfg(). It puts the static libraries into LINKFLAGS_WXWIDGETS, so fish them out. + stlibs = [] + new_linkflags = [] + stlib_paths = [] + for f in conf.env.LINKFLAGS_WXWIDGETS: + if f.startswith('/'): + d = os.path.dirname(f) + if not d in stlib_paths: + stlib_paths.append(d) + stlibs.append(os.path.basename(f)[3:-2]) + else: + new_linkflags.append(f) + + conf.env.STLIB_WXWIDGETS = stlibs + conf.env.LINKFLAGS_WXWIDGETS = new_linkflags + conf.env.STLIBPATH_WXWIDGETS = stlib_paths conf.in_msg = 1 - wx_version = conf.check_cfg(package='', path=conf.options.wx_config, args='--version').strip() + wx_version = conf.check_cfg(package='', path='wx-config', args='--version').strip() conf.im_msg = 0 if not wx_version.startswith('3.0.'): conf.fatal('wxwidgets version 3.0.x is required; %s found' % wx_version) def build(bld): - if bld.env.BUILD_STATIC: + if bld.env.STATIC_DCPOMATIC: obj = bld(features = 'cxx cxxstlib') else: obj = bld(features = 'cxx cxxshlib') diff --git a/test/image_test.cc b/test/image_test.cc index 9aca9878d..5561f021c 100644 --- a/test/image_test.cc +++ b/test/image_test.cc @@ -171,67 +171,6 @@ BOOST_AUTO_TEST_CASE (crop_image_test2) } } -static -boost::shared_ptr -read_file (string file) -{ - Magick::Image magick_image (file.c_str ()); - dcp::Size size (magick_image.columns(), magick_image.rows()); - - boost::shared_ptr image (new Image (PIX_FMT_RGB24, size, true)); - -#ifdef DCPOMATIC_IMAGE_MAGICK - using namespace MagickCore; -#endif - - uint8_t* p = image->data()[0]; - for (int y = 0; y < size.height; ++y) { - uint8_t* q = p; - for (int x = 0; x < size.width; ++x) { - Magick::Color c = magick_image.pixelColor (x, y); -#ifdef DCPOMATIC_IMAGE_MAGICK - *q++ = c.redQuantum() * 255 / QuantumRange; - *q++ = c.greenQuantum() * 255 / QuantumRange; - *q++ = c.blueQuantum() * 255 / QuantumRange; -#else - *q++ = c.redQuantum() * 255 / MaxRGB; - *q++ = c.greenQuantum() * 255 / MaxRGB; - *q++ = c.blueQuantum() * 255 / MaxRGB; -#endif - } - p += image->stride()[0]; - } - - return image; -} - -static -void -write_file (shared_ptr image, string file) -{ -#ifdef DCPOMATIC_IMAGE_MAGICK - using namespace MagickCore; -#endif - - Magick::Image magick_image (Magick::Geometry (image->size().width, image->size().height), Magick::Color (0, 0, 0)); - uint8_t*p = image->data()[0]; - for (int y = 0; y < image->size().height; ++y) { - uint8_t* q = p; - for (int x = 0; x < image->size().width; ++x) { -#ifdef DCPOMATIC_IMAGE_MAGICK - Magick::Color c (q[0] * QuantumRange / 256, q[1] * QuantumRange / 256, q[2] * QuantumRange / 256); -#else - Magick::Color c (q[0] * MaxRGB / 256, q[1] * MaxRGB / 256, q[2] * MaxRGB / 256); -#endif - magick_image.pixelColor (x, y, c); - q += 3; - } - p += image->stride()[0]; - } - - magick_image.write (file.c_str ()); -} - static void crop_scale_window_single (AVPixelFormat in_format, dcp::Size in_size, Crop crop, dcp::Size inter_size, dcp::Size out_size) diff --git a/test/wscript b/test/wscript index ffc93ea5e..a5556e25d 100644 --- a/test/wscript +++ b/test/wscript @@ -1,3 +1,21 @@ +# +# Copyright (C) 2012-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 +# 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, +# 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. +# + def configure(conf): boost_test_suffix='' if conf.env.TARGET_WINDOWS: diff --git a/wscript b/wscript index 644d69b8d..c19c717ce 100644 --- a/wscript +++ b/wscript @@ -1,8 +1,28 @@ +# +# Copyright (C) 2012-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 +# 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, +# 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. +# + import subprocess import os +import shlex import sys import distutils import distutils.spawn +from waflib import Logs APPNAME = 'dcpomatic' VERSION = '2.0.43devel' @@ -14,188 +34,61 @@ def options(opt): opt.add_option('--enable-debug', action='store_true', default=False, help='build with debugging information and without optimisation') opt.add_option('--disable-gui', action='store_true', default=False, help='disable building of GUI tools') opt.add_option('--disable-tests', action='store_true', default=False, help='disable building of tests') - opt.add_option('--target-windows', action='store_true', default=False, help='set up to do a cross-compile to make a Windows package') - opt.add_option('--target-debian', action='store_true', default=False, help='set up to compile for a Debian/Ubuntu package') - opt.add_option('--debian-unstable', action='store_true', default=False, help='add extra libraries to static-build correctly on Debian unstable') - opt.add_option('--target-centos-6', action='store_true', default=False, help='set up to compile for a Centos 6.5 package') - opt.add_option('--target-centos-7', action='store_true', default=False, help='set up to compile for a Centos 7 package') - opt.add_option('--magickpp-config', action='store', default='Magick++-config', help='path to Magick++-config') - opt.add_option('--wx-config', action='store', default='wx-config', help='path to wx-config') - opt.add_option('--address-sanitizer', action='store_true', default=False, help='build with address sanitizer') opt.add_option('--install-prefix', default=None, help='prefix of where DCP-o-matic will be installed') - -def static_ffmpeg(conf): - conf.check_cfg(package='libavformat', args='--cflags', uselib_store='AVFORMAT', mandatory=True) - conf.env.STLIB_AVFORMAT = ['avformat'] - conf.check_cfg(package='libavfilter', args='--cflags', uselib_store='AVFILTER', mandatory=True) - conf.env.STLIB_AVFILTER = ['avfilter', 'swresample'] - conf.check_cfg(package='libavcodec', args='--cflags', uselib_store='AVCODEC', mandatory=True) - # lzma link is needed by Centos 7, at least - conf.env.STLIB_AVCODEC = ['avcodec'] - conf.env.LIB_AVCODEC = ['z', 'lzma'] - conf.check_cfg(package='libavutil', args='--cflags', uselib_store='AVUTIL', mandatory=True) - conf.env.STLIB_AVUTIL = ['avutil'] - conf.check_cfg(package='libswscale', args='--cflags', uselib_store='SWSCALE', mandatory=True) - conf.env.STLIB_SWSCALE = ['swscale'] - conf.check_cfg(package='libswresample', args='--cflags', uselib_store='SWRESAMPLE', mandatory=True) - conf.env.STLIB_SWRESAMPLE = ['swresample'] - conf.check_cfg(package='libpostproc', args='--cflags', uselib_store='POSTPROC', mandatory=True) - conf.env.STLIB_POSTPROC = ['postproc'] - -def dynamic_ffmpeg(conf): - conf.check_cfg(package='libavformat', args='--cflags --libs', uselib_store='AVFORMAT', mandatory=True) - conf.check_cfg(package='libavfilter', args='--cflags --libs', uselib_store='AVFILTER', mandatory=True) - conf.check_cfg(package='libavcodec', args='--cflags --libs', uselib_store='AVCODEC', mandatory=True) - conf.check_cfg(package='libavutil', args='--cflags --libs', uselib_store='AVUTIL', mandatory=True) - conf.check_cfg(package='libswscale', args='--cflags --libs', uselib_store='SWSCALE', mandatory=True) - conf.check_cfg(package='libswresample', args='--cflags --libs', uselib_store='SWRESAMPLE', mandatory=True) - conf.check_cfg(package='libpostproc', args='--cflags --libs', uselib_store='POSTPROC', mandatory=True) - -def static_openjpeg(conf): - conf.check_cfg(package='libopenjpeg', args='--cflags', atleast_version='1.5.0', uselib_store='OPENJPEG', mandatory=True) - conf.check_cfg(package='libopenjpeg', args='--cflags', max_version='1.5.2', mandatory=True) - conf.env.STLIB_OPENJPEG = ['openjpeg'] - -def dynamic_openjpeg(conf): - conf.check_cfg(package='libopenjpeg', args='--cflags --libs', atleast_version='1.5.0', uselib_store='OPENJPEG', mandatory=True) - conf.check_cfg(package='libopenjpeg', args='--cflags --libs', max_version='1.5.2', mandatory=True) - -def static_sub(conf): - conf.check_cfg(package='libsub-1.0', atleast_version='1.0.0', args='--cflags', uselib_store='SUB', mandatory=True) - conf.env.DEFINES_SUB = [f.replace('\\', '') for f in conf.env.DEFINES_SUB] - conf.env.STLIB_SUB = ['sub-1.0'] - -def static_dcp(conf, static_boost, static_xmlpp, static_xmlsec, static_ssh): - conf.check_cfg(package='libdcp-1.0', atleast_version='1.0', args='--cflags', uselib_store='DCP', mandatory=True) - conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP] - conf.env.STLIB_DCP = ['dcp-1.0', 'asdcp-libdcp-1.0', 'kumu-libdcp-1.0'] - conf.env.LIB_DCP = ['glibmm-2.4', 'ssl', 'crypto', 'bz2', 'xslt'] - - if static_boost: - conf.env.STLIB_DCP.append('boost_system') - - if static_xmlpp: - conf.env.STLIB_DCP.append('xml++-2.6') - else: - conf.env.LIB_DCP.append('xml++-2.6') - - if static_xmlsec: - conf.env.STLIB_DCP.append('xmlsec1-openssl') - conf.env.STLIB_DCP.append('xmlsec1') - else: - conf.env.LIB_DCP.append('xmlsec1-openssl') - conf.env.LIB_DCP.append('xmlsec1') - - if static_ssh: - conf.env.STLIB_DCP.append('ssh') - else: - conf.env.LIB_DCP.append('ssh') - -def dynamic_dcp(conf): - conf.check_cfg(package='libdcp-1.0', atleast_version='0.92', args='--cflags --libs', uselib_store='DCP', mandatory=True) - conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP] - -def dynamic_sub(conf): - conf.check_cfg(package='libsub-1.0', atleast_version='1.0.0', args='--cflags --libs', uselib_store='SUB', mandatory=True) - conf.env.DEFINES_SUB = [f.replace('\\', '') for f in conf.env.DEFINES_SUB] - -def dynamic_ssh(conf): - conf.check_cc(fragment=""" - #include \n - int main () {\n - ssh_session s = ssh_new ();\n - return 0;\n - } - """, msg='Checking for library libssh', mandatory=True, lib='ssh', uselib_store='SSH') - -def dynamic_boost(conf, lib_suffix, thread): - conf.check_cxx(fragment=""" - #include \n - #if BOOST_VERSION < 104500\n - #error boost too old\n - #endif\n - int main(void) { return 0; }\n - """, - mandatory=True, - msg='Checking for boost library >= 1.45', - okmsg='yes', - errmsg='too old\nPlease install boost version 1.45 or higher.') - - conf.check_cxx(fragment=""" - #include \n - int main() { boost::thread t (); }\n - """, msg='Checking for boost threading library', - libpath='/usr/local/lib', - lib=[thread, 'boost_system%s' % lib_suffix], - uselib_store='BOOST_THREAD') - - conf.check_cxx(fragment=""" - #include \n - int main() { boost::filesystem::copy_file ("a", "b"); }\n - """, msg='Checking for boost filesystem library', - libpath='/usr/local/lib', - lib=['boost_filesystem%s' % lib_suffix, 'boost_system%s' % lib_suffix], - uselib_store='BOOST_FILESYSTEM') - - conf.check_cxx(fragment=""" - #include \n - int main() { boost::gregorian::day_clock::local_day(); }\n - """, msg='Checking for boost datetime library', - libpath='/usr/local/lib', - lib=['boost_date_time%s' % lib_suffix, 'boost_system%s' % lib_suffix], - uselib_store='BOOST_DATETIME') - - conf.check_cxx(fragment=""" - #include \n - int main() { boost::signals2::signal x; }\n - """, - msg='Checking for boost signals2 library', - uselib_store='BOOST_SIGNALS2') - -def static_boost(conf, lib_suffix): - conf.env.STLIB_BOOST_THREAD = ['boost_thread'] - conf.env.STLIB_BOOST_FILESYSTEM = ['boost_filesystem%s' % lib_suffix] - conf.env.STLIB_BOOST_DATETIME = ['boost_date_time%s' % lib_suffix, 'boost_system%s' % lib_suffix] - conf.env.STLIB_BOOST_SIGNALS2 = ['boost_signals2'] + opt.add_option('--target-windows', action='store_true', default=False, help='set up to do a cross-compile to make a Windows package') + opt.add_option('--static-dcpomatic', action='store_true', default=False, help='link to components of DCP-o-matic statically') + opt.add_option('--static-boost', action='store_true', default=False, help='link statically to Boost') + opt.add_option('--static-openjpeg', action='store_true', default=False, help='link statically to OpenJPEG') + opt.add_option('--static-wxwidgets', action='store_true', default=False, help='link statically to wxWidgets') + opt.add_option('--static-ffmpeg', action='store_true', default=False, help='link statically to FFmpeg') + opt.add_option('--static-xmlpp', action='store_true', default=False, help='link statically to libxml++') + opt.add_option('--static-xmlsec', action='store_true', default=False, help='link statically to xmlsec') + opt.add_option('--static-ssh', action='store_true', default=False, help='link statically to libssh') + opt.add_option('--static-cxml', action='store_true', default=False, help='link statically to libcxml') + opt.add_option('--static-dcp', action='store_true', default=False, help='link statically to libdcp') + opt.add_option('--static-sub', action='store_true', default=False, help='link statically to libsub') + opt.add_option('--static-curl', action='store_true', default=False, help='link statically to libcurl') + opt.add_option('--workaround-gssapi', action='store_true', default=False, help='link to gssapi_krb5') def configure(conf): conf.load('compiler_cxx') if conf.options.target_windows: conf.load('winres') - # conf.options -> conf.env - conf.env.TARGET_WINDOWS = conf.options.target_windows + # Save conf.options that we need elsewhere in conf.env conf.env.DISABLE_GUI = conf.options.disable_gui conf.env.DISABLE_TESTS = conf.options.disable_tests - conf.env.TARGET_DEBIAN = conf.options.target_debian - conf.env.DEBIAN_UNSTABLE = conf.options.debian_unstable - conf.env.TARGET_CENTOS_6 = conf.options.target_centos_6 - conf.env.TARGET_CENTOS_7 = conf.options.target_centos_7 - conf.env.VERSION = VERSION + conf.env.TARGET_WINDOWS = conf.options.target_windows conf.env.TARGET_OSX = sys.platform == 'darwin' conf.env.TARGET_LINUX = not conf.env.TARGET_WINDOWS and not conf.env.TARGET_OSX - # true if we should build dcpomatic/libdcpomatic/libdcpomatic-wx statically - conf.env.BUILD_STATIC = conf.options.target_debian or conf.options.target_centos_6 or conf.options.target_centos_7 + conf.env.VERSION = VERSION + conf.env.DEBUG = conf.options.enable_debug + conf.env.STATIC_DCPOMATIC = conf.options.static_dcpomatic if conf.options.install_prefix is None: conf.env.INSTALL_PREFIX = conf.env.PREFIX else: conf.env.INSTALL_PREFIX = conf.options.install_prefix # Common CXXFLAGS - conf.env.append_value('CXXFLAGS', ['-D__STDC_CONSTANT_MACROS', '-D__STDC_LIMIT_MACROS', '-msse', '-ffast-math', '-fno-strict-aliasing', - '-Wall', '-Wno-attributes', '-Wextra', '-Wno-unused-result', '-D_FILE_OFFSET_BITS=64']) + conf.env.append_value('CXXFLAGS', ['-D__STDC_CONSTANT_MACROS', + '-D__STDC_LIMIT_MACROS', + '-D__STDC_FORMAT_MACROS', + '-msse', + '-ffast-math', + '-fno-strict-aliasing', + '-Wall', + '-Wno-attributes', + '-Wextra', + '-Wno-unused-result', + '-D_FILE_OFFSET_BITS=64']) if conf.options.enable_debug: conf.env.append_value('CXXFLAGS', ['-g', '-DDCPOMATIC_DEBUG']) else: conf.env.append_value('CXXFLAGS', '-O2') - if conf.options.address_sanitizer: - conf.env.append_value('CXXFLAGS', ['-fsanitize=address', '-fno-omit-frame-pointer']) - conf.env.append_value('LINKFLAGS', ['-fsanitize=address']) - # - # Platform-specific CFLAGS hacks and other tinkering + # Windows/Linux/OS X specific # # Windows @@ -220,6 +113,14 @@ def configure(conf): conf.check(lib='mswsock', uselib_store='MSWSOCK', msg="Checking for library mswsock") boost_lib_suffix = '-mt' boost_thread = 'boost_thread_win32-mt' + conf.check_cxx(fragment=""" + #include \n + int main() { std::locale::global (boost::locale::generator().generate ("")); }\n + """, + msg='Checking for boost locale library', + libpath='/usr/local/lib', + lib=['boost_locale%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix], + uselib_store='BOOST_LOCALE') # POSIX if conf.env.TARGET_LINUX or conf.env.TARGET_OSX: @@ -235,15 +136,7 @@ def configure(conf): conf.env.append_value('CXXFLAGS', '-DLINUX_SHARE_PREFIX="%s/share/dcpomatic2"' % conf.env['INSTALL_PREFIX']) conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_LINUX') - if conf.env.TARGET_DEBIAN: - # libxml2 seems to be linked against this on Ubuntu but it doesn't mention it in its .pc file - conf.check_cfg(package='liblzma', args='--cflags --libs', uselib_store='LZMA', mandatory=True) - - if conf.env.TARGET_CENTOS_6 or conf.env.TARGET_CENTOS_7: - # libavcodec seems to be linked against this on Centos - conf.check_cfg(package='liblzma', args='--cflags --libs', uselib_store='LZMA', mandatory=True) - - if not conf.env.DISABLE_GUI and conf.env.TARGET_LINUX: + if not conf.env.DISABLE_GUI: conf.check_cfg(package='gtk+-2.0', args='--cflags --libs', uselib_store='GTK', mandatory=True) # OSX @@ -253,97 +146,203 @@ def configure(conf): # # Dependencies. - # There's probably a neater way of expressing these, but I've gone for brute force for now. # - if conf.env.TARGET_DEBIAN: - conf.check_cfg(package='libcxml', atleast_version='0.11', args='--cflags', uselib_store='CXML', mandatory=True) - conf.env.STLIB_CXML = ['cxml'] - conf.check_cfg(package='libxml++-2.6', args='--cflags --libs', uselib_store='XMLPP', mandatory=True) - conf.check_cfg(package='libcurl', args='--cflags --libs', uselib_store='CURL', mandatory=True) - static_ffmpeg(conf) - static_openjpeg(conf) - static_sub(conf) - static_dcp(conf, False, False, False, False) - dynamic_boost(conf, boost_lib_suffix, boost_thread) - - if conf.env.TARGET_CENTOS_6: - # Centos 6.5's boost is too old, so we build a new version statically in the chroot - conf.check_cfg(package='libcxml', atleast_version='0.08', args='--cflags --libs-only-L', uselib_store='CXML', mandatory=True) - conf.env.STLIB_CXML = ['cxml', 'boost_filesystem'] - conf.check_cfg(package='libcurl', args='--cflags --libs-only-L', uselib_store='CURL', mandatory=True) + # It should be possible to use check_cfg for both dynamic and static linking, but + # e.g. pkg-config --libs --static foo returns some libraries that should be statically + # linked and others that should be dynamic. This doesn't work too well with waf + # as it wants them separate. + + # libcurl + if conf.options.static_curl: conf.env.STLIB_CURL = ['curl'] conf.env.LIB_CURL = ['ssh2', 'idn'] - static_ffmpeg(conf) - static_openjpeg(conf) - static_sub(conf) - static_dcp(conf, True, True, True, True) - static_boost(conf, boost_lib_suffix) - - if conf.env.TARGET_CENTOS_7: - # Centos 7's boost is ok so we link it dynamically - conf.check_cfg(package='libcxml', atleast_version='0.08', args='--cflags', uselib_store='CXML', mandatory=True) - conf.env.STLIB_CXML = ['cxml'] + else: conf.check_cfg(package='libcurl', args='--cflags --libs', uselib_store='CURL', mandatory=True) - conf.env.LIB_SSH = ['gssapi_krb5'] - conf.env.LIB_XMLPP = ['xml2'] - conf.env.LIB_XMLSEC = ['ltdl'] - static_ffmpeg(conf) - static_openjpeg(conf) - static_sub(conf) - static_dcp(conf, False, True, True, True) - dynamic_boost(conf, boost_lib_suffix, boost_thread) - if conf.env.TARGET_WINDOWS: - conf.check_cfg(package='libxml++-2.6', args='--cflags --libs', uselib_store='XMLPP', mandatory=True) - conf.check_cfg(package='libcurl', args='--cflags --libs', uselib_store='CURL', mandatory=True) - conf.check_cxx(fragment=""" - #include \n - int main() { std::locale::global (boost::locale::generator().generate ("")); }\n - """, msg='Checking for boost locale library', - libpath='/usr/local/lib', - lib=['boost_locale%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix], - uselib_store='BOOST_LOCALE') - dynamic_boost(conf, boost_lib_suffix, boost_thread) - dynamic_ffmpeg(conf) - dynamic_openjpeg(conf) - dynamic_dcp(conf) - dynamic_sub(conf) - dynamic_ssh(conf) - - # Not packaging; just a straight build - if not conf.env.TARGET_WINDOWS and not conf.env.TARGET_DEBIAN and not conf.env.TARGET_CENTOS_6 and not conf.env.TARGET_CENTOS_7: - conf.check_cfg(package='libcxml', atleast_version='0.08', args='--cflags --libs', uselib_store='CXML', mandatory=True) - conf.check_cfg(package='libxml++-2.6', args='--cflags --libs', uselib_store='XMLPP', mandatory=True) - conf.check_cfg(package='libcurl', args='--cflags --libs', uselib_store='CURL', mandatory=True) - dynamic_boost(conf, boost_lib_suffix, boost_thread) - dynamic_ffmpeg(conf) - dynamic_dcp(conf) - dynamic_sub(conf) - dynamic_openjpeg(conf) - dynamic_ssh(conf) - - # Dependencies which are always dynamically linked + + # libsndfile conf.check_cfg(package='sndfile', args='--cflags --libs', uselib_store='SNDFILE', mandatory=True) + + # glib conf.check_cfg(package='glib-2.0', args='--cflags --libs', uselib_store='GLIB', mandatory=True) - if distutils.spawn.find_executable(conf.options.magickpp_config): - conf.check_cfg(package='', path=conf.options.magickpp_config, args='--cppflags --cxxflags --libs', uselib_store='MAGICK', mandatory=True) + + # ImageMagick / GraphicsMagick + if distutils.spawn.find_executable('Magick++-config'): + conf.check_cfg(package='', path='Magick++-config', args='--cppflags --cxxflags --libs', uselib_store='MAGICK', mandatory=True) conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_IMAGE_MAGICK') else: - conf.check_cfg(package='GraphicsMagick++', args='--cflags --libs', uselib_store='MAGICK', mandatory=True) - conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_GRAPHICS_MAGICK') + image = conf.check_cfg(package='ImageMagick++', args='--cflags --libs', uselib_store='MAGICK', mandatory=False) + graphics = conf.check_cfg(package='GraphicsMagick++', args='--cflags --libs', uselib_store='MAGICK', mandatory=False) + if image is None and graphics is None: + Logs.pprint('RED', 'Neither ImageMagick++ nor GraphicsMagick++ found: one or the other is required') + if image is not None: + conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_IMAGE_MAGICK') + if graphics is not None: + conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_GRAPHICS_MAGICK') + # libzip conf.check_cfg(package='libzip', args='--cflags --libs', uselib_store='ZIP', mandatory=True) + + # pangomm conf.check_cfg(package='pangomm-1.4', args='--cflags --libs', uselib_store='PANGOMM', mandatory=True) + + # cairomm conf.check_cfg(package='cairomm-1.0', args='--cflags --libs', uselib_store='CAIROMM', mandatory=True) - conf.check_cc(fragment=""" - #include - int main() { g_format_size (1); } - """, msg='Checking for g_format_size ()', - uselib='GLIB', - define_name='HAVE_G_FORMAT_SIZE', - mandatory=False) + # libcxml + if conf.options.static_cxml: + conf.check_cfg(package='libcxml', atleast_version='0.08', args='--cflags', uselib_store='CXML', mandatory=True) + conf.env.STLIB_CXML = ['cxml'] + else: + conf.check_cfg(package='libcxml', atleast_version='0.08', args='--cflags --libs', uselib_store='CXML', mandatory=True) + + # libssh + if conf.options.static_ssh: + conf.env.STLIB_SSH = ['ssh'] + if conf.options.workaround_gssapi: + conf.env.LIB_SSH = ['gssapi_krb5'] + else: + conf.check_cc(fragment=""" + #include \n + int main () {\n + ssh_session s = ssh_new ();\n + return 0;\n + } + """, + msg='Checking for library libssh', + mandatory=True, + lib='ssh', + uselib_store='SSH') + + # libdcp + if conf.options.static_dcp: + conf.check_cfg(package='libdcp-1.0', atleast_version='1.00.0', args='--cflags', uselib_store='DCP', mandatory=True) + conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP] + conf.env.STLIB_DCP = ['dcp', 'asdcp-libdcp', 'kumu-libdcp'] + conf.env.LIB_DCP = ['glibmm-2.4', 'ssl', 'crypto', 'bz2', 'xslt'] + else: + conf.check_cfg(package='libdcp-1.0', atleast_version='1.00.0', args='--cflags --libs', uselib_store='DCP', mandatory=True) + conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP] + + # libsub + if conf.options.static_sub: + conf.check_cfg(package='libsub-1.0', atleast_version='1.00.0', args='--cflags', uselib_store='DCP', mandatory=True) + conf.env.DEFINES_SUB = [f.replace('\\', '') for f in conf.env.DEFINES_SUB] + conf.env.STLIB_SUB = ['sub'] + else: + conf.check_cfg(package='libsub-1.0', atleast_version='1.00.0', args='--cflags --libs', uselib_store='DCP', mandatory=True) + conf.env.DEFINES_SUB = [f.replace('\\', '') for f in conf.env.DEFINES_SUB] + + # libxml++ + if conf.options.static_xmlpp: + conf.env.STLIB_XMLPP = ['xml++-2.6'] + else: + conf.check_cfg(package='libxml++-2.6', args='--cflags --libs', uselib_store='XMLPP', mandatory=True) + + # libxmlsec + if conf.options.static_xmlsec: + conf.env.STLIB_XMLSEC = ['xmlsec1-openssl', 'xmlsec1'] + else: + conf.env.LIB_XMLSEC = ['xmlsec1-openssl', 'xmlsec1'] + + # OpenJPEG + if conf.options.static_openjpeg: + conf.check_cfg(package='libopenjpeg', args='--cflags', atleast_version='1.5.0', uselib_store='OPENJPEG', mandatory=True) + conf.check_cfg(package='libopenjpeg', args='--cflags', max_version='1.5.2', mandatory=True) + conf.env.STLIB_OPENJPEG = ['openjpeg'] + else: + conf.check_cfg(package='libopenjpeg', args='--cflags --libs', atleast_version='1.5.0', uselib_store='OPENJPEG', mandatory=True) + conf.check_cfg(package='libopenjpeg', args='--cflags --libs', max_version='1.5.2', mandatory=True) + + # FFmpeg + if conf.options.static_ffmpeg: + names = ['avformat', 'avfilter', 'avcodec', 'avutil', 'swscale', 'swresample', 'postproc'] + for name in names: + static = subprocess.Popen(shlex.split('pkg-config --static --libs lib%s' % name), stdout=subprocess.PIPE).communicate()[0] + libs = [] + stlibs = [] + include = [] + libpath = [] + for s in static.split(): + if s.startswith('-L'): + libpath.append(s[2:]) + elif s.startswith('-I'): + include.append(s[2:]) + elif s.startswith('-l'): + if s[2:] not in names: + libs.append(s[2:]) + else: + stlibs.append(s[2:]) + + conf.env['LIB_%s' % name.upper()] = libs + conf.env['STLIB_%s' % name.upper()] = stlibs + conf.env['INCLUDE_%s' % name.upper()] = include + conf.env['LIBPATH_%s' % name.upper()] = libpath + else: + conf.check_cfg(package='libavformat', args='--cflags --libs', uselib_store='AVFORMAT', mandatory=True) + conf.check_cfg(package='libavfilter', args='--cflags --libs', uselib_store='AVFILTER', mandatory=True) + conf.check_cfg(package='libavcodec', args='--cflags --libs', uselib_store='AVCODEC', mandatory=True) + conf.check_cfg(package='libavutil', args='--cflags --libs', uselib_store='AVUTIL', mandatory=True) + conf.check_cfg(package='libswscale', args='--cflags --libs', uselib_store='SWSCALE', mandatory=True) + conf.check_cfg(package='libswresample', args='--cflags --libs', uselib_store='SWRESAMPLE', mandatory=True) + conf.check_cfg(package='libpostproc', args='--cflags --libs', uselib_store='POSTPROC', mandatory=True) + + # Boost + if conf.options.static_boost: + conf.env.STLIB_BOOST_THREAD = ['boost_thread'] + conf.env.STLIB_BOOST_FILESYSTEM = ['boost_filesystem%s' % boost_lib_suffix] + conf.env.STLIB_BOOST_DATETIME = ['boost_date_time%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix] + conf.env.STLIB_BOOST_SIGNALS2 = ['boost_signals2'] + conf.env.STLIB_BOOST_SYSTEM = ['boost_system'] + else: + conf.check_cxx(fragment=""" + #include \n + #if BOOST_VERSION < 104500\n + #error boost too old\n + #endif\n + int main(void) { return 0; }\n + """, + mandatory=True, + msg='Checking for boost library >= 1.45', + okmsg='yes', + errmsg='too old\nPlease install boost version 1.45 or higher.') + + conf.check_cxx(fragment=""" + #include \n + int main() { boost::thread t (); }\n + """, + msg='Checking for boost threading library', + libpath='/usr/local/lib', + lib=[boost_thread, 'boost_system%s' % boost_lib_suffix], + uselib_store='BOOST_THREAD') + + conf.check_cxx(fragment=""" + #include \n + int main() { boost::filesystem::copy_file ("a", "b"); }\n + """, + msg='Checking for boost filesystem library', + libpath='/usr/local/lib', + lib=['boost_filesystem%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix], + uselib_store='BOOST_FILESYSTEM') + + conf.check_cxx(fragment=""" + #include \n + int main() { boost::gregorian::day_clock::local_day(); }\n + """, + msg='Checking for boost datetime library', + libpath='/usr/local/lib', + lib=['boost_date_time%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix], + uselib_store='BOOST_DATETIME') + + conf.check_cxx(fragment=""" + #include \n + int main() { boost::signals2::signal x; }\n + """, + msg='Checking for boost signals2 library', + uselib_store='BOOST_SIGNALS2') + + # Other stuff conf.find_program('msgfmt', var='MSGFMT') @@ -358,6 +357,36 @@ def configure(conf): if not conf.env.DISABLE_TESTS: conf.recurse('test') + Logs.pprint('YELLOW', '') + if conf.env.TARGET_WINDOWS: + Logs.pprint('YELLOW', '\t' + 'Target'.ljust(25) + ': Windows') + elif conf.env.TARGET_LINUX: + Logs.pprint('YELLOW', '\t' + 'Target'.ljust(25) + ': Linux') + elif conf.env.TARGET_OSX: + Logs.pprint('YELLOW', '\t' + 'Target'.ljust(25) + ': OS X') + + def report(name, variable): + linkage = '' + if variable: + linkage = 'static' + else: + linkage = 'dynamic' + Logs.pprint('YELLOW', '\t%s: %s' % (name.ljust(25), linkage)) + + report('DCP-o-matic libraries', conf.options.static_dcpomatic) + report('Boost', conf.options.static_boost) + report('OpenJPEG', conf.options.static_openjpeg) + report('wxWidgets', conf.options.static_wxwidgets) + report('FFmpeg', conf.options.static_ffmpeg) + report('libxml++', conf.options.static_xmlpp) + report('xmlsec', conf.options.static_xmlsec) + report('libssh', conf.options.static_ssh) + report('libcxml', conf.options.static_cxml) + report('libdcp', conf.options.static_dcp) + report('libcurl', conf.options.static_curl) + + Logs.pprint('YELLOW', '') + def build(bld): create_version_cc(VERSION, bld.env.CXXFLAGS) @@ -372,11 +401,10 @@ def build(bld): bld.recurse('platform/osx') for r in ['22x22', '32x32', '48x48', '64x64', '128x128']: - bld.install_files('${PREFIX}/share/icons/hicolor/%s/apps' % r, 'icons/%s/dcpomatic2.png' % r) + bld.install_files('${PREFIX}/share/icons/hicolor/%s/apps' % r, 'icons/%s/dcpomatic.png' % r) - if bld.env.TARGET_LINUX: - bld.install_files('${PREFIX}/share/dcpomatic2', 'icons/taskbar_icon.png') - bld.install_files('${PREFIX}/share/dcpomatic2', 'LiberationSans-Regular.ttf') + if not bld.env.TARGET_WINDOWS: + bld.install_files('${PREFIX}/share/dcpomatic', 'icons/taskbar_icon.png') bld.add_post_fun(post) @@ -402,7 +430,6 @@ def dist(ctx): GRSYMS GRTAGS GSYMS GTAGS """ - def create_version_cc(version, cxx_flags): commit = git_revision() if commit is None and os.path.exists('.git_revision'): @@ -442,4 +469,4 @@ def pot_merge(bld): bld.recurse('src') def tags(bld): - os.system('etags src/lib/*.cc src/lib/*.h src/wx/*.cc src/wx/*.h src/tools/*.cc src/tools/*.h test/*.cc') + os.system('etags src/lib/*.cc src/lib/*.h src/wx/*.cc src/wx/*.h src/tools/*.cc src/tools/*.h') -- cgit v1.2.3 From 2bfd531137f1a4874493186015046e33c5a07c1e Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 12 May 2015 16:13:48 +0100 Subject: Assorted image subtitle fixes. --- src/lib/dcp_decoder.cc | 8 ++++- src/lib/dcp_decoder.h | 3 +- src/lib/dcp_subtitle_decoder.cc | 8 ++++- src/lib/dcp_subtitle_decoder.h | 3 +- src/lib/dcpomatic_time.h | 1 + src/lib/ffmpeg_decoder.cc | 66 ++++++++++++++++++++++++++------------- src/lib/ffmpeg_decoder.h | 8 ++++- src/lib/ffmpeg_examiner.cc | 15 ++++++--- src/lib/ffmpeg_examiner.h | 4 ++- src/lib/ffmpeg_subtitle_stream.cc | 20 +++++++++++- src/lib/player.cc | 8 +++-- src/lib/render_subtitles.cc | 4 --- src/lib/subrip_decoder.cc | 8 ++++- src/lib/subrip_decoder.h | 3 +- src/lib/subtitle_decoder.cc | 14 ++++----- src/lib/subtitle_decoder.h | 5 +-- src/lib/types.h | 1 + src/lib/util.cc | 13 +++++--- src/lib/util.h | 19 ++++++++++- 19 files changed, 153 insertions(+), 58 deletions(-) (limited to 'src/lib') diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc index 51d16b43c..ab0df8a86 100644 --- a/src/lib/dcp_decoder.cc +++ b/src/lib/dcp_decoder.cc @@ -133,7 +133,13 @@ DCPDecoder::seek (ContentTime t, bool accurate) list -DCPDecoder::subtitles_during (ContentTimePeriod, bool) const +DCPDecoder::image_subtitles_during (ContentTimePeriod, bool) const +{ + return list (); +} + +list +DCPDecoder::text_subtitles_during (ContentTimePeriod, bool) const { /* XXX */ return list (); diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h index 8afebff57..20ca2bb67 100644 --- a/src/lib/dcp_decoder.h +++ b/src/lib/dcp_decoder.h @@ -40,7 +40,8 @@ public: private: void seek (ContentTime t, bool accurate); bool pass (); - std::list subtitles_during (ContentTimePeriod, bool starting) const; + std::list image_subtitles_during (ContentTimePeriod, bool starting) const; + std::list text_subtitles_during (ContentTimePeriod, bool starting) const; ContentTime _next; std::list > _reels; diff --git a/src/lib/dcp_subtitle_decoder.cc b/src/lib/dcp_subtitle_decoder.cc index e3c06378b..236e99996 100644 --- a/src/lib/dcp_subtitle_decoder.cc +++ b/src/lib/dcp_subtitle_decoder.cc @@ -61,7 +61,13 @@ DCPSubtitleDecoder::pass () } list -DCPSubtitleDecoder::subtitles_during (ContentTimePeriod p, bool starting) const +DCPSubtitleDecoder::image_subtitles_during (ContentTimePeriod, bool) const +{ + return list (); +} + +list +DCPSubtitleDecoder::text_subtitles_during (ContentTimePeriod p, bool starting) const { /* XXX: inefficient */ diff --git a/src/lib/dcp_subtitle_decoder.h b/src/lib/dcp_subtitle_decoder.h index 2326b31ad..9a16fb7c8 100644 --- a/src/lib/dcp_subtitle_decoder.h +++ b/src/lib/dcp_subtitle_decoder.h @@ -32,7 +32,8 @@ protected: bool pass (); private: - std::list subtitles_during (ContentTimePeriod, bool starting) const; + std::list image_subtitles_during (ContentTimePeriod, bool starting) const; + std::list text_subtitles_during (ContentTimePeriod, bool starting) const; std::list _subtitles; std::list::const_iterator _next; diff --git a/src/lib/dcpomatic_time.h b/src/lib/dcpomatic_time.h index dc9b0cd8a..ae8f25199 100644 --- a/src/lib/dcpomatic_time.h +++ b/src/lib/dcpomatic_time.h @@ -193,6 +193,7 @@ class ContentTimePeriod { public: ContentTimePeriod () {} + ContentTimePeriod (ContentTime f, ContentTime t) : from (f) , to (t) diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index bd01b280b..5dc06fa8e 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -301,6 +301,7 @@ FFmpegDecoder::seek (ContentTime time, bool accurate) { VideoDecoder::seek (time, accurate); AudioDecoder::seek (time, accurate); + SubtitleDecoder::seek (time, accurate); /* If we are doing an `accurate' seek, we need to use pre-roll, as we don't really know what the seek will give us. @@ -426,27 +427,38 @@ FFmpegDecoder::decode_subtitle_packet () return; } - /* Sometimes we get an empty AVSubtitle, which is used by some codecs to - indicate that the previous subtitle should stop. + /* Subtitle PTS (within the source, not taking into account any of the + source that we may have chopped off for the DCP) */ + FFmpegSubtitlePeriod period = subtitle_period (sub); + period.from += _pts_offset; + if (period.to) { + period.to = period.to.get() + _pts_offset; + } + if (sub.num_rects <= 0) { - image_subtitle (ContentTimePeriod (), shared_ptr (), dcpomatic::Rect ()); + /* Sometimes we get an empty AVSubtitle, which is used by some codecs to + indicate that the previous subtitle should stop. Emit the pending one. + */ + if (_pending_subtitle_from && _pending_subtitle_image && _pending_subtitle_rect) { + image_subtitle ( + ContentTimePeriod (_pending_subtitle_from.get(), period.from), + _pending_subtitle_image, + _pending_subtitle_rect.get () + ); + _pending_subtitle_from = optional (); + _pending_subtitle_image.reset (); + _pending_subtitle_rect = optional > (); + } return; } else if (sub.num_rects > 1) { throw DecodeError (_("multi-part subtitles not yet supported")); } - /* Subtitle PTS (within the source, not taking into account any of the - source that we may have chopped off for the DCP) - */ - ContentTimePeriod period = subtitle_period (sub) + _pts_offset; - AVSubtitleRect const * rect = sub.rects[0]; if (rect->type != SUBTITLE_BITMAP) { - /* XXX */ - // throw DecodeError (_("non-bitmap subtitles not yet supported")); - return; + throw DecodeError (_("non-bitmap subtitles not yet supported")); } /* Note RGBA is expressed little-endian, so the first byte in the word is R, second @@ -475,23 +487,33 @@ FFmpegDecoder::decode_subtitle_packet () } dcp::Size const vs = _ffmpeg_content->video_size (); - - image_subtitle ( - period, - image, - dcpomatic::Rect ( - static_cast (rect->x) / vs.width, - static_cast (rect->y) / vs.height, - static_cast (rect->w) / vs.width, - static_cast (rect->h) / vs.height - ) + dcpomatic::Rect const scaled_rect ( + static_cast (rect->x) / vs.width, + static_cast (rect->y) / vs.height, + static_cast (rect->w) / vs.width, + static_cast (rect->h) / vs.height ); + + if (period.to) { + image_subtitle (ContentTimePeriod (period.from, period.to.get()), image, scaled_rect); + } else { + /* We don't know when this subtitle stops, so store it until we find out */ + _pending_subtitle_from = period.from; + _pending_subtitle_image = image; + _pending_subtitle_rect = scaled_rect; + } avsubtitle_free (&sub); } list -FFmpegDecoder::subtitles_during (ContentTimePeriod p, bool starting) const +FFmpegDecoder::image_subtitles_during (ContentTimePeriod p, bool starting) const { return _ffmpeg_content->subtitles_during (p, starting); } + +list +FFmpegDecoder::text_subtitles_during (ContentTimePeriod, bool) const +{ + return list (); +} diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 0334a30e2..b5bcdd358 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -27,6 +27,7 @@ #include "audio_decoder.h" #include "subtitle_decoder.h" #include "ffmpeg.h" +#include "rect.h" extern "C" { #include } @@ -66,7 +67,12 @@ private: void maybe_add_subtitle (); boost::shared_ptr deinterleave_audio (uint8_t** data, int size); - std::list subtitles_during (ContentTimePeriod, bool starting) const; + boost::optional _pending_subtitle_from; + boost::shared_ptr _pending_subtitle_image; + boost::optional > _pending_subtitle_rect; + + std::list image_subtitles_during (ContentTimePeriod, bool starting) const; + std::list text_subtitles_during (ContentTimePeriod, bool starting) const; boost::shared_ptr _log; diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc index 4409526dc..e4f4e6f29 100644 --- a/src/lib/ffmpeg_examiner.cc +++ b/src/lib/ffmpeg_examiner.cc @@ -150,13 +150,18 @@ FFmpegExaminer::subtitle_packet (AVCodecContext* context, shared_ptr= 0 && frame_finished) { - ContentTimePeriod const period = subtitle_period (sub); - if (sub.num_rects == 0 && !stream->periods.empty () && stream->periods.back().to > period.from) { - /* Finish the last subtitle */ - stream->periods.back().to = period.from; + FFmpegSubtitlePeriod const period = subtitle_period (sub); + if (sub.num_rects <= 0 && _last_subtitle_start) { + stream->periods.push_back (ContentTimePeriod (_last_subtitle_start.get (), period.from)); + _last_subtitle_start = optional (); } else if (sub.num_rects == 1) { - stream->periods.push_back (period); + if (period.to) { + stream->periods.push_back (ContentTimePeriod (period.from, period.to.get ())); + } else { + _last_subtitle_start = period.from; + } } + avsubtitle_free (&sub); } } diff --git a/src/lib/ffmpeg_examiner.h b/src/lib/ffmpeg_examiner.h index b873222c1..34d4b1e0d 100644 --- a/src/lib/ffmpeg_examiner.h +++ b/src/lib/ffmpeg_examiner.h @@ -55,7 +55,7 @@ private: std::string audio_stream_name (AVStream* s) const; std::string subtitle_stream_name (AVStream* s) const; boost::optional frame_time (AVStream* s) const; - + std::vector > _subtitle_streams; std::vector > _audio_streams; boost::optional _first_video; @@ -64,4 +64,6 @@ private: */ ContentTime _video_length; bool _need_video_length; + + boost::optional _last_subtitle_start; }; diff --git a/src/lib/ffmpeg_subtitle_stream.cc b/src/lib/ffmpeg_subtitle_stream.cc index 3d8fd4e83..66b587209 100644 --- a/src/lib/ffmpeg_subtitle_stream.cc +++ b/src/lib/ffmpeg_subtitle_stream.cc @@ -18,6 +18,11 @@ */ #include "ffmpeg_subtitle_stream.h" +#include "raw_convert.h" +#include +#include + +using std::string; /** Construct a SubtitleStream from a value returned from to_string(). * @param t String returned from to_string(). @@ -26,11 +31,24 @@ FFmpegSubtitleStream::FFmpegSubtitleStream (cxml::ConstNodePtr node) : FFmpegStream (node) { - + BOOST_FOREACH (cxml::NodePtr i, node->node_children ("Period")) { + periods.push_back ( + ContentTimePeriod ( + ContentTime (node->number_child ("From")), + ContentTime (node->number_child ("To")) + ) + ); + } } void FFmpegSubtitleStream::as_xml (xmlpp::Node* root) const { FFmpegStream::as_xml (root); + + BOOST_FOREACH (ContentTimePeriod const & i, periods) { + xmlpp::Node* node = root->add_child ("Period"); + node->add_child("From")->add_child_text (raw_convert (i.from.get ())); + node->add_child("To")->add_child_text (raw_convert (i.to.get ())); + } } diff --git a/src/lib/player.cc b/src/lib/player.cc index 436ae3fe8..640253c6d 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -376,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)); diff --git a/src/lib/render_subtitles.cc b/src/lib/render_subtitles.cc index bc89fd3f8..9620eacbf 100644 --- a/src/lib/render_subtitles.cc +++ b/src/lib/render_subtitles.cc @@ -50,10 +50,6 @@ calculate_position (dcp::VAlign v_align, double v_position, int target_height, i PositionImage render_subtitles (list subtitles, dcp::Size target) { - if (subtitles.empty ()) { - return PositionImage (); - } - /* Estimate height that the subtitle image needs to be */ optional top; optional bottom; diff --git a/src/lib/subrip_decoder.cc b/src/lib/subrip_decoder.cc index 552a96b8f..bef48ba78 100644 --- a/src/lib/subrip_decoder.cc +++ b/src/lib/subrip_decoder.cc @@ -85,7 +85,13 @@ SubRipDecoder::pass () } list -SubRipDecoder::subtitles_during (ContentTimePeriod p, bool starting) const +SubRipDecoder::image_subtitles_during (ContentTimePeriod, bool) const +{ + return list (); +} + +list +SubRipDecoder::text_subtitles_during (ContentTimePeriod p, bool starting) const { /* XXX: inefficient */ diff --git a/src/lib/subrip_decoder.h b/src/lib/subrip_decoder.h index ad9d04e40..876f763d3 100644 --- a/src/lib/subrip_decoder.h +++ b/src/lib/subrip_decoder.h @@ -35,7 +35,8 @@ protected: bool pass (); private: - std::list subtitles_during (ContentTimePeriod, bool starting) const; + std::list image_subtitles_during (ContentTimePeriod, bool starting) const; + std::list text_subtitles_during (ContentTimePeriod, bool starting) const; size_t _next; }; diff --git a/src/lib/subtitle_decoder.cc b/src/lib/subtitle_decoder.cc index 9b2aa8ab0..ac9a67e33 100644 --- a/src/lib/subtitle_decoder.cc +++ b/src/lib/subtitle_decoder.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 @@ -33,7 +33,8 @@ SubtitleDecoder::SubtitleDecoder (shared_ptr c) } /** Called by subclasses when an image subtitle is ready. - * Image may be 0 to say that there is no current subtitle. + * @param period Period of the subtitle. + * @param image Subtitle image. * @param rect Area expressed as a fraction of the video frame that this subtitle * is for (e.g. a width of 0.5 means the width of the subtitle is half the width * of the video frame) @@ -50,12 +51,11 @@ SubtitleDecoder::text_subtitle (list s) _decoded_text_subtitles.push_back (ContentTextSubtitle (s)); } +/** @param sp Full periods of subtitles that are showing or starting during the specified period */ template list -SubtitleDecoder::get (list const & subs, ContentTimePeriod period, bool starting) +SubtitleDecoder::get (list const & subs, list const & sp, ContentTimePeriod period, bool starting) { - /* Get the full periods of the subtitles that are showing or starting during the specified period */ - list sp = subtitles_during (period, starting); if (sp.empty ()) { /* Nothing in this period */ return list (); @@ -88,13 +88,13 @@ SubtitleDecoder::get (list const & subs, ContentTimePeriod period, bool start list SubtitleDecoder::get_text_subtitles (ContentTimePeriod period, bool starting) { - return get (_decoded_text_subtitles, period, starting); + return get (_decoded_text_subtitles, text_subtitles_during (period, starting), period, starting); } list SubtitleDecoder::get_image_subtitles (ContentTimePeriod period, bool starting) { - return get (_decoded_image_subtitles, period, starting); + return get (_decoded_image_subtitles, image_subtitles_during (period, starting), period, starting); } void diff --git a/src/lib/subtitle_decoder.h b/src/lib/subtitle_decoder.h index d7faaa014..8ba74404f 100644 --- a/src/lib/subtitle_decoder.h +++ b/src/lib/subtitle_decoder.h @@ -49,12 +49,13 @@ protected: private: template - std::list get (std::list const & subs, ContentTimePeriod period, bool starting); + std::list get (std::list const & subs, std::list const & sp, ContentTimePeriod period, bool starting); /** @param starting true if we want only subtitles that start during the period, otherwise * we want subtitles that overlap the period. */ - virtual std::list subtitles_during (ContentTimePeriod period, bool starting) const = 0; + virtual std::list image_subtitles_during (ContentTimePeriod period, bool starting) const = 0; + virtual std::list text_subtitles_during (ContentTimePeriod period, bool starting) const = 0; boost::shared_ptr _subtitle_content; }; diff --git a/src/lib/types.h b/src/lib/types.h index f3877d0d5..e7017a295 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -22,6 +22,7 @@ #include "dcpomatic_time.h" #include "position.h" +#include "rect.h" #include #include #include diff --git a/src/lib/util.cc b/src/lib/util.cc index bffbe90d4..d8c754607 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -564,18 +564,21 @@ wrapped_av_malloc (size_t s) } return p; } - -ContentTimePeriod + +FFmpegSubtitlePeriod subtitle_period (AVSubtitle const & sub) { ContentTime const packet_time = ContentTime::from_seconds (static_cast (sub.pts) / AV_TIME_BASE); - ContentTimePeriod period ( + if (sub.end_display_time == static_cast (-1)) { + /* End time is not known */ + return FFmpegSubtitlePeriod (packet_time + ContentTime::from_seconds (sub.start_display_time / 1e3)); + } + + return FFmpegSubtitlePeriod ( packet_time + ContentTime::from_seconds (sub.start_display_time / 1e3), packet_time + ContentTime::from_seconds (sub.end_display_time / 1e3) ); - - return period; } map diff --git a/src/lib/util.h b/src/lib/util.h index c1f7a78c7..44bd7dced 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -74,7 +74,24 @@ extern int dcp_audio_frame_rate (int); extern int stride_round_up (int, int const *, int); extern int round_to (float n, int r); extern void* wrapped_av_malloc (size_t); -extern ContentTimePeriod subtitle_period (AVSubtitle const &); + +class FFmpegSubtitlePeriod +{ +public: + FFmpegSubtitlePeriod (ContentTime f) + : from (f) + {} + + FFmpegSubtitlePeriod (ContentTime f, ContentTime t) + : from (f) + , to (t) + {} + + ContentTime from; + boost::optional to; +}; + +extern FFmpegSubtitlePeriod subtitle_period (AVSubtitle const &); extern void set_backtrace_file (boost::filesystem::path); extern dcp::FrameInfo read_frame_info (FILE* file, int frame, Eyes eyes); extern void write_frame_info (FILE* file, int frame, Eyes eyes, dcp::FrameInfo info); -- cgit v1.2.3 From 83e5567530d0be24490abdda46d196e4279c5030 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 13 May 2015 00:41:50 +0100 Subject: Look up unknown subtitle end times from the data prepared by the examiner. --- src/lib/ffmpeg_content.cc | 15 +++---------- src/lib/ffmpeg_content.h | 2 +- src/lib/ffmpeg_decoder.cc | 46 +++++++++++++++------------------------ src/lib/ffmpeg_decoder.h | 4 ---- src/lib/ffmpeg_examiner.cc | 4 ++-- src/lib/ffmpeg_subtitle_stream.cc | 42 ++++++++++++++++++++++++++++++----- src/lib/ffmpeg_subtitle_stream.h | 9 ++++++-- src/lib/util.cc | 1 + 8 files changed, 68 insertions(+), 55 deletions(-) (limited to 'src/lib') diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc index 3a42b169f..ad9096b92 100644 --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_content.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 @@ -377,21 +377,12 @@ FFmpegContent::audio_analysis_path () const list FFmpegContent::subtitles_during (ContentTimePeriod period, bool starting) const { - list d; - shared_ptr stream = subtitle_stream (); if (!stream) { - return d; - } - - /* XXX: inefficient */ - for (vector::const_iterator i = stream->periods.begin(); i != stream->periods.end(); ++i) { - if ((starting && period.contains (i->from)) || (!starting && period.overlaps (*i))) { - d.push_back (*i); - } + return list (); } - return d; + return stream->subtitles_during (period, starting); } bool diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h index 76ba43567..d6edb2bdb 100644 --- a/src/lib/ffmpeg_content.h +++ b/src/lib/ffmpeg_content.h @@ -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 diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 5dc06fa8e..a3d647cde 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -426,35 +426,30 @@ FFmpegDecoder::decode_subtitle_packet () if (avcodec_decode_subtitle2 (subtitle_codec_context(), &sub, &got_subtitle, &_packet) < 0 || !got_subtitle) { return; } - - /* Subtitle PTS (within the source, not taking into account any of the - source that we may have chopped off for the DCP) - */ - FFmpegSubtitlePeriod period = subtitle_period (sub); - period.from += _pts_offset; - if (period.to) { - period.to = period.to.get() + _pts_offset; - } if (sub.num_rects <= 0) { /* Sometimes we get an empty AVSubtitle, which is used by some codecs to - indicate that the previous subtitle should stop. Emit the pending one. + indicate that the previous subtitle should stop. We can ignore it here. */ - if (_pending_subtitle_from && _pending_subtitle_image && _pending_subtitle_rect) { - image_subtitle ( - ContentTimePeriod (_pending_subtitle_from.get(), period.from), - _pending_subtitle_image, - _pending_subtitle_rect.get () - ); - _pending_subtitle_from = optional (); - _pending_subtitle_image.reset (); - _pending_subtitle_rect = optional > (); - } return; } else if (sub.num_rects > 1) { throw DecodeError (_("multi-part subtitles not yet supported")); } - + + /* Subtitle PTS (within the source, not taking into account any of the + source that we may have chopped off for the DCP). + */ + FFmpegSubtitlePeriod sub_period = subtitle_period (sub); + ContentTimePeriod period; + period.from = sub_period.from + _pts_offset; + if (sub_period.to) { + /* We already know the subtitle period `to' time */ + period.to = sub_period.to.get() + _pts_offset; + } else { + /* We have to look up the `to' time in the stream's records */ + period.to = ffmpeg_content()->subtitle_stream()->find_subtitle_to (sub_period.from); + } + AVSubtitleRect const * rect = sub.rects[0]; if (rect->type != SUBTITLE_BITMAP) { @@ -494,14 +489,7 @@ FFmpegDecoder::decode_subtitle_packet () static_cast (rect->h) / vs.height ); - if (period.to) { - image_subtitle (ContentTimePeriod (period.from, period.to.get()), image, scaled_rect); - } else { - /* We don't know when this subtitle stops, so store it until we find out */ - _pending_subtitle_from = period.from; - _pending_subtitle_image = image; - _pending_subtitle_rect = scaled_rect; - } + image_subtitle (ContentTimePeriod (period.from, period.to), image, scaled_rect); avsubtitle_free (&sub); } diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index b5bcdd358..0a0eea18a 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -67,10 +67,6 @@ private: void maybe_add_subtitle (); boost::shared_ptr deinterleave_audio (uint8_t** data, int size); - boost::optional _pending_subtitle_from; - boost::shared_ptr _pending_subtitle_image; - boost::optional > _pending_subtitle_rect; - std::list image_subtitles_during (ContentTimePeriod, bool starting) const; std::list text_subtitles_during (ContentTimePeriod, bool starting) const; diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc index e4f4e6f29..8afd4c164 100644 --- a/src/lib/ffmpeg_examiner.cc +++ b/src/lib/ffmpeg_examiner.cc @@ -152,11 +152,11 @@ FFmpegExaminer::subtitle_packet (AVCodecContext* context, shared_ptr= 0 && frame_finished) { FFmpegSubtitlePeriod const period = subtitle_period (sub); if (sub.num_rects <= 0 && _last_subtitle_start) { - stream->periods.push_back (ContentTimePeriod (_last_subtitle_start.get (), period.from)); + stream->add_subtitle (ContentTimePeriod (_last_subtitle_start.get (), period.from)); _last_subtitle_start = optional (); } else if (sub.num_rects == 1) { if (period.to) { - stream->periods.push_back (ContentTimePeriod (period.from, period.to.get ())); + stream->add_subtitle (ContentTimePeriod (period.from, period.to.get ())); } else { _last_subtitle_start = period.from; } diff --git a/src/lib/ffmpeg_subtitle_stream.cc b/src/lib/ffmpeg_subtitle_stream.cc index 66b587209..77a56e330 100644 --- a/src/lib/ffmpeg_subtitle_stream.cc +++ b/src/lib/ffmpeg_subtitle_stream.cc @@ -23,6 +23,8 @@ #include using std::string; +using std::map; +using std::list; /** Construct a SubtitleStream from a value returned from to_string(). * @param t String returned from to_string(). @@ -32,7 +34,7 @@ FFmpegSubtitleStream::FFmpegSubtitleStream (cxml::ConstNodePtr node) : FFmpegStream (node) { BOOST_FOREACH (cxml::NodePtr i, node->node_children ("Period")) { - periods.push_back ( + add_subtitle ( ContentTimePeriod ( ContentTime (node->number_child ("From")), ContentTime (node->number_child ("To")) @@ -46,9 +48,39 @@ FFmpegSubtitleStream::as_xml (xmlpp::Node* root) const { FFmpegStream::as_xml (root); - BOOST_FOREACH (ContentTimePeriod const & i, periods) { - xmlpp::Node* node = root->add_child ("Period"); - node->add_child("From")->add_child_text (raw_convert (i.from.get ())); - node->add_child("To")->add_child_text (raw_convert (i.to.get ())); + for (map::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { + xmlpp::Node* node = root->add_child ("Subtitle"); + node->add_child("From")->add_child_text (raw_convert (i->first.get ())); + node->add_child("To")->add_child_text (raw_convert (i->second.get ())); } } + +void +FFmpegSubtitleStream::add_subtitle (ContentTimePeriod period) +{ + DCPOMATIC_ASSERT (_subtitles.find (period.from) == _subtitles.end ()); + _subtitles[period.from] = period.to; +} + +list +FFmpegSubtitleStream::subtitles_during (ContentTimePeriod period, bool starting) const +{ + list d; + + /* XXX: inefficient */ + for (map::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { + if ((starting && period.contains (i->first)) || (!starting && period.overlaps (ContentTimePeriod (i->first, i->second)))) { + d.push_back (ContentTimePeriod (i->first, i->second)); + } + } + + return d; +} + +ContentTime +FFmpegSubtitleStream::find_subtitle_to (ContentTime from) const +{ + map::const_iterator i = _subtitles.find (from); + DCPOMATIC_ASSERT (i != _subtitles.end ()); + return i->second; +} diff --git a/src/lib/ffmpeg_subtitle_stream.h b/src/lib/ffmpeg_subtitle_stream.h index b16b825e7..3ed931b8c 100644 --- a/src/lib/ffmpeg_subtitle_stream.h +++ b/src/lib/ffmpeg_subtitle_stream.h @@ -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 @@ -31,6 +31,11 @@ public: void as_xml (xmlpp::Node *) const; - std::vector periods; + void add_subtitle (ContentTimePeriod period); + std::list subtitles_during (ContentTimePeriod period, bool starting) const; + ContentTime find_subtitle_to (ContentTime from) const; + +private: + std::map _subtitles; }; diff --git a/src/lib/util.cc b/src/lib/util.cc index d8c754607..99d9ba2c4 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -670,3 +670,4 @@ write_frame_info (FILE* file, int frame, Eyes eyes, dcp::FrameInfo info) fwrite (&info.size, sizeof (info.size), 1, file); fwrite (info.hash.c_str(), 1, info.hash.size(), file); } + -- cgit v1.2.3 From 9922c1f2eaea674ba2ff6cce5f5853655fd8ad7a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 13 May 2015 10:15:26 +0100 Subject: Don't bother decoding video frames when we're seeking around trying to find subtitles. --- src/lib/audio_decoder.cc | 4 ++-- src/lib/dcp_decoder.cc | 2 +- src/lib/dcp_decoder.h | 3 ++- src/lib/dcp_subtitle_decoder.cc | 2 +- src/lib/dcp_subtitle_decoder.h | 2 +- src/lib/decoder.h | 12 ++++++++++-- src/lib/ffmpeg_decoder.cc | 9 +++++---- src/lib/ffmpeg_decoder.h | 2 +- src/lib/image_decoder.cc | 2 +- src/lib/image_decoder.h | 5 ++--- src/lib/sndfile_decoder.cc | 2 +- src/lib/sndfile_decoder.h | 5 ++--- src/lib/subrip_decoder.cc | 2 +- src/lib/subrip_decoder.h | 2 +- src/lib/subtitle_decoder.cc | 4 +++- src/lib/video_decoder.cc | 6 +++--- test/audio_decoder_test.cc | 2 +- 17 files changed, 38 insertions(+), 28 deletions(-) (limited to 'src/lib') diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc index 22376e3e3..f6133947a 100644 --- a/src/lib/audio_decoder.cc +++ b/src/lib/audio_decoder.cc @@ -80,10 +80,10 @@ AudioDecoder::get_audio (AudioFrame frame, AudioFrame length, bool accurate) */ if (accurate) { /* Keep stuffing data into _decoded_audio until we have enough data, or the subclass does not want to give us any more */ - while ((_decoded_audio.frame > frame || (_decoded_audio.frame + _decoded_audio.audio->frames()) < end) && !pass ()) {} + while ((_decoded_audio.frame > frame || (_decoded_audio.frame + _decoded_audio.audio->frames()) < end) && !pass (PASS_REASON_AUDIO)) {} decoded_offset = frame - _decoded_audio.frame; } else { - while (_decoded_audio.audio->frames() < length && !pass ()) {} + while (_decoded_audio.audio->frames() < length && !pass (PASS_REASON_AUDIO)) {} /* Use decoded_offset of 0, as we don't really care what frames we return */ } diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc index ab0df8a86..3bfbd7720 100644 --- a/src/lib/dcp_decoder.cc +++ b/src/lib/dcp_decoder.cc @@ -55,7 +55,7 @@ DCPDecoder::DCPDecoder (shared_ptr c) } bool -DCPDecoder::pass () +DCPDecoder::pass (PassReason) { if (_reel == _reels.end () || !_dcp_content->can_be_played ()) { return true; diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h index 20ca2bb67..3a05325c7 100644 --- a/src/lib/dcp_decoder.h +++ b/src/lib/dcp_decoder.h @@ -38,8 +38,9 @@ public: DCPDecoder (boost::shared_ptr); private: + bool pass (PassReason); void seek (ContentTime t, bool accurate); - bool pass (); + std::list image_subtitles_during (ContentTimePeriod, bool starting) const; std::list text_subtitles_during (ContentTimePeriod, bool starting) const; diff --git a/src/lib/dcp_subtitle_decoder.cc b/src/lib/dcp_subtitle_decoder.cc index 236e99996..93a122590 100644 --- a/src/lib/dcp_subtitle_decoder.cc +++ b/src/lib/dcp_subtitle_decoder.cc @@ -46,7 +46,7 @@ DCPSubtitleDecoder::seek (ContentTime time, bool accurate) } bool -DCPSubtitleDecoder::pass () +DCPSubtitleDecoder::pass (PassReason) { if (_next == _subtitles.end ()) { return true; diff --git a/src/lib/dcp_subtitle_decoder.h b/src/lib/dcp_subtitle_decoder.h index 9a16fb7c8..52e400416 100644 --- a/src/lib/dcp_subtitle_decoder.h +++ b/src/lib/dcp_subtitle_decoder.h @@ -28,8 +28,8 @@ public: DCPSubtitleDecoder (boost::shared_ptr); protected: + bool pass (PassReason); void seek (ContentTime time, bool accurate); - bool pass (); private: std::list image_subtitles_during (ContentTimePeriod, bool starting) const; diff --git a/src/lib/decoder.h b/src/lib/decoder.h index c1b859865..0703a5426 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2013 Carl Hetherington + Copyright (C) 2012-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 @@ -41,6 +41,7 @@ public: virtual ~Decoder () {} protected: + /** Seek so that the next pass() will yield the next thing * (video/sound frame, subtitle etc.) at or after the requested * time. Pass accurate = true to try harder to ensure that, at worst, @@ -50,7 +51,14 @@ protected: * it may seek to just the right spot. */ virtual void seek (ContentTime time, bool accurate) = 0; - virtual bool pass () = 0; + + enum PassReason { + PASS_REASON_VIDEO, + PASS_REASON_AUDIO, + PASS_REASON_SUBTITLE + }; + + virtual bool pass (PassReason reason) = 0; }; #endif diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index a3d647cde..6122a547e 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -132,7 +132,7 @@ FFmpegDecoder::flush () } bool -FFmpegDecoder::pass () +FFmpegDecoder::pass (PassReason reason) { int r = av_read_frame (_format_context, &_packet); @@ -153,12 +153,13 @@ FFmpegDecoder::pass () } int const si = _packet.stream_index; + shared_ptr fc = _ffmpeg_content; - if (si == _video_stream && !_ignore_video) { + if (si == _video_stream && !_ignore_video && reason != PASS_REASON_SUBTITLE) { decode_video_packet (); - } else if (_ffmpeg_content->audio_stream() && _ffmpeg_content->audio_stream()->uses_index (_format_context, si)) { + } else if (fc->audio_stream() && fc->audio_stream()->uses_index (_format_context, si) && reason != PASS_REASON_SUBTITLE) { decode_audio_packet (); - } else if (_ffmpeg_content->subtitle_stream() && _ffmpeg_content->subtitle_stream()->uses_index (_format_context, si)) { + } else if (fc->subtitle_stream() && fc->subtitle_stream()->uses_index (_format_context, si)) { decode_subtitle_packet (); } diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 0a0eea18a..616fa88dd 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -53,8 +53,8 @@ public: private: friend struct ::ffmpeg_pts_offset_test; + bool pass (PassReason reason); void seek (ContentTime time, bool); - bool pass (); void flush (); AVSampleFormat audio_sample_format () const; diff --git a/src/lib/image_decoder.cc b/src/lib/image_decoder.cc index 78201fc23..250c8f845 100644 --- a/src/lib/image_decoder.cc +++ b/src/lib/image_decoder.cc @@ -43,7 +43,7 @@ ImageDecoder::ImageDecoder (shared_ptr c) } bool -ImageDecoder::pass () +ImageDecoder::pass (PassReason) { if (_video_position >= _image_content->video_length().frames (_image_content->video_frame_rate ())) { return true; diff --git a/src/lib/image_decoder.h b/src/lib/image_decoder.h index 242f69477..ec90051da 100644 --- a/src/lib/image_decoder.h +++ b/src/lib/image_decoder.h @@ -34,10 +34,9 @@ public: return _image_content; } - void seek (ContentTime, bool); - private: - bool pass (); + bool pass (PassReason); + void seek (ContentTime, bool); boost::shared_ptr _image_content; boost::shared_ptr _image; diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc index 602014d58..09059a8b0 100644 --- a/src/lib/sndfile_decoder.cc +++ b/src/lib/sndfile_decoder.cc @@ -65,7 +65,7 @@ SndfileDecoder::~SndfileDecoder () } bool -SndfileDecoder::pass () +SndfileDecoder::pass (PassReason) { if (_remaining == 0) { return true; diff --git a/src/lib/sndfile_decoder.h b/src/lib/sndfile_decoder.h index 5ebe1da7b..68c8633a0 100644 --- a/src/lib/sndfile_decoder.h +++ b/src/lib/sndfile_decoder.h @@ -30,14 +30,13 @@ public: SndfileDecoder (boost::shared_ptr c); ~SndfileDecoder (); - void seek (ContentTime, bool); - int audio_channels () const; ContentTime audio_length () const; int audio_frame_rate () const; private: - bool pass (); + bool pass (PassReason); + void seek (ContentTime, bool); boost::shared_ptr _sndfile_content; SNDFILE* _sndfile; diff --git a/src/lib/subrip_decoder.cc b/src/lib/subrip_decoder.cc index bef48ba78..6ed2e5254 100644 --- a/src/lib/subrip_decoder.cc +++ b/src/lib/subrip_decoder.cc @@ -48,7 +48,7 @@ SubRipDecoder::seek (ContentTime time, bool accurate) } bool -SubRipDecoder::pass () +SubRipDecoder::pass (PassReason) { if (_next >= _subtitles.size ()) { return true; diff --git a/src/lib/subrip_decoder.h b/src/lib/subrip_decoder.h index 876f763d3..264ca8899 100644 --- a/src/lib/subrip_decoder.h +++ b/src/lib/subrip_decoder.h @@ -32,7 +32,7 @@ public: protected: void seek (ContentTime time, bool accurate); - bool pass (); + bool pass (PassReason); private: std::list image_subtitles_during (ContentTimePeriod, bool starting) const; diff --git a/src/lib/subtitle_decoder.cc b/src/lib/subtitle_decoder.cc index ac9a67e33..edb291ab8 100644 --- a/src/lib/subtitle_decoder.cc +++ b/src/lib/subtitle_decoder.cc @@ -70,7 +70,7 @@ SubtitleDecoder::get (list const & subs, list const & sp, * (a) give us what we want, or * (b) hit the end of the decoder. */ - while (!pass() && (subs.empty() || (subs.back().period().to < sp.back().to))) {} + while (!pass(PASS_REASON_SUBTITLE) && (subs.empty() || (subs.back().period().to < sp.back().to))) {} /* Now look for what we wanted in the data we have collected */ /* XXX: inefficient */ @@ -82,6 +82,8 @@ SubtitleDecoder::get (list const & subs, list const & sp, } } + /* XXX: should clear out _decoded_* at some point */ + return out; } diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index b7cf1641b..31dc3cdc2 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -96,7 +96,7 @@ VideoDecoder::get_video (VideoFrame frame, bool accurate) break; } - if (pass ()) { + if (pass (PASS_REASON_VIDEO)) { /* The decoder has nothing more for us */ break; } @@ -113,7 +113,7 @@ VideoDecoder::get_video (VideoFrame frame, bool accurate) dec = decoded_video (frame); } else { /* Any frame will do: use the first one that comes out of pass() */ - while (_decoded_video.empty() && !pass ()) {} + while (_decoded_video.empty() && !pass (PASS_REASON_VIDEO)) {} if (!_decoded_video.empty ()) { dec.push_back (_decoded_video.front ()); } @@ -237,7 +237,7 @@ VideoDecoder::video (shared_ptr image, VideoFrame frame) if (_ignore_video) { return; } - + /* We may receive the same frame index twice for 3D, and we need to know when that happens. */ diff --git a/test/audio_decoder_test.cc b/test/audio_decoder_test.cc index a14e2f9be..b1f672356 100644 --- a/test/audio_decoder_test.cc +++ b/test/audio_decoder_test.cc @@ -40,7 +40,7 @@ public: , _position (0) {} - bool pass () + bool pass (PassReason) { AudioFrame const N = min ( AudioFrame (2000), -- cgit v1.2.3 From 4447fc2edd80d85c9779187e4f3a0405e6e4d443 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 13 May 2015 10:23:44 +0100 Subject: Don't allow image subtitle store to get too big. --- src/lib/subtitle_decoder.cc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'src/lib') diff --git a/src/lib/subtitle_decoder.cc b/src/lib/subtitle_decoder.cc index edb291ab8..2efe9afb6 100644 --- a/src/lib/subtitle_decoder.cc +++ b/src/lib/subtitle_decoder.cc @@ -82,7 +82,22 @@ SubtitleDecoder::get (list const & subs, list const & sp, } } - /* XXX: should clear out _decoded_* at some point */ + /* Discard anything in _decoded_image_subtitles that is outside 5 seconds either side of period */ + + list::iterator i = _decoded_image_subtitles.begin(); + while (i != _decoded_image_subtitles.end()) { + list::iterator tmp = i; + ++tmp; + + if ( + i->period().to < (period.from - ContentTime::from_seconds (5)) || + i->period().from > (period.to + ContentTime::from_seconds (5)) + ) { + _decoded_image_subtitles.erase (i); + } + + i = tmp; + } return out; } -- cgit v1.2.3 From 55bbed60ce090b8f84194b546e2c8d4ebed2eb2f Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 13 May 2015 11:16:14 +0100 Subject: Fix missing audio mapping digest in FFmpegContent audio analysis paths. --- src/lib/ffmpeg_content.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/lib') diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc index ad9096b92..496dac8d8 100644 --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_content.cc @@ -365,12 +365,10 @@ FFmpegContent::audio_analysis_path () const analyses for each stream. */ - boost::filesystem::path p = film->audio_analysis_dir (); - string name = digest(); + boost::filesystem::path p = AudioContent::audio_analysis_path (); if (audio_stream ()) { - name += "_" + audio_stream()->identifier (); + p += "_" + audio_stream()->identifier (); } - p /= name; return p; } -- cgit v1.2.3 From 9e4dda377dc53c90ae6e817f0a1989772637406e Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 13 May 2015 12:52:46 +0100 Subject: Try to fix build. --- src/lib/ffmpeg_content.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib') diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc index 496dac8d8..a52b53b04 100644 --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_content.cc @@ -367,7 +367,7 @@ FFmpegContent::audio_analysis_path () const boost::filesystem::path p = AudioContent::audio_analysis_path (); if (audio_stream ()) { - p += "_" + audio_stream()->identifier (); + p = p.string() + "_" + audio_stream()->identifier (); } return p; } -- cgit v1.2.3 From 05c37bfdb86be26497d5baa448a0cbda20e33bed Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 13 May 2015 21:57:40 +0100 Subject: Fix crashes on x-thread signal emission. Fix crashes on x-thread signal emission if the emitting object is destroyed between the storage of the message on the queue and the emission of the object in the UI thread. --- src/lib/content.cc | 4 +- src/lib/content.h | 3 +- src/lib/film.cc | 9 +--- src/lib/film.h | 3 +- src/lib/job.cc | 12 ++--- src/lib/job.h | 3 +- src/lib/job_manager.cc | 8 +-- src/lib/job_manager.h | 3 +- src/lib/server_finder.cc | 2 +- src/lib/server_finder.h | 3 +- src/lib/signaller.h | 131 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib/ui_signaller.h | 38 ++++++++------ src/lib/update.cc | 2 +- src/lib/update.h | 3 +- 14 files changed, 175 insertions(+), 49 deletions(-) create mode 100644 src/lib/signaller.h (limited to 'src/lib') diff --git a/src/lib/content.cc b/src/lib/content.cc index fcc658717..b00ea3e57 100644 --- a/src/lib/content.cc +++ b/src/lib/content.cc @@ -153,9 +153,7 @@ Content::examine (shared_ptr job) void Content::signal_changed (int p) { - if (ui_signaller) { - ui_signaller->emit (boost::bind (boost::ref (Changed), shared_from_this (), p, _change_signals_frequent)); - } + emit (boost::bind (boost::ref (Changed), shared_from_this (), p, _change_signals_frequent)); } void diff --git a/src/lib/content.h b/src/lib/content.h index c6cede5fa..2b966110b 100644 --- a/src/lib/content.h +++ b/src/lib/content.h @@ -25,6 +25,7 @@ #define DCPOMATIC_CONTENT_H #include "types.h" +#include "signaller.h" #include "dcpomatic_time.h" #include #include @@ -53,7 +54,7 @@ public: /** @class Content * @brief A piece of content represented by one or more files on disk. */ -class Content : public boost::enable_shared_from_this, public boost::noncopyable +class Content : public boost::enable_shared_from_this, public Signaller, public boost::noncopyable { public: Content (boost::shared_ptr); diff --git a/src/lib/film.cc b/src/lib/film.cc index 80755a4cb..35773c797 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -32,7 +32,6 @@ #include "exceptions.h" #include "examine_content_job.h" #include "config.h" -#include "ui_signaller.h" #include "playlist.h" #include "player.h" #include "dcp_content_type.h" @@ -790,9 +789,7 @@ Film::signal_changed (Property p) break; } - if (ui_signaller) { - ui_signaller->emit (boost::bind (boost::ref (Changed), p)); - } + emit (boost::bind (boost::ref (Changed), p)); } void @@ -995,9 +992,7 @@ Film::playlist_content_changed (boost::weak_ptr c, int p) signal_changed (NAME); } - if (ui_signaller) { - ui_signaller->emit (boost::bind (boost::ref (ContentChanged), c, p)); - } + emit (boost::bind (boost::ref (ContentChanged), c, p)); } void diff --git a/src/lib/film.h b/src/lib/film.h index 3cd370a0d..f61062be0 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -29,6 +29,7 @@ #include "types.h" #include "isdcf_metadata.h" #include "frame_rate_change.h" +#include "signaller.h" #include "ratio.h" #include #include @@ -55,7 +56,7 @@ struct isdcf_name_test; * * The content of a Film is held in a Playlist (created and managed by the Film). */ -class Film : public boost::enable_shared_from_this, public boost::noncopyable +class Film : public boost::enable_shared_from_this, public Signaller, public boost::noncopyable { public: Film (boost::filesystem::path, bool log = true); diff --git a/src/lib/job.cc b/src/lib/job.cc index eadafbf73..f28146632 100644 --- a/src/lib/job.cc +++ b/src/lib/job.cc @@ -203,8 +203,8 @@ Job::set_state (State s) } } - if (finished && ui_signaller) { - ui_signaller->emit (boost::bind (boost::ref (Finished))); + if (finished) { + emit (boost::bind (boost::ref (Finished))); } } @@ -239,9 +239,7 @@ Job::set_progress (float p, bool force) _pause_changed.wait (lm2); } - if (ui_signaller) { - ui_signaller->emit (boost::bind (boost::ref (Progress))); - } + emit (boost::bind (boost::ref (Progress))); } /** @return fractional progress of the current sub-job, if known */ @@ -301,9 +299,7 @@ Job::set_progress_unknown () _progress.reset (); lm.unlock (); - if (ui_signaller) { - ui_signaller->emit (boost::bind (boost::ref (Progress))); - } + emit (boost::bind (boost::ref (Progress))); } /** @return Human-readable status of this job */ diff --git a/src/lib/job.h b/src/lib/job.h index 7c6707880..8fe87747c 100644 --- a/src/lib/job.h +++ b/src/lib/job.h @@ -24,6 +24,7 @@ #ifndef DCPOMATIC_JOB_H #define DCPOMATIC_JOB_H +#include "signaller.h" #include #include #include @@ -35,7 +36,7 @@ class Film; /** @class Job * @brief A parent class to represent long-running tasks which are run in their own thread. */ -class Job : public boost::enable_shared_from_this, public boost::noncopyable +class Job : public boost::enable_shared_from_this, public Signaller, public boost::noncopyable { public: Job (boost::shared_ptr); diff --git a/src/lib/job_manager.cc b/src/lib/job_manager.cc index 2b727b0aa..63db662d0 100644 --- a/src/lib/job_manager.cc +++ b/src/lib/job_manager.cc @@ -64,9 +64,7 @@ JobManager::add (shared_ptr j) _jobs.push_back (j); } - if (ui_signaller) { - ui_signaller->emit (boost::bind (boost::ref (JobAdded), weak_ptr (j))); - } + emit (boost::bind (boost::ref (JobAdded), weak_ptr (j))); return j; } @@ -138,9 +136,7 @@ JobManager::scheduler () if (active_jobs != _last_active_jobs) { _last_active_jobs = active_jobs; - if (ui_signaller) { - ui_signaller->emit (boost::bind (boost::ref (ActiveJobsChanged), active_jobs)); - } + emit (boost::bind (boost::ref (ActiveJobsChanged), active_jobs)); } dcpomatic_sleep (1); diff --git a/src/lib/job_manager.h b/src/lib/job_manager.h index 9d8620cbb..b946c1a98 100644 --- a/src/lib/job_manager.h +++ b/src/lib/job_manager.h @@ -21,6 +21,7 @@ * @brief A simple scheduler for jobs. */ +#include "signaller.h" #include #include #include @@ -32,7 +33,7 @@ extern void wait_for_jobs (); /** @class JobManager * @brief A simple scheduler for jobs. */ -class JobManager : public boost::noncopyable +class JobManager : public Signaller, public boost::noncopyable { public: diff --git a/src/lib/server_finder.cc b/src/lib/server_finder.cc index 979046dab..72a9a4ef5 100644 --- a/src/lib/server_finder.cc +++ b/src/lib/server_finder.cc @@ -173,7 +173,7 @@ ServerFinder::handle_accept (boost::system::error_code ec, shared_ptr so boost::mutex::scoped_lock lm (_mutex); _servers.push_back (sd); } - ui_signaller->emit (boost::bind (boost::ref (ServerFound), sd)); + emit (boost::bind (boost::ref (ServerFound), sd)); } start_accept (); diff --git a/src/lib/server_finder.h b/src/lib/server_finder.h index 3fab6864a..dc62f998d 100644 --- a/src/lib/server_finder.h +++ b/src/lib/server_finder.h @@ -18,9 +18,10 @@ */ #include "server.h" +#include "signaller.h" #include -class ServerFinder : public ExceptionStore +class ServerFinder : public Signaller, public ExceptionStore { public: boost::signals2::connection connect (boost::function); diff --git a/src/lib/signaller.h b/src/lib/signaller.h new file mode 100644 index 000000000..408cfcf5b --- /dev/null +++ b/src/lib/signaller.h @@ -0,0 +1,131 @@ +/* + Copyright (C) 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 + 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, + 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. + +*/ + +#ifndef DCPOMATIC_SIGNALLER_H +#define DCPOMATIC_SIGNALLER_H + +#include "ui_signaller.h" +#include +#include + +class WrapperBase +{ +public: + WrapperBase () + : _valid (true) + , _finished (false) + {} + + virtual ~WrapperBase () {} + + /* Can be called from any thread */ + void invalidate () + { + boost::mutex::scoped_lock lm (_mutex); + _valid = false; + } + + bool finished () const { + boost::mutex::scoped_lock lm (_mutex); + return _finished; + } + +protected: + /* Protect _valid and _finished */ + mutable boost::mutex _mutex; + bool _valid; + bool _finished; +}; + +/** Helper class to manage lifetime of signals, specifically to address + * the problem where an object containing a signal is deleted before + * its signal is emitted. + */ +template +class Wrapper : public WrapperBase +{ +public: + Wrapper (T signal) + : _signal (signal) + { + + } + + /* Called by the UI thread only */ + void signal () + { + boost::mutex::scoped_lock lm (_mutex); + if (_valid) { + _signal (); + } + _finished = true; + } + +private: + T _signal; +}; + +/** Parent for any class which needs to raise cross-thread signals (from non-UI + * to UI). Subclasses should call, e.g. emit (boost::bind (boost::ref (MySignal), foo, bar)); + */ +class Signaller +{ +public: + /* Can be called from any thread */ + virtual ~Signaller () { + boost::mutex::scoped_lock lm (_mutex); + for (std::list::iterator i = _wrappers.begin(); i != _wrappers.end(); ++i) { + (*i)->invalidate (); + } + } + + /* Can be called from any thread */ + template + void emit (T signal) + { + Wrapper* w = new Wrapper (signal); + if (ui_signaller) { + ui_signaller->emit (boost::bind (&Wrapper::signal, w)); + } + + boost::mutex::scoped_lock lm (_mutex); + + /* Clean up finished Wrappers */ + std::list::iterator i = _wrappers.begin (); + while (i != _wrappers.end ()) { + std::list::iterator tmp = i; + ++tmp; + if ((*i)->finished ()) { + delete *i; + _wrappers.erase (i); + } + i = tmp; + } + + /* Add the new one */ + _wrappers.push_back (w); + } + +private: + /* Protect _wrappers */ + boost::mutex _mutex; + std::list _wrappers; +}; + +#endif diff --git a/src/lib/ui_signaller.h b/src/lib/ui_signaller.h index ee4d230d4..9d4495cd1 100644 --- a/src/lib/ui_signaller.h +++ b/src/lib/ui_signaller.h @@ -24,6 +24,8 @@ #include #include +class Signaller; + /** A class to allow signals to be emitted from non-UI threads and handled * by a UI thread. */ @@ -37,23 +39,6 @@ public: _ui_thread = boost::this_thread::get_id (); } - /** Emit a signal from any thread whose handlers will be called in the UI - * thread. Use something like: - * - * ui_signaller->emit (boost::bind (boost::ref (SomeSignal), parameter)); - */ - template - void emit (T f) { - if (boost::this_thread::get_id() == _ui_thread) { - /* already in the UI thread */ - f (); - } else { - /* non-UI thread; post to the service and wake up the UI */ - _service.post (f); - wake_ui (); - } - } - /* Do something next time the UI is idle */ template void when_idle (T f) { @@ -73,6 +58,25 @@ public: } private: + /** Emit a signal from any thread whose handlers will be called in the UI + * thread. Use something like: + * + * ui_signaller->emit (boost::bind (boost::ref (SomeSignal), parameter)); + */ + template + void emit (T f) { + if (boost::this_thread::get_id() == _ui_thread) { + /* already in the UI thread */ + f (); + } else { + /* non-UI thread; post to the service and wake up the UI */ + _service.post (f); + wake_ui (); + } + } + + friend class Signaller; + /** A io_service which is used as the conduit for messages */ boost::asio::io_service _service; /** Object required to keep io_service from stopping when it has nothing to do */ diff --git a/src/lib/update.cc b/src/lib/update.cc index a05df8ef3..26944ecc3 100644 --- a/src/lib/update.cc +++ b/src/lib/update.cc @@ -168,7 +168,7 @@ UpdateChecker::set_state (State s) _emits++; } - ui_signaller->emit (boost::bind (boost::ref (StateChanged))); + emit (boost::bind (boost::ref (StateChanged))); } UpdateChecker * diff --git a/src/lib/update.h b/src/lib/update.h index 5bb9e9501..461217a37 100644 --- a/src/lib/update.h +++ b/src/lib/update.h @@ -21,6 +21,7 @@ * @brief UpdateChecker class. */ +#include "signaller.h" #include #include #include @@ -30,7 +31,7 @@ struct update_checker_test; /** Class to check for the existance of an update for DCP-o-matic on a remote server */ -class UpdateChecker : public boost::noncopyable +class UpdateChecker : public Signaller, public boost::noncopyable { public: UpdateChecker (); -- cgit v1.2.3 From 04bd447fd8960625bda5081cbac235b848d7631f Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 13 May 2015 22:28:50 +0100 Subject: Rename UISignaller -> SignalManager. --- src/lib/content.cc | 1 - src/lib/job.cc | 1 - src/lib/job_manager.cc | 1 - src/lib/server_finder.cc | 1 - src/lib/signal_manager.cc | 23 ++++++++++ src/lib/signal_manager.h | 90 +++++++++++++++++++++++++++++++++++++ src/lib/signaller.h | 6 +-- src/lib/ui_signaller.cc | 24 ---------- src/lib/ui_signaller.h | 90 ------------------------------------- src/lib/update.cc | 1 - src/lib/wscript | 2 +- src/tools/dcpomatic.cc | 8 ++-- src/tools/dcpomatic_batch.cc | 6 +-- src/tools/dcpomatic_cli.cc | 4 +- src/tools/dcpomatic_create.cc | 10 ++--- src/wx/dolby_certificate_dialog.cc | 10 ++--- src/wx/doremi_certificate_dialog.cc | 4 +- src/wx/wscript | 2 +- src/wx/wx_signal_manager.cc | 34 ++++++++++++++ src/wx/wx_signal_manager.h | 36 +++++++++++++++ src/wx/wx_ui_signaller.cc | 34 -------------- src/wx/wx_ui_signaller.h | 36 --------------- test/test.cc | 10 ++--- 23 files changed, 214 insertions(+), 220 deletions(-) create mode 100644 src/lib/signal_manager.cc create mode 100644 src/lib/signal_manager.h delete mode 100644 src/lib/ui_signaller.cc delete mode 100644 src/lib/ui_signaller.h create mode 100644 src/wx/wx_signal_manager.cc create mode 100644 src/wx/wx_signal_manager.h delete mode 100644 src/wx/wx_ui_signaller.cc delete mode 100644 src/wx/wx_ui_signaller.h (limited to 'src/lib') diff --git a/src/lib/content.cc b/src/lib/content.cc index b00ea3e57..b9e8367e1 100644 --- a/src/lib/content.cc +++ b/src/lib/content.cc @@ -24,7 +24,6 @@ #include "content.h" #include "util.h" #include "content_factory.h" -#include "ui_signaller.h" #include "exceptions.h" #include "film.h" #include "safe_stringstream.h" diff --git a/src/lib/job.cc b/src/lib/job.cc index f28146632..c4d93ddc1 100644 --- a/src/lib/job.cc +++ b/src/lib/job.cc @@ -27,7 +27,6 @@ #include "job.h" #include "util.h" #include "cross.h" -#include "ui_signaller.h" #include "exceptions.h" #include "film.h" #include "log.h" diff --git a/src/lib/job_manager.cc b/src/lib/job_manager.cc index 63db662d0..b5b64a77e 100644 --- a/src/lib/job_manager.cc +++ b/src/lib/job_manager.cc @@ -26,7 +26,6 @@ #include "job_manager.h" #include "job.h" #include "cross.h" -#include "ui_signaller.h" using std::string; using std::list; diff --git a/src/lib/server_finder.cc b/src/lib/server_finder.cc index 72a9a4ef5..b4b400b52 100644 --- a/src/lib/server_finder.cc +++ b/src/lib/server_finder.cc @@ -22,7 +22,6 @@ #include "util.h" #include "config.h" #include "cross.h" -#include "ui_signaller.h" #include "dcpomatic_socket.h" #include "raw_convert.h" #include diff --git a/src/lib/signal_manager.cc b/src/lib/signal_manager.cc new file mode 100644 index 000000000..7c2b3e11a --- /dev/null +++ b/src/lib/signal_manager.cc @@ -0,0 +1,23 @@ +/* + Copyright (C) 2012-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 + 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, + 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. + +*/ + +#include "signal_manager.h" + +/** Global SignalManager instance */ +SignalManager* signal_manager = 0; diff --git a/src/lib/signal_manager.h b/src/lib/signal_manager.h new file mode 100644 index 000000000..ae4306e30 --- /dev/null +++ b/src/lib/signal_manager.h @@ -0,0 +1,90 @@ +/* + Copyright (C) 2012-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 + 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, + 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. + +*/ + +#ifndef DCPOMATIC_SIGNAL_MANAGER_H +#define DCPOMATIC_SIGNAL_MANAGER_H + +#include +#include +#include + +class Signaller; + +/** A class to allow signals to be emitted from non-UI threads and handled + * by a UI thread. + */ +class SignalManager : public boost::noncopyable +{ +public: + /** Create a SignalManager. Must be called from the UI thread */ + SignalManager () + : _work (_service) + { + _ui_thread = boost::this_thread::get_id (); + } + + /* Do something next time the UI is idle */ + template + void when_idle (T f) { + _service.post (f); + } + + /** Call this in the UI when it is idle */ + size_t ui_idle () { + /* This executes any functors that have been post()ed to _service */ + return _service.poll (); + } + + /** This should wake the UI and make it call ui_idle() */ + virtual void wake_ui () { + /* This is only a sensible implementation when there is no GUI */ + ui_idle (); + } + +private: + /** Emit a signal from any thread whose handlers will be called in the UI + * thread. Use something like: + * + * ui_signaller->emit (boost::bind (boost::ref (SomeSignal), parameter)); + */ + template + void emit (T f) { + if (boost::this_thread::get_id() == _ui_thread) { + /* already in the UI thread */ + f (); + } else { + /* non-UI thread; post to the service and wake up the UI */ + _service.post (f); + wake_ui (); + } + } + + friend class Signaller; + + /** A io_service which is used as the conduit for messages */ + boost::asio::io_service _service; + /** Object required to keep io_service from stopping when it has nothing to do */ + boost::asio::io_service::work _work; + /** The UI thread's ID */ + boost::thread::id _ui_thread; +}; + +extern SignalManager* signal_manager; + +#endif diff --git a/src/lib/signaller.h b/src/lib/signaller.h index 408cfcf5b..4ef9b38b3 100644 --- a/src/lib/signaller.h +++ b/src/lib/signaller.h @@ -20,7 +20,7 @@ #ifndef DCPOMATIC_SIGNALLER_H #define DCPOMATIC_SIGNALLER_H -#include "ui_signaller.h" +#include "signal_manager.h" #include #include @@ -100,8 +100,8 @@ public: void emit (T signal) { Wrapper* w = new Wrapper (signal); - if (ui_signaller) { - ui_signaller->emit (boost::bind (&Wrapper::signal, w)); + if (signal_manager) { + signal_manager->emit (boost::bind (&Wrapper::signal, w)); } boost::mutex::scoped_lock lm (_mutex); diff --git a/src/lib/ui_signaller.cc b/src/lib/ui_signaller.cc deleted file mode 100644 index 4cb34da51..000000000 --- a/src/lib/ui_signaller.cc +++ /dev/null @@ -1,24 +0,0 @@ -/* - Copyright (C) 2012 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 - 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, - 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. - -*/ - -#include "ui_signaller.h" - -/** Global UISignaller instance */ -UISignaller* ui_signaller = 0; - diff --git a/src/lib/ui_signaller.h b/src/lib/ui_signaller.h deleted file mode 100644 index 9d4495cd1..000000000 --- a/src/lib/ui_signaller.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - Copyright (C) 2012 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 - 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, - 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. - -*/ - -#ifndef DCPOMATIC_UI_SIGNALLER_H -#define DCPOMATIC_UI_SIGNALLER_H - -#include -#include -#include - -class Signaller; - -/** A class to allow signals to be emitted from non-UI threads and handled - * by a UI thread. - */ -class UISignaller : public boost::noncopyable -{ -public: - /** Create a UISignaller. Must be called from the UI thread */ - UISignaller () - : _work (_service) - { - _ui_thread = boost::this_thread::get_id (); - } - - /* Do something next time the UI is idle */ - template - void when_idle (T f) { - _service.post (f); - } - - /** Call this in the UI when it is idle */ - size_t ui_idle () { - /* This executes any functors that have been post()ed to _service */ - return _service.poll (); - } - - /** This should wake the UI and make it call ui_idle() */ - virtual void wake_ui () { - /* This is only a sensible implementation when there is no GUI... */ - ui_idle (); - } - -private: - /** Emit a signal from any thread whose handlers will be called in the UI - * thread. Use something like: - * - * ui_signaller->emit (boost::bind (boost::ref (SomeSignal), parameter)); - */ - template - void emit (T f) { - if (boost::this_thread::get_id() == _ui_thread) { - /* already in the UI thread */ - f (); - } else { - /* non-UI thread; post to the service and wake up the UI */ - _service.post (f); - wake_ui (); - } - } - - friend class Signaller; - - /** A io_service which is used as the conduit for messages */ - boost::asio::io_service _service; - /** Object required to keep io_service from stopping when it has nothing to do */ - boost::asio::io_service::work _work; - /** The UI thread's ID */ - boost::thread::id _ui_thread; -}; - -extern UISignaller* ui_signaller; - -#endif diff --git a/src/lib/update.cc b/src/lib/update.cc index 26944ecc3..f433ff991 100644 --- a/src/lib/update.cc +++ b/src/lib/update.cc @@ -19,7 +19,6 @@ #include "update.h" #include "version.h" -#include "ui_signaller.h" #include "safe_stringstream.h" #include "config.h" #include "util.h" diff --git a/src/lib/wscript b/src/lib/wscript index 68897a4c4..5956c73d6 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -109,7 +109,7 @@ sources = """ transcode_job.cc transcoder.cc types.cc - ui_signaller.cc + signal_manager.cc update.cc upmixer_a.cc util.cc diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index e59220785..904e39fda 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -21,7 +21,7 @@ #include "lib/config.h" #include "lib/util.h" #include "lib/version.h" -#include "lib/ui_signaller.h" +#include "lib/signal_manager.h" #include "lib/log.h" #include "lib/job_manager.h" #include "lib/transcode_job.h" @@ -39,7 +39,7 @@ #include "wx/wx_util.h" #include "wx/new_film_dialog.h" #include "wx/properties_dialog.h" -#include "wx/wx_ui_signaller.h" +#include "wx/wx_signal_manager.h" #include "wx/about_dialog.h" #include "wx/kdm_dialog.h" #include "wx/servers_list_dialog.h" @@ -841,7 +841,7 @@ private: } } - ui_signaller = new wxUISignaller (this); + signal_manager = new wxSignalManager (this); Bind (wxEVT_IDLE, boost::bind (&App::idle, this)); Bind (wxEVT_TIMER, boost::bind (&App::check, this)); @@ -909,7 +909,7 @@ private: void idle () { - ui_signaller->ui_idle (); + signal_manager->ui_idle (); } void check () diff --git a/src/tools/dcpomatic_batch.cc b/src/tools/dcpomatic_batch.cc index da8a61414..ae2f3a2c5 100644 --- a/src/tools/dcpomatic_batch.cc +++ b/src/tools/dcpomatic_batch.cc @@ -29,7 +29,7 @@ #include "lib/job_manager.h" #include "wx/wx_util.h" #include "wx/about_dialog.h" -#include "wx/wx_ui_signaller.h" +#include "wx/wx_signal_manager.h" #include "wx/job_manager_view.h" using std::exception; @@ -225,7 +225,7 @@ class App : public wxApp f->Maximize (); f->Show (); - ui_signaller = new wxUISignaller (this); + signal_manager = new wxSignalManager (this); this->Bind (wxEVT_IDLE, boost::bind (&App::idle, this)); shared_ptr film; @@ -244,7 +244,7 @@ class App : public wxApp void idle () { - ui_signaller->ui_idle (); + signal_manager->ui_idle (); } void OnInitCmdLine (wxCmdLineParser& parser) diff --git a/src/tools/dcpomatic_cli.cc b/src/tools/dcpomatic_cli.cc index 4facdd4d1..0307cac9c 100644 --- a/src/tools/dcpomatic_cli.cc +++ b/src/tools/dcpomatic_cli.cc @@ -30,7 +30,7 @@ #include "lib/cross.h" #include "lib/config.h" #include "lib/log.h" -#include "lib/ui_signaller.h" +#include "lib/signal_manager.h" #include "lib/server_finder.h" #include "lib/json_server.h" @@ -119,7 +119,7 @@ main (int argc, char* argv[]) film_dir = argv[optind]; dcpomatic_setup (); - ui_signaller = new UISignaller (); + signal_manager = new SignalManager (); if (no_remote) { ServerFinder::instance()->disable (); diff --git a/src/tools/dcpomatic_create.cc b/src/tools/dcpomatic_create.cc index 304f4f697..d121eb0cc 100644 --- a/src/tools/dcpomatic_create.cc +++ b/src/tools/dcpomatic_create.cc @@ -28,7 +28,7 @@ #include "lib/util.h" #include "lib/content_factory.h" #include "lib/job_manager.h" -#include "lib/ui_signaller.h" +#include "lib/signal_manager.h" #include "lib/job.h" #include "lib/dcp_content_type.h" #include "lib/ratio.h" @@ -59,11 +59,11 @@ help (string n) << " -o, --output output directory\n"; } -class SimpleUISignaller : public UISignaller +class SimpleSignalManager : public SignalManager { public: /* Do nothing in this method so that UI events happen in our thread - when we call UISignaller::ui_idle(). + when we call SignalManager::ui_idle(). */ void wake_ui () {} }; @@ -161,7 +161,7 @@ main (int argc, char* argv[]) exit (EXIT_FAILURE); } - ui_signaller = new SimpleUISignaller (); + signal_manager = new SimpleSignalManager (); try { shared_ptr film (new Film (output, false)); @@ -184,7 +184,7 @@ main (int argc, char* argv[]) JobManager* jm = JobManager::instance (); while (jm->work_to_do ()) {} - while (ui_signaller->ui_idle() > 0) {} + while (signal_manager->ui_idle() > 0) {} ContentList content = film->content (); for (ContentList::iterator i = content.begin(); i != content.end(); ++i) { diff --git a/src/wx/dolby_certificate_dialog.cc b/src/wx/dolby_certificate_dialog.cc index 15551a424..ad43f6479 100644 --- a/src/wx/dolby_certificate_dialog.cc +++ b/src/wx/dolby_certificate_dialog.cc @@ -21,7 +21,7 @@ #include #include "lib/compose.hpp" #include "lib/internet.h" -#include "lib/ui_signaller.h" +#include "lib/signal_manager.h" #include "dolby_certificate_dialog.h" #include "wx_util.h" @@ -80,7 +80,7 @@ DolbyCertificateDialog::setup_countries () /* See DoremiCertificateDialog for discussion about this daft delay */ wxMilliSleep (200); #endif - ui_signaller->when_idle (boost::bind (&DolbyCertificateDialog::finish_setup_countries, this)); + signal_manager->when_idle (boost::bind (&DolbyCertificateDialog::finish_setup_countries, this)); } void @@ -103,7 +103,7 @@ DolbyCertificateDialog::country_selected () #ifdef DCPOMATIC_OSX wxMilliSleep (200); #endif - ui_signaller->when_idle (boost::bind (&DolbyCertificateDialog::finish_country_selected, this)); + signal_manager->when_idle (boost::bind (&DolbyCertificateDialog::finish_country_selected, this)); } void @@ -126,7 +126,7 @@ DolbyCertificateDialog::cinema_selected () #ifdef DCPOMATIC_OSX wxMilliSleep (200); #endif - ui_signaller->when_idle (boost::bind (&DolbyCertificateDialog::finish_cinema_selected, this)); + signal_manager->when_idle (boost::bind (&DolbyCertificateDialog::finish_cinema_selected, this)); } void @@ -161,7 +161,7 @@ DolbyCertificateDialog::download () wxMilliSleep (200); #endif - ui_signaller->when_idle (boost::bind (&DolbyCertificateDialog::finish_download, this)); + signal_manager->when_idle (boost::bind (&DolbyCertificateDialog::finish_download, this)); } void diff --git a/src/wx/doremi_certificate_dialog.cc b/src/wx/doremi_certificate_dialog.cc index 105555da9..578a7a72d 100644 --- a/src/wx/doremi_certificate_dialog.cc +++ b/src/wx/doremi_certificate_dialog.cc @@ -21,7 +21,7 @@ #include #include "lib/compose.hpp" #include "lib/util.h" -#include "lib/ui_signaller.h" +#include "lib/signal_manager.h" #include "lib/internet.h" #include "doremi_certificate_dialog.h" #include "wx_util.h" @@ -59,7 +59,7 @@ DoremiCertificateDialog::download () wxMilliSleep (200); #endif - ui_signaller->when_idle (boost::bind (&DoremiCertificateDialog::finish_download, this, serial)); + signal_manager->when_idle (boost::bind (&DoremiCertificateDialog::finish_download, this, serial)); } void diff --git a/src/wx/wscript b/src/wx/wscript index a05774cd2..370f59c62 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -80,7 +80,7 @@ sources = """ update_dialog.cc video_panel.cc wx_util.cc - wx_ui_signaller.cc + wx_signal_manager.cc """ def configure(conf): diff --git a/src/wx/wx_signal_manager.cc b/src/wx/wx_signal_manager.cc new file mode 100644 index 000000000..3d8b9992a --- /dev/null +++ b/src/wx/wx_signal_manager.cc @@ -0,0 +1,34 @@ +/* + Copyright (C) 2012-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 + 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, + 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. + +*/ + +#include +#include "wx_signal_manager.h" + +wxSignalManager::wxSignalManager (wxEvtHandler* h) + : _handler (h) +{ + +} + +void +wxSignalManager::wake_ui () +{ + wxCommandEvent event (-1, -1); + _handler->AddPendingEvent (event); +} diff --git a/src/wx/wx_signal_manager.h b/src/wx/wx_signal_manager.h new file mode 100644 index 000000000..ad18e6880 --- /dev/null +++ b/src/wx/wx_signal_manager.h @@ -0,0 +1,36 @@ +/* + Copyright (C) 2012-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 + 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, + 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. + +*/ + +#include "lib/signal_manager.h" + +class wxEvtHandler; + +/** @class wxSignalManager + * @brief SignalManager for the wxWidgets event loop + */ + +class wxSignalManager : public SignalManager +{ +public: + wxSignalManager (wxEvtHandler *); + void wake_ui (); + +private: + wxEvtHandler* _handler; +}; diff --git a/src/wx/wx_ui_signaller.cc b/src/wx/wx_ui_signaller.cc deleted file mode 100644 index 8fc6670d6..000000000 --- a/src/wx/wx_ui_signaller.cc +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright (C) 2012-2014 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 - 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, - 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. - -*/ - -#include -#include "wx_ui_signaller.h" - -wxUISignaller::wxUISignaller (wxEvtHandler* h) - : _handler (h) -{ - -} - -void -wxUISignaller::wake_ui () -{ - wxCommandEvent event (-1, -1); - _handler->AddPendingEvent (event); -} diff --git a/src/wx/wx_ui_signaller.h b/src/wx/wx_ui_signaller.h deleted file mode 100644 index 63f2049cd..000000000 --- a/src/wx/wx_ui_signaller.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - Copyright (C) 2012-2014 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 - 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, - 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. - -*/ - -#include "lib/ui_signaller.h" - -class wxEvtHandler; - -/** @class wxUISignaller - * @brief UISignaller for the wxWidgets event loop - */ - -class wxUISignaller : public UISignaller -{ -public: - wxUISignaller (wxEvtHandler *); - void wake_ui (); - -private: - wxEvtHandler* _handler; -}; diff --git a/test/test.cc b/test/test.cc index 9201e5b4c..32ad6ca70 100644 --- a/test/test.cc +++ b/test/test.cc @@ -29,7 +29,7 @@ #include #include "lib/config.h" #include "lib/util.h" -#include "lib/ui_signaller.h" +#include "lib/signal_manager.h" #include "lib/film.h" #include "lib/job_manager.h" #include "lib/job.h" @@ -51,7 +51,7 @@ using boost::scoped_array; boost::filesystem::path private_data = boost::filesystem::path ("..") / boost::filesystem::path ("dcpomatic-test-private"); -class TestUISignaller : public UISignaller +class TestSignalManager : public SignalManager { public: /* No wakes in tests: we call ui_idle ourselves */ @@ -77,7 +77,7 @@ struct TestConfig ServerFinder::instance()->disable (); - ui_signaller = new TestUISignaller (); + signal_manager = new TestSignalManager (); } ~TestConfig () @@ -285,7 +285,7 @@ wait_for_jobs () { JobManager* jm = JobManager::instance (); while (jm->work_to_do ()) { - ui_signaller->ui_idle (); + signal_manager->ui_idle (); } if (jm->errors ()) { int N = 0; @@ -305,7 +305,7 @@ wait_for_jobs () } } - ui_signaller->ui_idle (); + signal_manager->ui_idle (); /* Discard all jobs so we lose any we just reported an error in */ JobManager::drop (); -- cgit v1.2.3 From 1504f8460d6c438525489ee22eb9a4b437cb449e Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 13 May 2015 22:59:47 +0100 Subject: Port XML audio analysis code from master. --- src/lib/audio_analysis.cc | 96 ++++++++++++++++------------------------------- src/lib/audio_analysis.h | 25 +++--------- src/wx/audio_dialog.cc | 9 ++++- 3 files changed, 46 insertions(+), 84 deletions(-) (limited to 'src/lib') diff --git a/src/lib/audio_analysis.cc b/src/lib/audio_analysis.cc index ee34b0d80..73422a9be 100644 --- a/src/lib/audio_analysis.cc +++ b/src/lib/audio_analysis.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington + Copyright (C) 2012-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 @@ -18,15 +18,17 @@ */ #include "audio_analysis.h" -#include "dcpomatic_assert.h" #include "cross.h" +#include "util.h" +#include "raw_convert.h" +#include #include +#include #include -#include #include -#include #include #include +#include using std::ostream; using std::istream; @@ -35,6 +37,7 @@ using std::vector; using std::cout; using std::max; using std::list; +using boost::shared_ptr; AudioPoint::AudioPoint () { @@ -43,14 +46,10 @@ AudioPoint::AudioPoint () } } -AudioPoint::AudioPoint (FILE* f) +AudioPoint::AudioPoint (cxml::ConstNodePtr node) { - for (int i = 0; i < COUNT; ++i) { - int n = fscanf (f, "%f", &_data[i]); - if (n != 1) { - _data[i] = 0; - } - } + _data[PEAK] = node->number_child ("Peak"); + _data[RMS] = node->number_child ("RMS"); } AudioPoint::AudioPoint (AudioPoint const & other) @@ -75,14 +74,12 @@ AudioPoint::operator= (AudioPoint const & other) } void -AudioPoint::write (FILE* f) const +AudioPoint::as_xml (xmlpp::Element* parent) const { - for (int i = 0; i < COUNT; ++i) { - fprintf (f, "%f\n", _data[i]); - } + parent->add_child ("Peak")->add_child_text (raw_convert (_data[PEAK])); + parent->add_child ("RMS")->add_child_text (raw_convert (_data[RMS])); } - AudioAnalysis::AudioAnalysis (int channels) { _data.resize (channels); @@ -90,44 +87,21 @@ AudioAnalysis::AudioAnalysis (int channels) AudioAnalysis::AudioAnalysis (boost::filesystem::path filename) { - FILE* f = fopen_boost (filename, "r"); - if (!f) { - throw OpenFileError (filename); - } + cxml::Document f ("AudioAnalysis"); + f.read_file (filename); - int channels = 0; - fscanf (f, "%d", &channels); - _data.resize (channels); + BOOST_FOREACH (cxml::NodePtr i, f.node_children ("Channel")) { + vector channel; - for (int i = 0; i < channels; ++i) { - int points; - fscanf (f, "%d", &points); - if (feof (f)) { - fclose (f); - return; - } - - for (int j = 0; j < points; ++j) { - _data[i].push_back (AudioPoint (f)); - if (feof (f)) { - fclose (f); - return; - } + BOOST_FOREACH (cxml::NodePtr j, i->node_children ("Point")) { + channel.push_back (AudioPoint (j)); } - } - /* These may not exist in old analysis files, so be careful - about reading them. - */ - - float peak; - DCPTime::Type peak_time; - if (fscanf (f, "%f%" SCNd64, &peak, &peak_time) == 2) { - _peak = peak; - _peak_time = DCPTime (peak_time); + _data.push_back (channel); } - - fclose (f); + + _peak = f.number_child ("Peak"); + _peak_time = DCPTime (f.number_child ("PeakTime")); } void @@ -160,26 +134,20 @@ AudioAnalysis::points (int c) const void AudioAnalysis::write (boost::filesystem::path filename) { - boost::filesystem::path tmp = filename; - tmp.replace_extension (".tmp"); - - FILE* f = fopen_boost (tmp, "w"); - if (!f) { - throw OpenFileError (tmp); - } + shared_ptr doc (new xmlpp::Document); + xmlpp::Element* root = doc->create_root_node ("AudioAnalysis"); - fprintf (f, "%ld\n", _data.size ()); - for (vector >::iterator i = _data.begin(); i != _data.end(); ++i) { - fprintf (f, "%ld\n", i->size ()); - for (vector::iterator j = i->begin(); j != i->end(); ++j) { - j->write (f); + BOOST_FOREACH (vector& i, _data) { + xmlpp::Element* channel = root->add_child ("Channel"); + BOOST_FOREACH (AudioPoint& j, i) { + j.as_xml (channel->add_child ("Point")); } } if (_peak) { - fprintf (f, "%f%" PRId64, _peak.get (), _peak_time.get().get ()); + root->add_child("Peak")->add_child_text (raw_convert (_peak.get ())); + root->add_child("PeakTime")->add_child_text (raw_convert (_peak_time.get().get ())); } - fclose (f); - boost::filesystem::rename (tmp, filename); + doc->write_to_file_formatted (filename.string ()); } diff --git a/src/lib/audio_analysis.h b/src/lib/audio_analysis.h index 1872c57ad..9387ec896 100644 --- a/src/lib/audio_analysis.h +++ b/src/lib/audio_analysis.h @@ -17,21 +17,16 @@ */ -/** @file src/lib/audio_analysis.h - * @brief AudioAnalysis and AudioPoint classes. - */ - #ifndef DCPOMATIC_AUDIO_ANALYSIS_H #define DCPOMATIC_AUDIO_ANALYSIS_H -#include "types.h" +#include +#include #include #include -#include +#include +#include "types.h" -/** @class AudioPoint - * @brief A single point of an audio analysis for one portion of one channel. - */ class AudioPoint { public: @@ -42,11 +37,11 @@ public: }; AudioPoint (); - AudioPoint (FILE *); + AudioPoint (cxml::ConstNodePtr node); AudioPoint (AudioPoint const &); AudioPoint& operator= (AudioPoint const &); - void write (FILE *) const; + void as_xml (xmlpp::Element *) const; float& operator[] (int t) { return _data[t]; @@ -56,14 +51,6 @@ private: float _data[COUNT]; }; -/** @class AudioAnalysis - * @brief An analysis of the audio data in a piece of AudioContent. - * - * This is a set of AudioPoints for each channel. The AudioPoints - * each represent some measurement of the audio over a portion of the - * content. For example each AudioPoint may give the RMS level of - * a 1-minute portion of the audio. - */ class AudioAnalysis : public boost::noncopyable { public: diff --git a/src/wx/audio_dialog.cc b/src/wx/audio_dialog.cc index 286e7f49d..fcae9c30f 100644 --- a/src/wx/audio_dialog.cc +++ b/src/wx/audio_dialog.cc @@ -124,8 +124,15 @@ AudioDialog::try_to_load_analysis () _analysis_finished_connection = _content->analyse_audio (bind (&AudioDialog::analysis_finished, this)); return; } + + try { + _analysis.reset (new AudioAnalysis (_content->audio_analysis_path ())); + } catch (xmlpp::exception& e) { + /* Probably an old-style analysis file: recreate it */ + _analysis_finished_connection = _content->analyse_audio (bind (&AudioDialog::analysis_finished, this)); + return; + } - _analysis.reset (new AudioAnalysis (_content->audio_analysis_path ())); _plot->set_analysis (_analysis); setup_peak_time (); -- cgit v1.2.3 From 419c025709132e21ce948c4a6051a323f06ee61d Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 13 May 2015 23:38:49 +0100 Subject: Token effort toward non-bitmap subtitles from FFmpegDecoder. --- src/lib/ffmpeg_decoder.cc | 53 ++++++++++++++++++++++++++++++----------------- src/lib/ffmpeg_decoder.h | 2 ++ 2 files changed, 36 insertions(+), 19 deletions(-) (limited to 'src/lib') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 6122a547e..35e15a331 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -453,15 +453,43 @@ FFmpegDecoder::decode_subtitle_packet () AVSubtitleRect const * rect = sub.rects[0]; - if (rect->type != SUBTITLE_BITMAP) { - throw DecodeError (_("non-bitmap subtitles not yet supported")); + switch (rect->type) { + case SUBTITLE_NONE: + break; + case SUBTITLE_BITMAP: + decode_bitmap_subtitle (rect, period); + break; + case SUBTITLE_TEXT: + cout << "XXX: SUBTITLE_TEXT " << rect->text << "\n"; + break; + case SUBTITLE_ASS: + cout << "XXX: SUBTITLE_ASS " << rect->ass << "\n"; + break; } + + avsubtitle_free (&sub); +} + +list +FFmpegDecoder::image_subtitles_during (ContentTimePeriod p, bool starting) const +{ + return _ffmpeg_content->subtitles_during (p, starting); +} +list +FFmpegDecoder::text_subtitles_during (ContentTimePeriod, bool) const +{ + return list (); +} + +void +FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimePeriod period) +{ /* Note RGBA is expressed little-endian, so the first byte in the word is R, second G, third B, fourth A. */ shared_ptr image (new Image (PIX_FMT_RGBA, dcp::Size (rect->w, rect->h), true)); - + /* Start of the first line in the subtitle */ uint8_t* sub_p = rect->pict.data[0]; /* sub_p looks up into a BGRA palette which is here @@ -470,7 +498,7 @@ FFmpegDecoder::decode_subtitle_packet () uint32_t const * palette = (uint32_t *) rect->pict.data[1]; /* Start of the output data */ uint32_t* out_p = (uint32_t *) image->data()[0]; - + for (int y = 0; y < rect->h; ++y) { uint8_t* sub_line_p = sub_p; uint32_t* out_line_p = out_p; @@ -481,7 +509,7 @@ FFmpegDecoder::decode_subtitle_packet () sub_p += rect->pict.linesize[0]; out_p += image->stride()[0] / sizeof (uint32_t); } - + dcp::Size const vs = _ffmpeg_content->video_size (); dcpomatic::Rect const scaled_rect ( static_cast (rect->x) / vs.width, @@ -489,20 +517,7 @@ FFmpegDecoder::decode_subtitle_packet () static_cast (rect->w) / vs.width, static_cast (rect->h) / vs.height ); - - image_subtitle (ContentTimePeriod (period.from, period.to), image, scaled_rect); - avsubtitle_free (&sub); -} - -list -FFmpegDecoder::image_subtitles_during (ContentTimePeriod p, bool starting) const -{ - return _ffmpeg_content->subtitles_during (p, starting); + image_subtitle (period, image, scaled_rect); } -list -FFmpegDecoder::text_subtitles_during (ContentTimePeriod, bool) const -{ - return list (); -} diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 616fa88dd..6f027ce1c 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -64,6 +64,8 @@ private: void decode_audio_packet (); void decode_subtitle_packet (); + void decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimePeriod period); + void maybe_add_subtitle (); boost::shared_ptr deinterleave_audio (uint8_t** data, int size); -- cgit v1.2.3 From edecc975384bfef50418e3fb77e5c4629207ec08 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 13 May 2015 23:44:41 +0100 Subject: Fix test reference after subtitle fixes. --- run/tests | 1 + src/lib/cross.cc | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'src/lib') diff --git a/run/tests b/run/tests index 9b94ada80..e9c025893 100755 --- a/run/tests +++ b/run/tests @@ -3,6 +3,7 @@ # e.g. --run_tests=foo export LD_LIBRARY_PATH=build/src/lib:$LD_LIBRARY_PATH +export DCPOMATIC_LINUX_SHARE_PREFIX=`pwd` if [ "$1" == "--debug" ]; then shift; gdb --args build/test/unit-tests --catch_system_errors=no $* diff --git a/src/lib/cross.cc b/src/lib/cross.cc index 9894d885f..29591c69f 100644 --- a/src/lib/cross.cc +++ b/src/lib/cross.cc @@ -155,6 +155,12 @@ boost::filesystem::path shared_path () { #ifdef DCPOMATIC_LINUX +#ifdef DCPOMATIC_DEBUG + char const * p = getenv ("DCPOMATIC_LINUX_SHARE_PREFIX"); + if (p) { + return p; + } +#endif return boost::filesystem::canonical (LINUX_SHARE_PREFIX); #endif #ifdef DCPOMATIC_WINDOWS -- cgit v1.2.3 From 476ed8e65f9edd2303c94609f974b15f2a9166cd Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 14 May 2015 13:52:49 +0100 Subject: Obey DCPOMATIC_LINUX_SHARE_PREFIX in non-debug as well as debug mode. --- src/lib/cross.cc | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/lib') diff --git a/src/lib/cross.cc b/src/lib/cross.cc index 29591c69f..285fbe1ce 100644 --- a/src/lib/cross.cc +++ b/src/lib/cross.cc @@ -155,12 +155,10 @@ boost::filesystem::path shared_path () { #ifdef DCPOMATIC_LINUX -#ifdef DCPOMATIC_DEBUG char const * p = getenv ("DCPOMATIC_LINUX_SHARE_PREFIX"); if (p) { return p; } -#endif return boost::filesystem::canonical (LINUX_SHARE_PREFIX); #endif #ifdef DCPOMATIC_WINDOWS -- cgit v1.2.3