for (SourceFrame i = _film->dcp_trim_start(); i < N; i += dfr.skip) {
string const j2k_file = _encode_opt->frame_out_path (i, false);
- string const hash_file = j2k_file + ".md5";
+ string const hash_file = _encode_opt->hash_out_path (i, false);
if (!boost::filesystem::exists (j2k_file)) {
_film->log()->log (String::compose ("Frame %1 has a missing J2K file.", i));
boost::filesystem::rename (tmp_j2k, real_j2k);
/* Write a file containing the hash */
- string const hash = real_j2k + ".md5";
+ string const hash = opt->hash_out_path (frame, false);
ofstream h (hash.c_str());
h << md5_digest (_data, _size) << "\n";
h.close ();
, _opt (o)
, _job (j)
{
-
+ _film_connection = f->Changed.connect (bind (&Decoder::film_changed, this, _1));
}
bool
#include "stream.h"
#include "video_source.h"
#include "audio_source.h"
+#include "film.h"
class Job;
class DecodeOptions;
class DelayLine;
class TimedSubtitle;
class Subtitle;
-class Film;
class FilterGraph;
/** @class Decoder.
boost::shared_ptr<const DecodeOptions> _opt;
/** associated Job, or 0 */
Job* _job;
+
+private:
+ virtual void film_changed (Film::Property) {}
+
+ boost::signals2::scoped_connection _film_connection;
};
#endif
setup_audio ();
setup_subtitle ();
- _film_connection = f->Changed.connect (bind (&FFmpegDecoder::film_changed, this, _1));
-
if (!o->video_sync) {
_first_video = 0;
}
boost::shared_ptr<AudioBuffers> deinterleave_audio (uint8_t* data, int size);
void film_changed (Film::Property);
- boost::signals2::scoped_connection _film_connection;
std::string stream_name (AVStream* s) const;
boost::optional<SourceFrame>
Film::dcp_length () const
{
+ if (content_type() == STILL) {
+ return _still_duration * frames_per_second();
+ }
+
if (!length()) {
return boost::optional<SourceFrame> ();
}
float frames_per_second () const {
boost::mutex::scoped_lock lm (_state_mutex);
+ if (content_type() == STILL) {
+ return 24;
+ }
+
return _frames_per_second;
}
using namespace std;
using namespace boost;
+void
+Image::swap (Image& other)
+{
+ std::swap (_pixel_format, other._pixel_format);
+}
+
/** @param n Component index.
* @return Number of lines in the image for the given component.
*/
shared_ptr<Image> rgb (new SimpleImage (PIX_FMT_RGB24, content_size, aligned));
+ cout << "scale to " << out_size.width << "x" << out_size.height << "\n";
+
struct SwsContext* scale_context = sws_getContext (
size().width, size().height, pixel_format(),
content_size.width, content_size.height, PIX_FMT_RGB24,
return out;
}
+shared_ptr<Image>
+Image::crop (Crop crop, bool aligned) const
+{
+ Size cropped_size = size ();
+ cropped_size.width -= crop.left + crop.right;
+ cropped_size.height -= crop.top + crop.bottom;
+
+ shared_ptr<Image> out (new SimpleImage (pixel_format(), cropped_size, aligned));
+
+ for (int c = 0; c < components(); ++c) {
+ int const crop_left_in_bytes = bytes_per_pixel(c) * crop.left;
+ int const cropped_width_in_bytes = bytes_per_pixel(c) * cropped_size.width;
+
+ /* Start of the source line, cropped from the top but not the left */
+ uint8_t* in_p = data()[c] + crop.top * stride()[c];
+ uint8_t* out_p = out->data()[c];
+
+ for (int y = 0; y < cropped_size.height; ++y) {
+ memcpy (out_p, in_p + crop_left_in_bytes, cropped_width_in_bytes);
+ in_p += line_size()[c];
+ out_p += out->line_size()[c];
+ }
+ }
+
+ return out;
+}
+
void
Image::make_black ()
{
}
}
+
+float
+Image::bytes_per_pixel (int c) const
+{
+ if (c == 3) {
+ return 0;
+ }
+
+ switch (_pixel_format) {
+ case PIX_FMT_RGB24:
+ if (c == 0) {
+ return 3;
+ } else {
+ return 0;
+ }
+ case PIX_FMT_RGBA:
+ if (c == 0) {
+ return 4;
+ } else {
+ return 0;
+ }
+ case PIX_FMT_YUV420P:
+ case PIX_FMT_YUV422P:
+ if (c == 0) {
+ return 1;
+ } else {
+ return 0.5;
+ }
+ case PIX_FMT_YUV422P10LE:
+ if (c == 1) {
+ return 2;
+ } else {
+ return 1;
+ }
+ default:
+ assert (false);
+ }
+
+ return 0;
+}
+
+
/** Construct a SimpleImage of a given size and format, allocating memory
* as required.
*
: Image (p)
, _size (s)
, _aligned (aligned)
+{
+ allocate ();
+}
+
+void
+SimpleImage::allocate ()
{
_data = (uint8_t **) av_malloc (4 * sizeof (uint8_t *));
_data[0] = _data[1] = _data[2] = _data[3] = 0;
_stride = (int *) av_malloc (4 * sizeof (int));
_stride[0] = _stride[1] = _stride[2] = _stride[3] = 0;
- switch (p) {
- case PIX_FMT_RGB24:
- _line_size[0] = s.width * 3;
- break;
- case PIX_FMT_RGBA:
- _line_size[0] = s.width * 4;
- break;
- case PIX_FMT_YUV420P:
- case PIX_FMT_YUV422P:
- _line_size[0] = s.width;
- _line_size[1] = s.width / 2;
- _line_size[2] = s.width / 2;
- break;
- case PIX_FMT_YUV422P10LE:
- _line_size[0] = s.width * 2;
- _line_size[1] = s.width;
- _line_size[2] = s.width;
- break;
- default:
- assert (false);
- }
-
for (int i = 0; i < components(); ++i) {
+ _line_size[i] = _size.width * bytes_per_pixel(i);
_stride[i] = stride_round_up (i, _line_size, _aligned ? 32 : 1);
_data[i] = (uint8_t *) av_malloc (_stride[i] * lines (i));
}
}
+SimpleImage::SimpleImage (SimpleImage const & other)
+ : Image (other)
+{
+ _size = other._size;
+ _aligned = other._aligned;
+
+ allocate ();
+
+ for (int i = 0; i < components(); ++i) {
+ memcpy (_data[i], other._data[i], _line_size[i] * lines(i));
+ }
+}
+
+SimpleImage&
+SimpleImage::operator= (SimpleImage const & other)
+{
+ if (this == &other) {
+ return *this;
+ }
+
+ SimpleImage tmp (other);
+ swap (tmp);
+ return *this;
+}
+
+void
+SimpleImage::swap (SimpleImage & other)
+{
+ Image::swap (other);
+
+ assert (_size == other._size);
+ assert (_aligned == other._aligned);
+
+ std::swap (_size, other._size);
+
+ for (int i = 0; i < 4; ++i) {
+ std::swap (_data[i], other._data[i]);
+ std::swap (_line_size[i], other._line_size[i]);
+ std::swap (_stride[i], other._stride[i]);
+ }
+
+ std::swap (_aligned, other._aligned);
+}
+
/** Destroy a SimpleImage */
SimpleImage::~SimpleImage ()
{
{
av_free (_alpha);
}
+
boost::shared_ptr<Image> scale (Size, Scaler const *, bool aligned) const;
boost::shared_ptr<Image> post_process (std::string, bool aligned) const;
void alpha_blend (boost::shared_ptr<const Image> image, Position pos);
+ boost::shared_ptr<Image> crop (Crop c, bool aligned) const;
void make_black ();
return _pixel_format;
}
-private:
+protected:
+ virtual void swap (Image &);
+ float bytes_per_pixel (int) const;
+
+private:
AVPixelFormat _pixel_format; ///< FFmpeg's way of describing the pixel format of this Image
};
Size size () const;
private:
+ /* Not allowed */
+ FilterBufferImage (FilterBufferImage const &);
+ FilterBufferImage& operator= (FilterBufferImage const &);
+
AVFilterBufferRef* _buffer;
};
{
public:
SimpleImage (AVPixelFormat, Size, bool);
+ SimpleImage (SimpleImage const &);
+ SimpleImage& operator= (SimpleImage const &);
SimpleImage (boost::shared_ptr<const Image>, bool aligned);
~SimpleImage ();
int * line_size () const;
int * stride () const;
Size size () const;
+
+protected:
+ void allocate ();
+ void swap (SimpleImage &);
private:
-
Size _size; ///< size in pixels
uint8_t** _data; ///< array of pointers to components
int* _line_size; ///< array of sizes of the data in each line, in pixels (without any alignment padding bytes)
return true;
}
- using namespace MagickCore;
-
Magick::Image* magick_image = new Magick::Image (_film->content_path ());
Size size = native_size ();
- shared_ptr<SimpleImage> image (new SimpleImage (PIX_FMT_RGB24, size, false));
+ shared_ptr<Image> image (new SimpleImage (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) {
}
delete magick_image;
+
+ image = image->crop (_film->crop(), false);
emit_video (image, 0);
_iter = _files.begin ();
for (int i = 0; i < f; ++i) {
if (_iter == _files.end()) {
- return false;
+ return true;
}
++_iter;
}
- return true;
+ return false;
+}
+
+void
+ImageMagickDecoder::film_changed (Film::Property p)
+{
+ if (p == Film::CROP) {
+ OutputChanged ();
+ }
}
}
private:
+ void film_changed (Film::Property);
+
std::list<std::string> _files;
std::list<std::string>::iterator _iter;
};
}
string const real = _opt->frame_out_path (0, false);
- for (int i = 1; i < (_film->still_duration() * 24); ++i) {
+ string const real_hash = _opt->hash_out_path (0, false);
+ for (int i = 1; i < (_film->still_duration() * _film->frames_per_second()); ++i) {
+
if (!boost::filesystem::exists (_opt->frame_out_path (i, false))) {
- string const link = _opt->frame_out_path (i, false);
+ link (real, _opt->frame_out_path (i, false));
+ }
+
+ if (!boost::filesystem::exists (_opt->hash_out_path (i, false))) {
+ link (real_hash, _opt->hash_out_path (i, false));
+ }
+
+ frame_done ();
+ }
+}
+
+void
+J2KStillEncoder::link (string a, string b) const
+{
#ifdef DVDOMATIC_POSIX
- int const r = symlink (real.c_str(), link.c_str());
- if (r) {
- throw EncodeError ("could not create symlink");
- }
+ int const r = symlink (a.c_str(), b.c_str());
+ if (r) {
+ throw EncodeError ("could not create symlink");
+ }
#endif
+
#ifdef DVDOMATIC_WINDOWS
- boost::filesystem::copy_file (real, link);
+ boost::filesystem::copy_file (a, b);
#endif
- }
- frame_done ();
- }
}
private:
void do_process_video (boost::shared_ptr<Image>, boost::shared_ptr<Subtitle>);
void do_process_audio (boost::shared_ptr<AudioBuffers>) {}
+
+ void link (std::string, std::string) const;
};
* @param t true to return a temporary file path, otherwise a permanent one.
* @return The path to write this video frame to.
*/
- std::string frame_out_path (SourceFrame f, bool t, std::string e = "") const {
- if (e.empty ()) {
- e = _frame_out_extension;
- }
-
+ std::string frame_out_path (SourceFrame f, bool t) const {
std::stringstream s;
s << _frame_out_path << "/";
s.width (8);
- s << std::setfill('0') << f << e;
+ s << std::setfill('0') << f << _frame_out_extension;
if (t) {
s << ".tmp";
return s.str ();
}
+ std::string hash_out_path (SourceFrame f, bool t) const {
+ return frame_out_path (f, t) + ".md5";
+ }
+
/** @return Path to write multichannel audio data to */
std::string multichannel_audio_out_path () const {
return _multichannel_audio_out_path;
return 0;
}
- /* We assume that dcp_length() is valid */
+ if (!_film->dcp_length()) {
+ return 0;
+ }
+
+ /* We assume that dcp_length() is valid, if it is set */
SourceFrame const left = _film->dcp_trim_start() + _film->dcp_length().get() - _encoder->video_frame();
return left / fps;
}
void
VideoDecoder::set_progress () const
{
- if (_job && _film->dcp_length()) {
+ if (_job && _film->length()) {
_job->set_progress (float (_video_frame) / _film->length().get());
}
}
pad->Add (_video_sizer, 0, wxALL, 8);
_video_panel->SetSizer (pad);
- video_control (add_label_to_sizer (_video_sizer, _video_panel, "Format"));
+ add_label_to_sizer (_video_sizer, _video_panel, "Format");
_format = new wxComboBox (_video_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
- _video_sizer->Add (video_control (_format));
+ _video_sizer->Add (_format);
{
add_label_to_sizer (_video_sizer, _video_panel, "Crop");
FilmViewer::seek_and_update (SourceFrame f)
{
if (_decoders.video->seek (f)) {
+ cout << "could not s&u to " << f << "\n";
return;
}
name fred
use_dci_name 1
content
+trust_content_header 1
dcp_content_type Short
format 185
left_crop 1
BOOST_CHECK_EQUAL (b->running(), false);
}
+BOOST_AUTO_TEST_CASE (compact_image_test)
+{
+ SimpleImage* s = new SimpleImage (PIX_FMT_RGB24, Size (50, 50), false);
+ BOOST_CHECK_EQUAL (s->components(), 1);
+ BOOST_CHECK_EQUAL (s->stride()[0], 50 * 3);
+ BOOST_CHECK_EQUAL (s->line_size()[0], 50 * 3);
+ BOOST_CHECK (s->data()[0]);
+ BOOST_CHECK (!s->data()[1]);
+ BOOST_CHECK (!s->data()[2]);
+ BOOST_CHECK (!s->data()[3]);
+
+ /* copy constructor */
+ SimpleImage* t = new SimpleImage (*s);
+ BOOST_CHECK_EQUAL (t->components(), 1);
+ BOOST_CHECK_EQUAL (t->stride()[0], 50 * 3);
+ BOOST_CHECK_EQUAL (t->line_size()[0], 50 * 3);
+ BOOST_CHECK (t->data()[0]);
+ BOOST_CHECK (!t->data()[1]);
+ BOOST_CHECK (!t->data()[2]);
+ BOOST_CHECK (!t->data()[3]);
+ BOOST_CHECK (t->data() != t->data());
+ BOOST_CHECK (t->data()[0] != t->data()[0]);
+ BOOST_CHECK (t->line_size() != t->line_size());
+ BOOST_CHECK (t->line_size()[0] != t->line_size()[0]);
+ BOOST_CHECK (t->stride() != t->stride());
+ BOOST_CHECK (t->stride()[0] != t->stride()[0]);
+
+ /* assignment operator */
+ SimpleImage* u = new SimpleImage (PIX_FMT_YUV422P, Size (150, 150), true);
+ *s = *u;
+ BOOST_CHECK_EQUAL (s->components(), 1);
+ BOOST_CHECK_EQUAL (s->stride()[0], 50 * 3);
+ BOOST_CHECK_EQUAL (s->line_size()[0], 50 * 3);
+ BOOST_CHECK (s->data()[0]);
+ BOOST_CHECK (!s->data()[1]);
+ BOOST_CHECK (!s->data()[2]);
+ BOOST_CHECK (!s->data()[3]);
+ BOOST_CHECK (s->data() != t->data());
+ BOOST_CHECK (s->data()[0] != t->data()[0]);
+ BOOST_CHECK (s->line_size() != t->line_size());
+ BOOST_CHECK (s->line_size()[0] != t->line_size()[0]);
+ BOOST_CHECK (s->stride() != t->stride());
+ BOOST_CHECK (s->stride()[0] != t->stride()[0]);
+}
+
+BOOST_AUTO_TEST_CASE (aligned_image_test)
+{
+ SimpleImage* s = new SimpleImage (PIX_FMT_RGB24, Size (50, 50), true);
+ BOOST_CHECK_EQUAL (s->components(), 1);
+ /* 160 is 150 aligned to the nearest 32 bytes */
+ BOOST_CHECK_EQUAL (s->stride()[0], 160);
+ BOOST_CHECK_EQUAL (s->line_size()[0], 150);
+ BOOST_CHECK (s->data()[0]);
+ BOOST_CHECK (!s->data()[1]);
+ BOOST_CHECK (!s->data()[2]);
+ BOOST_CHECK (!s->data()[3]);
+
+ /* copy constructor */
+ SimpleImage* t = new SimpleImage (*s);
+ BOOST_CHECK_EQUAL (t->components(), 1);
+ BOOST_CHECK_EQUAL (t->stride()[0], 160);
+ BOOST_CHECK_EQUAL (t->line_size()[0], 150);
+ BOOST_CHECK (t->data()[0]);
+ BOOST_CHECK (!t->data()[1]);
+ BOOST_CHECK (!t->data()[2]);
+ BOOST_CHECK (!t->data()[3]);
+ BOOST_CHECK (t->data() != t->data());
+ BOOST_CHECK (t->data()[0] != t->data()[0]);
+ BOOST_CHECK (t->line_size() != t->line_size());
+ BOOST_CHECK (t->line_size()[0] != t->line_size()[0]);
+ BOOST_CHECK (t->stride() != t->stride());
+ BOOST_CHECK (t->stride()[0] != t->stride()[0]);
+
+ /* assignment operator */
+ SimpleImage* u = new SimpleImage (PIX_FMT_YUV422P, Size (150, 150), false);
+ *s = *u;
+ BOOST_CHECK_EQUAL (s->components(), 1);
+ BOOST_CHECK_EQUAL (s->stride()[0], 160);
+ BOOST_CHECK_EQUAL (s->line_size()[0], 150);
+ BOOST_CHECK (s->data()[0]);
+ BOOST_CHECK (!s->data()[1]);
+ BOOST_CHECK (!s->data()[2]);
+ BOOST_CHECK (!s->data()[3]);
+ BOOST_CHECK (s->data() != t->data());
+ BOOST_CHECK (s->data()[0] != t->data()[0]);
+ BOOST_CHECK (s->line_size() != t->line_size());
+ BOOST_CHECK (s->line_size()[0] != t->line_size()[0]);
+ BOOST_CHECK (s->stride() != t->stride());
+ BOOST_CHECK (s->stride()[0] != t->stride()[0]);
+}