+2014-03-07 Carl Hetherington <cth@carlh.net>
+
+ * Add subtitle view.
+
2014-06-12 Carl Hetherington <cth@carlh.net>
+ * Version 1.69.27 released.
+
+ 2014-06-12 Carl Hetherington <cth@carlh.net>
+
+ * Add Content menu with "scale to fit width" and "scale
+ to fit height" options.
+
* Version 1.69.26 released.
2014-06-12 Carl Hetherington <cth@carlh.net>
#include <iomanip>
#include <libcxml/cxml.h>
-#include <libdcp/colour_matrix.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/colour_matrix.h>
+#include <dcp/raw_convert.h>
#include "video_content.h"
#include "video_examiner.h"
#include "compose.hpp"
using std::setprecision;
using std::cout;
using std::vector;
+ using std::min;
+ using std::max;
using boost::shared_ptr;
using boost::optional;
using boost::dynamic_pointer_cast;
-using libdcp::raw_convert;
+using dcp::raw_convert;
vector<VideoContentScale> VideoContentScale::_scales;
setup_default_colour_conversion ();
}
-VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Frame len)
+VideoContent::VideoContent (shared_ptr<const Film> f, DCPTime s, ContentTime len)
: Content (f, s)
, _video_length (len)
, _video_frame_rate (0)
setup_default_colour_conversion ();
}
-VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
+VideoContent::VideoContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
: Content (f, node)
{
- _video_length = node->number_child<VideoContent::Frame> ("VideoLength");
_video_size.width = node->number_child<int> ("VideoWidth");
_video_size.height = node->number_child<int> ("VideoHeight");
_video_frame_rate = node->number_child<float> ("VideoFrameRate");
+
+ if (version < 32) {
+ /* DCP-o-matic 1.0 branch */
+ _video_length = ContentTime::from_frames (node->number_child<int64_t> ("VideoLength"), _video_frame_rate);
+ } else {
+ _video_length = ContentTime (node->number_child<int64_t> ("VideoLength"));
+ }
+
_video_frame_type = static_cast<VideoFrameType> (node->number_child<int> ("VideoFrameType"));
_crop.left = node->number_child<int> ("LeftCrop");
_crop.right = node->number_child<int> ("RightCrop");
VideoContent::as_xml (xmlpp::Node* node) const
{
boost::mutex::scoped_lock lm (_mutex);
- node->add_child("VideoLength")->add_child_text (raw_convert<string> (_video_length));
+ node->add_child("VideoLength")->add_child_text (raw_convert<string> (_video_length.get ()));
node->add_child("VideoWidth")->add_child_text (raw_convert<string> (_video_size.width));
node->add_child("VideoHeight")->add_child_text (raw_convert<string> (_video_size.height));
node->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
void
VideoContent::setup_default_colour_conversion ()
{
- _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, libdcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
+ _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, dcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
}
void
VideoContent::take_from_video_examiner (shared_ptr<VideoExaminer> d)
{
/* These examiner calls could call other content methods which take a lock on the mutex */
- libdcp::Size const vs = d->video_size ();
+ dcp::Size const vs = d->video_size ();
float const vfr = d->video_frame_rate ();
{
{
return String::compose (
"video: length %1, size %2x%3, rate %4",
- video_length_after_3d_combine(), video_size().width, video_size().height, video_frame_rate()
+ video_length_after_3d_combine().seconds(),
+ video_size().width,
+ video_size().height,
+ video_frame_rate()
);
}
-libdcp::Size
+dcp::Size
VideoContent::video_size_after_3d_split () const
{
- libdcp::Size const s = video_size ();
+ dcp::Size const s = video_size ();
switch (video_frame_type ()) {
case VIDEO_FRAME_TYPE_2D:
case VIDEO_FRAME_TYPE_3D_ALTERNATE:
case VIDEO_FRAME_TYPE_3D_RIGHT:
return s;
case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
- return libdcp::Size (s.width / 2, s.height);
+ return dcp::Size (s.width / 2, s.height);
case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
- return libdcp::Size (s.width, s.height / 2);
+ return dcp::Size (s.width, s.height / 2);
}
assert (false);
}
/** @return Video size after 3D split and crop */
-libdcp::Size
+dcp::Size
VideoContent::video_size_after_crop () const
{
return crop().apply (video_size_after_3d_split ());
}
/** @param t A time offset from the start of this piece of content.
- * @return Corresponding frame index.
+ * @return Corresponding time with respect to the content.
*/
-VideoContent::Frame
-VideoContent::time_to_content_video_frames (Time t) const
+ContentTime
+VideoContent::dcp_time_to_content_time (DCPTime t) const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- FrameRateChange frc (video_frame_rate(), film->video_frame_rate());
-
- /* Here we are converting from time (in the DCP) to a frame number in the content.
- Hence we need to use the DCP's frame rate and the double/skip correction, not
- the source's rate.
- */
- return t * film->video_frame_rate() / (frc.factor() * TIME_HZ);
+ return ContentTime (t, FrameRateChange (video_frame_rate(), film->video_frame_rate()));
}
+ void
+ VideoContent::scale_and_crop_to_fit_width ()
+ {
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
+
+ set_scale (VideoContentScale (film->container ()));
+
+ int const crop = max (0, int (video_size().height - double (film->frame_size().height) * video_size().width / film->frame_size().width));
+ set_top_crop (crop / 2);
+ set_bottom_crop (crop / 2);
+ }
+
+ void
+ VideoContent::scale_and_crop_to_fit_height ()
+ {
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
+
+ set_scale (VideoContentScale (film->container ()));
+
+ int const crop = max (0, int (video_size().width - double (film->frame_size().width) * video_size().height / film->frame_size().height));
+ set_left_crop (crop / 2);
+ set_right_crop (crop / 2);
+ }
+
VideoContentScale::VideoContentScale (Ratio const * r)
: _ratio (r)
, _scale (true)
}
-VideoContentScale::VideoContentScale (shared_ptr<cxml::Node> node)
+VideoContentScale::VideoContentScale (cxml::NodePtr node)
: _ratio (0)
, _scale (true)
{
/** @param display_container Size of the container that we are displaying this content in.
* @param film_container The size of the film's image.
*/
-libdcp::Size
-VideoContentScale::size (shared_ptr<const VideoContent> c, libdcp::Size display_container, libdcp::Size film_container) const
+dcp::Size
+VideoContentScale::size (shared_ptr<const VideoContent> c, dcp::Size display_container, dcp::Size film_container) const
{
if (_ratio) {
return fit_ratio_within (_ratio->ratio (), display_container);
}
- libdcp::Size const ac = c->video_size_after_crop ();
+ dcp::Size const ac = c->video_size_after_crop ();
/* Force scale if the film_container is smaller than the content's image */
if (_scale || film_container.width < ac.width || film_container.height < ac.height) {
/* Scale the image so that it will be in the right place in film_container, even if display_container is a
different size.
*/
- return libdcp::Size (
+ return dcp::Size (
c->video_size().width * float(display_container.width) / film_container.width,
c->video_size().height * float(display_container.height) / film_container.height
);
VideoContentScale ();
VideoContentScale (Ratio const *);
VideoContentScale (bool);
- VideoContentScale (boost::shared_ptr<cxml::Node>);
+ VideoContentScale (cxml::NodePtr);
- libdcp::Size size (boost::shared_ptr<const VideoContent>, libdcp::Size, libdcp::Size) const;
+ dcp::Size size (boost::shared_ptr<const VideoContent>, dcp::Size, dcp::Size) const;
std::string id () const;
std::string name () const;
void as_xml (xmlpp::Node *) const;
typedef int Frame;
VideoContent (boost::shared_ptr<const Film>);
- VideoContent (boost::shared_ptr<const Film>, Time, VideoContent::Frame);
+ VideoContent (boost::shared_ptr<const Film>, DCPTime, ContentTime);
VideoContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- VideoContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int);
+ VideoContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int);
VideoContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
void as_xml (xmlpp::Node *) const;
virtual std::string information () const;
virtual std::string identifier () const;
- VideoContent::Frame video_length () const {
+ ContentTime video_length () const {
boost::mutex::scoped_lock lm (_mutex);
return _video_length;
}
- VideoContent::Frame video_length_after_3d_combine () const {
+ ContentTime video_length_after_3d_combine () const {
boost::mutex::scoped_lock lm (_mutex);
if (_video_frame_type == VIDEO_FRAME_TYPE_3D_ALTERNATE) {
- return _video_length / 2;
+ return ContentTime (_video_length.get() / 2);
}
return _video_length;
}
- libdcp::Size video_size () const {
+ dcp::Size video_size () const {
boost::mutex::scoped_lock lm (_mutex);
return _video_size;
}
return _colour_conversion;
}
- libdcp::Size video_size_after_3d_split () const;
- libdcp::Size video_size_after_crop () const;
+ dcp::Size video_size_after_3d_split () const;
+ dcp::Size video_size_after_crop () const;
- VideoContent::Frame time_to_content_video_frames (Time) const;
+ ContentTime dcp_time_to_content_time (DCPTime) const;
+ void scale_and_crop_to_fit_width ();
+ void scale_and_crop_to_fit_height ();
+
protected:
void take_from_video_examiner (boost::shared_ptr<VideoExaminer>);
- VideoContent::Frame _video_length;
+ ContentTime _video_length;
float _video_frame_rate;
private:
void setup_default_colour_conversion ();
- libdcp::Size _video_size;
+ dcp::Size _video_size;
VideoFrameType _video_frame_type;
Crop _crop;
VideoContentScale _scale;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
- static FilmEditor* film_editor = 0;
- static FilmViewer* film_viewer = 0;
static shared_ptr<Film> film;
static std::string film_to_load;
static std::string film_to_create;
static std::string content_to_add;
static wxMenu* jobs_menu = 0;
- static void set_menu_sensitivity ();
-
// #define DCPOMATIC_WINDOWS_CONSOLE 1
class FilmChangedDialog
}
}
- #define ALWAYS 0x0
- #define NEEDS_FILM 0x1
- #define NOT_DURING_DCP_CREATION 0x2
- #define NEEDS_CPL 0x4
+ #define ALWAYS 0x0
+ #define NEEDS_FILM 0x1
+ #define NOT_DURING_DCP_CREATION 0x2
+ #define NEEDS_CPL 0x4
+ #define NEEDS_SELECTED_VIDEO_CONTENT 0x8
map<wxMenuItem*, int> menu_items;
menu_items.insert (make_pair (item, sens));
}
- static void
- set_menu_sensitivity ()
- {
- list<shared_ptr<Job> > jobs = JobManager::instance()->get ();
- list<shared_ptr<Job> >::iterator i = jobs.begin();
- while (i != jobs.end() && dynamic_pointer_cast<TranscodeJob> (*i) == 0) {
- ++i;
- }
- bool const dcp_creation = (i != jobs.end ()) && !(*i)->finished ();
- bool const have_cpl = film && !film->cpls().empty ();
-
- for (map<wxMenuItem*, int>::iterator j = menu_items.begin(); j != menu_items.end(); ++j) {
-
- bool enabled = true;
-
- if ((j->second & NEEDS_FILM) && film == 0) {
- enabled = false;
- }
-
- if ((j->second & NOT_DURING_DCP_CREATION) && dcp_creation) {
- enabled = false;
- }
-
- if ((j->second & NEEDS_CPL) && !have_cpl) {
- enabled = false;
- }
-
- j->first->Enable (enabled);
- }
- }
-
enum {
ID_file_new = 1,
ID_file_open,
ID_file_save,
ID_file_properties,
+ ID_content_scale_to_fit_width,
+ ID_content_scale_to_fit_height,
ID_jobs_make_dcp,
ID_jobs_make_kdms,
ID_jobs_send_dcp_to_tms,
#else
wxMenu* edit = new wxMenu;
add_item (edit, _("&Preferences..."), wxID_PREFERENCES, ALWAYS);
- #endif
+ #endif
+
+ wxMenu* content = new wxMenu;
+ add_item (content, _("Scale to fit &width"), ID_content_scale_to_fit_width, NEEDS_FILM | NEEDS_SELECTED_VIDEO_CONTENT);
+ add_item (content, _("Scale to fit &height"), ID_content_scale_to_fit_height, NEEDS_FILM | NEEDS_SELECTED_VIDEO_CONTENT);
jobs_menu = new wxMenu;
add_item (jobs_menu, _("&Make DCP"), ID_jobs_make_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION);
m->Append (file, _("&File"));
#ifndef __WXOSX__
m->Append (edit, _("&Edit"));
- #endif
+ #endif
+ m->Append (content, _("&Content"));
m->Append (jobs_menu, _("&Jobs"));
m->Append (tools, _("&Tools"));
m->Append (help, _("&Help"));
Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::file_properties, this), ID_file_properties);
Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::file_exit, this), wxID_EXIT);
Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::edit_preferences, this), wxID_PREFERENCES);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::content_scale_to_fit_width, this), ID_content_scale_to_fit_width);
+ Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::content_scale_to_fit_height, this), ID_content_scale_to_fit_height);
Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::jobs_make_dcp, this), ID_jobs_make_dcp);
Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::jobs_make_kdms, this), ID_jobs_make_kdms);
Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::jobs_send_dcp_to_tms, this), ID_jobs_send_dcp_to_tms);
*/
wxPanel* overall_panel = new wxPanel (this, wxID_ANY);
- film_editor = new FilmEditor (film, overall_panel);
- film_viewer = new FilmViewer (film, overall_panel);
+ _film_editor = new FilmEditor (film, overall_panel);
+ _film_viewer = new FilmViewer (film, overall_panel);
JobManagerView* job_manager_view = new JobManagerView (overall_panel, static_cast<JobManagerView::Buttons> (0));
wxBoxSizer* right_sizer = new wxBoxSizer (wxVERTICAL);
- right_sizer->Add (film_viewer, 2, wxEXPAND | wxALL, 6);
+ right_sizer->Add (_film_viewer, 2, wxEXPAND | wxALL, 6);
right_sizer->Add (job_manager_view, 1, wxEXPAND | wxALL, 6);
wxBoxSizer* main_sizer = new wxBoxSizer (wxHORIZONTAL);
- main_sizer->Add (film_editor, 1, wxEXPAND | wxALL, 6);
+ main_sizer->Add (_film_editor, 1, wxEXPAND | wxALL, 6);
main_sizer->Add (right_sizer, 2, wxEXPAND | wxALL, 6);
set_menu_sensitivity ();
- film_editor->FileChanged.connect (bind (&Frame::file_changed, this, _1));
+ _film_editor->FileChanged.connect (bind (&Frame::file_changed, this, _1));
if (film) {
file_changed (film->directory ());
} else {
file_changed ("");
}
- JobManager::instance()->ActiveJobsChanged.connect (boost::bind (set_menu_sensitivity));
+ JobManager::instance()->ActiveJobsChanged.connect (boost::bind (&Frame::set_menu_sensitivity, this));
set_film ();
overall_panel->SetSizer (main_sizer);
void set_film ()
{
- film_viewer->set_film (film);
- film_editor->set_film (film);
+ _film_viewer->set_film (film);
+ _film_editor->set_film (film);
set_menu_sensitivity ();
}
d->Destroy ();
}
+
+ void content_scale_to_fit_width ()
+ {
+ VideoContentList vc = _film_editor->selected_video_content ();
+ for (VideoContentList::iterator i = vc.begin(); i != vc.end(); ++i) {
+ (*i)->scale_and_crop_to_fit_width ();
+ }
+ }
+
+ void content_scale_to_fit_height ()
+ {
+ VideoContentList vc = _film_editor->selected_video_content ();
+ for (VideoContentList::iterator i = vc.begin(); i != vc.end(); ++i) {
+ (*i)->scale_and_crop_to_fit_height ();
+ }
+ }
void jobs_send_dcp_to_tms ()
{
ev.Skip ();
}
+ void set_menu_sensitivity ()
+ {
+ list<shared_ptr<Job> > jobs = JobManager::instance()->get ();
+ list<shared_ptr<Job> >::iterator i = jobs.begin();
+ while (i != jobs.end() && dynamic_pointer_cast<TranscodeJob> (*i) == 0) {
+ ++i;
+ }
+ bool const dcp_creation = (i != jobs.end ()) && !(*i)->finished ();
+ bool const have_cpl = film && !film->cpls().empty ();
+ bool const have_selected_video_content = !_film_editor->selected_video_content().empty();
+
+ for (map<wxMenuItem*, int>::iterator j = menu_items.begin(); j != menu_items.end(); ++j) {
+
+ bool enabled = true;
+
+ if ((j->second & NEEDS_FILM) && film == 0) {
+ enabled = false;
+ }
+
+ if ((j->second & NOT_DURING_DCP_CREATION) && dcp_creation) {
+ enabled = false;
+ }
+
+ if ((j->second & NEEDS_CPL) && !have_cpl) {
+ enabled = false;
+ }
+
+ if ((j->second & NEEDS_SELECTED_VIDEO_CONTENT) && !have_selected_video_content) {
+ enabled = false;
+ }
+
+ j->first->Enable (enabled);
+ }
+ }
+
+ FilmEditor* _film_editor;
+ FilmViewer* _film_viewer;
HintsDialog* _hints_dialog;
ServersListDialog* _servers_list_dialog;
wxPreferencesEditor* _config_dialog;
{ wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 }
};
+/** @class App
+ * @brief The magic App class for wxWidgets.
+ */
class App : public wxApp
{
bool OnInit ()