#include <libcxml/cxml.h>
#include "ffmpeg_content.h"
#include "still_image_content.h"
+#include "moving_image_content.h"
#include "sndfile_content.h"
using std::string;
content.reset (new FFmpegContent (film, node));
} else if (type == "StillImage") {
content.reset (new StillImageContent (film, node));
+ } else if (type == "MovingImage") {
+ content.reset (new MovingImageContent (film, node));
} else if (type == "Sndfile") {
content.reset (new SndfileContent (film, node));
}
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ 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.
+
+*/
+
+class MovingImageContent;
+
+class MovingImage
+{
+public:
+ MovingImage (boost::shared_ptr<const MovingImageContent> c)
+ : _moving_image_content (c)
+ {}
+
+ boost::shared_ptr<const MovingImageContent> content () const {
+ return _moving_image_content;
+ }
+
+protected:
+ boost::shared_ptr<const MovingImageContent> _moving_image_content;
+};
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ 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 <libcxml/cxml.h>
+#include "moving_image_content.h"
+#include "moving_image_examiner.h"
+#include "config.h"
+#include "compose.hpp"
+#include "film.h"
+
+#include "i18n.h"
+
+using std::string;
+using std::cout;
+using std::list;
+using std::stringstream;
+using std::vector;
+using boost::shared_ptr;
+
+MovingImageContent::MovingImageContent (shared_ptr<const Film> f, boost::filesystem::path p)
+ : Content (f, p)
+ , VideoContent (f, p)
+{
+
+}
+
+MovingImageContent::MovingImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
+ : Content (f, node)
+ , VideoContent (f, node)
+{
+ list<shared_ptr<cxml::Node> > c = node->node_children ("File");
+ for (list<shared_ptr<cxml::Node> >::const_iterator i = c.begin(); i != c.end(); ++i) {
+ _files.push_back ((*i)->content ());
+ }
+}
+
+string
+MovingImageContent::summary () const
+{
+ /* Get the string() here so that the name does not have quotes around it */
+ return String::compose (_("%1 [moving images]"), path().filename().string());
+}
+
+string
+MovingImageContent::technical_summary () const
+{
+ return Content::technical_summary() + " - "
+ + VideoContent::technical_summary() + " - "
+ + "moving";
+}
+
+void
+MovingImageContent::as_xml (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text ("MovingImage");
+ Content::as_xml (node);
+ VideoContent::as_xml (node);
+
+ for (vector<boost::filesystem::path>::const_iterator i = _files.begin(); i != _files.end(); ++i) {
+ node->add_child("File")->add_child_text (i->filename().string());
+ }
+}
+
+void
+MovingImageContent::examine (shared_ptr<Job> job)
+{
+ Content::examine (job);
+
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
+
+ shared_ptr<MovingImageExaminer> examiner (new MovingImageExaminer (film, shared_from_this(), job));
+
+ take_from_video_examiner (examiner);
+
+ _video_length = examiner->files().size ();
+ _files = examiner->files ();
+}
+
+Time
+MovingImageContent::length () const
+{
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
+
+ FrameRateConversion frc (video_frame_rate(), film->video_frame_rate ());
+ return video_length() * frc.factor() * TIME_HZ / video_frame_rate();
+}
+
+string
+MovingImageContent::identifier () const
+{
+ stringstream s;
+ s << VideoContent::identifier ();
+ s << "_" << video_length();
+ return s.str ();
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ 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_MOVING_IMAGE_CONTENT_H
+#define DCPOMATIC_MOVING_IMAGE_CONTENT_H
+
+#include <boost/enable_shared_from_this.hpp>
+#include "video_content.h"
+
+namespace cxml {
+ class Node;
+}
+
+/** A directory of image files which are to be presented as a movie */
+class MovingImageContent : public VideoContent
+{
+public:
+ MovingImageContent (boost::shared_ptr<const Film>, boost::filesystem::path);
+ MovingImageContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
+
+ boost::shared_ptr<MovingImageContent> shared_from_this () {
+ return boost::dynamic_pointer_cast<MovingImageContent> (Content::shared_from_this ());
+ };
+
+ void examine (boost::shared_ptr<Job>);
+ std::string summary () const;
+ std::string technical_summary () const;
+ void as_xml (xmlpp::Node *) const;
+ Time length () const;
+
+ std::string identifier () const;
+
+ std::vector<boost::filesystem::path> const & files () const {
+ return _files;
+ }
+
+private:
+ std::vector<boost::filesystem::path> _files;
+};
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+
+ 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 <iostream>
+#include <boost/filesystem.hpp>
+#include <Magick++.h>
+#include "moving_image_content.h"
+#include "moving_image_decoder.h"
+#include "image.h"
+#include "film.h"
+#include "exceptions.h"
+
+#include "i18n.h"
+
+using std::cout;
+using boost::shared_ptr;
+using libdcp::Size;
+
+MovingImageDecoder::MovingImageDecoder (shared_ptr<const Film> f, shared_ptr<const MovingImageContent> c)
+ : Decoder (f)
+ , VideoDecoder (f, c)
+ , MovingImage (c)
+{
+
+}
+
+void
+MovingImageDecoder::pass ()
+{
+ if (_video_position >= _moving_image_content->video_length ()) {
+ return;
+ }
+
+ boost::filesystem::path path = _moving_image_content->path ();
+ path /= _moving_image_content->files()[_video_position];
+
+ Magick::Image* magick_image = new Magick::Image (path.string());
+ libdcp::Size size (magick_image->columns(), magick_image->rows());
+
+ shared_ptr<Image> image (new Image (PIX_FMT_RGB24, size, false));
+
+ using namespace MagickCore;
+
+ uint8_t* p = image->data()[0];
+ for (int y = 0; y < size.height; ++y) {
+ for (int x = 0; x < size.width; ++x) {
+ Magick::Color c = magick_image->pixelColor (x, y);
+ *p++ = c.redQuantum() * 255 / QuantumRange;
+ *p++ = c.greenQuantum() * 255 / QuantumRange;
+ *p++ = c.blueQuantum() * 255 / QuantumRange;
+ }
+ }
+
+ delete magick_image;
+
+ video (image, false, _video_position);
+}
+
+void
+MovingImageDecoder::seek (VideoContent::Frame frame, bool)
+{
+ _video_position = frame;
+}
+
+bool
+MovingImageDecoder::done () const
+{
+ return _video_position >= _moving_image_content->video_length ();
+}
--- /dev/null
+/*
+ Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+
+ 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 "video_decoder.h"
+#include "moving_image.h"
+
+namespace Magick {
+ class Image;
+}
+
+class MovingImageContent;
+
+class MovingImageDecoder : public VideoDecoder, public MovingImage
+{
+public:
+ MovingImageDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const MovingImageContent>);
+
+ /* Decoder */
+
+ void pass ();
+ void seek (VideoContent::Frame, bool);
+ bool done () const;
+};
+
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ 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 <iostream>
+#include <boost/lexical_cast.hpp>
+#include <Magick++.h>
+#include "moving_image_content.h"
+#include "moving_image_examiner.h"
+#include "film.h"
+#include "job.h"
+#include "exceptions.h"
+
+#include "i18n.h"
+
+using std::cout;
+using std::list;
+using std::sort;
+using boost::shared_ptr;
+using boost::lexical_cast;
+
+MovingImageExaminer::MovingImageExaminer (shared_ptr<const Film> film, shared_ptr<const MovingImageContent> content, shared_ptr<Job> job)
+ : MovingImage (content)
+ , _film (film)
+ , _video_length (0)
+{
+ list<unsigned int> frames;
+ unsigned int files = 0;
+
+ for (boost::filesystem::directory_iterator i(content->path()); i != boost::filesystem::directory_iterator(); ++i) {
+ if (boost::filesystem::is_regular_file (i->path ())) {
+ ++files;
+ }
+ }
+
+ int j = 0;
+ for (boost::filesystem::directory_iterator i(content->path()); i != boost::filesystem::directory_iterator(); ++i) {
+ if (!boost::filesystem::is_regular_file (i->path ())) {
+ continue;
+ }
+
+ if (valid_image_file (i->path ())) {
+ int n = lexical_cast<int> (i->path().stem().string());
+ frames.push_back (n);
+ _files.push_back (i->path().filename ());
+
+ if (!_video_size) {
+ using namespace MagickCore;
+ Magick::Image* image = new Magick::Image (i->path().string());
+ _video_size = libdcp::Size (image->columns(), image->rows());
+ delete image;
+ }
+ }
+
+ job->set_progress (float (j) / files);
+ ++j;
+ }
+
+ frames.sort ();
+ sort (_files.begin(), _files.end ());
+
+ if (frames.size() < 2) {
+ throw StringError (String::compose (_("only %1 file(s) found in moving image directory"), frames.size ()));
+ }
+
+ if (frames.front() != 0 && frames.front() != 1) {
+ throw StringError (String::compose (_("first frame in moving image directory is number %1"), frames.front ()));
+ }
+
+ if (frames.back() != frames.size() && frames.back() != (frames.size() - 1)) {
+ throw StringError (String::compose (_("there are %1 images in the directory but the last one is number %2"), frames.size(), frames.back ()));
+ }
+
+ _video_length = frames.size ();
+}
+
+libdcp::Size
+MovingImageExaminer::video_size () const
+{
+ return _video_size.get ();
+}
+
+int
+MovingImageExaminer::video_length () const
+{
+ cout << "ex video length is " << _video_length << "\n";
+ return _video_length;
+}
+
+float
+MovingImageExaminer::video_frame_rate () const
+{
+ return 24;
+}
+
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ 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 "moving_image.h"
+#include "video_examiner.h"
+
+namespace Magick {
+ class Image;
+}
+
+class MovingImageContent;
+
+class MovingImageExaminer : public MovingImage, public VideoExaminer
+{
+public:
+ MovingImageExaminer (boost::shared_ptr<const Film>, boost::shared_ptr<const MovingImageContent>, boost::shared_ptr<Job>);
+
+ float video_frame_rate () const;
+ libdcp::Size video_size () const;
+ VideoContent::Frame video_length () const;
+
+ std::vector<boost::filesystem::path> const & files () const {
+ return _files;
+ }
+
+private:
+ boost::weak_ptr<const Film> _film;
+ boost::optional<libdcp::Size> _video_size;
+ VideoContent::Frame _video_length;
+ std::vector<boost::filesystem::path> _files;
+};
#include "ffmpeg_content.h"
#include "still_image_decoder.h"
#include "still_image_content.h"
+#include "moving_image_decoder.h"
+#include "moving_image_content.h"
#include "sndfile_decoder.h"
#include "sndfile_content.h"
#include "subtitle_content.h"
piece->decoder = id;
}
+ shared_ptr<const MovingImageContent> mc = dynamic_pointer_cast<const MovingImageContent> (*i);
+ if (mc) {
+ shared_ptr<MovingImageDecoder> md;
+
+ if (!md) {
+ md.reset (new MovingImageDecoder (_film, mc));
+ md->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4));
+ }
+
+ piece->decoder = md;
+ }
+
shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
if (sc) {
shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
*/
+#ifndef DCPOMATIC_STILL_IMAGE_H
+#define DCPOMATIC_STILL_IMAGE_H
+
class StillImageContent;
class StillImage
protected:
boost::shared_ptr<const StillImageContent> _still_image_content;
};
+
+#endif
+ "still";
}
-bool
-StillImageContent::valid_file (boost::filesystem::path f)
-{
- string ext = f.extension().string();
- transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
- return (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp");
-}
-
void
StillImageContent::as_xml (xmlpp::Node* node) const
{
class Node;
}
+/** A single image which is to be held on screen for some time (i.e. a slide) */
class StillImageContent : public VideoContent
{
public:
std::string identifier () const;
void set_video_length (VideoContent::Frame);
-
- static bool valid_file (boost::filesystem::path);
};
#endif
setlocale (LC_NUMERIC, _old);
free (_old);
}
+
+bool
+valid_image_file (boost::filesystem::path f)
+{
+ string ext = f.extension().string();
+ transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
+ return (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp");
+}
+
extern std::string md5_digest (void const *, int);
extern void ensure_ui_thread ();
extern std::string audio_channel_name (int);
+extern bool valid_image_file (boost::filesystem::path);
#ifdef DCPOMATIC_WINDOWS
extern boost::filesystem::path mo_path ();
#endif
film.cc
filter.cc
image.cc
- still_image_content.cc
- still_image_decoder.cc
- still_image_examiner.cc
job.cc
job_manager.cc
log.cc
+ moving_image_content.cc
+ moving_image_decoder.cc
+ moving_image_examiner.cc
player.cc
playlist.cc
ratio.cc
sndfile_content.cc
sndfile_decoder.cc
sound_processor.cc
+ still_image_content.cc
+ still_image_decoder.cc
+ still_image_examiner.cc
subtitle_content.cc
subtitle_decoder.cc
timer.cc
#include "lib/ratio.h"
#include "lib/config.h"
#include "lib/still_image_content.h"
+#include "lib/moving_image_content.h"
#include "lib/sndfile_content.h"
#include "lib/dcp_content_type.h"
#include "lib/sound_processor.h"
_content->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&FilmEditor::content_selection_changed, this));
_content->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&FilmEditor::content_selection_changed, this));
_content->Bind (wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, boost::bind (&FilmEditor::content_right_click, this, _1));
- _content_add->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_add_clicked, this));
+ _content_add_file->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_add_file_clicked, this));
+ _content_add_folder->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_add_folder_clicked, this));
_content_remove->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_remove_clicked, this));
_content_timeline->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_timeline_clicked, this));
_scaler->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::scaler_changed, this));
_content->SetColumnWidth (0, 512);
wxBoxSizer* b = new wxBoxSizer (wxVERTICAL);
- _content_add = new wxButton (_content_panel, wxID_ANY, _("Add..."));
- b->Add (_content_add, 1, wxEXPAND | wxLEFT | wxRIGHT);
+ _content_add_file = new wxButton (_content_panel, wxID_ANY, _("Add file..."));
+ b->Add (_content_add_file, 1, wxEXPAND | wxLEFT | wxRIGHT);
+ _content_add_folder = new wxButton (_content_panel, wxID_ANY, _("Add folder..."));
+ b->Add (_content_add_folder, 1, wxEXPAND | wxLEFT | wxRIGHT);
_content_remove = new wxButton (_content_panel, wxID_ANY, _("Remove"));
b->Add (_content_remove, 1, wxEXPAND | wxLEFT | wxRIGHT);
_content_timeline = new wxButton (_content_panel, wxID_ANY, _("Timeline..."));
_use_dci_name->Enable (s);
_edit_dci_button->Enable (s);
_content->Enable (s);
- _content_add->Enable (s);
+ _content_add_file->Enable (s);
+ _content_add_folder->Enable (s);
_content_remove->Enable (s);
_content_timeline->Enable (s);
_dcp_content_type->Enable (s);
}
void
-FilmEditor::content_add_clicked ()
+FilmEditor::content_add_file_clicked ()
{
wxFileDialog* d = new wxFileDialog (this, _("Choose a file or files"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE);
int const r = d->ShowModal ();
wxArrayString paths;
d->GetPaths (paths);
+ /* XXX: check for lots of files here and do something */
+
for (unsigned int i = 0; i < paths.GetCount(); ++i) {
boost::filesystem::path p (wx_to_std (paths[i]));
shared_ptr<Content> c;
- if (StillImageContent::valid_file (p)) {
+ if (valid_image_file (p)) {
c.reset (new StillImageContent (_film, p));
} else if (SndfileContent::valid_file (p)) {
c.reset (new SndfileContent (_film, p));
}
}
+void
+FilmEditor::content_add_folder_clicked ()
+{
+ wxDirDialog* d = new wxDirDialog (this, _("Choose a folder"), wxT (""), wxDD_DIR_MUST_EXIST);
+ int const r = d->ShowModal ();
+ d->Destroy ();
+
+ if (r != wxID_OK) {
+ return;
+ }
+
+ _film->examine_and_add_content (
+ shared_ptr<MovingImageContent> (
+ new MovingImageContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ())))
+ )
+ );
+}
+
void
FilmEditor::content_remove_clicked ()
{
void
FilmEditor::setup_content_sensitivity ()
{
- _content_add->Enable (_generally_sensitive);
+ _content_add_file->Enable (_generally_sensitive);
+ _content_add_folder->Enable (_generally_sensitive);
shared_ptr<Content> selection = selected_content ();
void use_dci_name_toggled ();
void edit_dci_button_clicked ();
void content_selection_changed ();
- void content_add_clicked ();
+ void content_add_file_clicked ();
+ void content_add_folder_clicked ();
void content_remove_clicked ();
void container_changed ();
void dcp_content_type_changed ();
wxCheckBox* _use_dci_name;
wxChoice* _container;
wxListCtrl* _content;
- wxButton* _content_add;
+ wxButton* _content_add_file;
+ wxButton* _content_add_folder;
wxButton* _content_remove;
wxButton* _content_earlier;
wxButton* _content_later;