summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2012-10-15 12:42:22 +0100
committerCarl Hetherington <cth@carlh.net>2012-10-15 12:42:22 +0100
commit13511ed2fcc23f4d5f9c507c775c3c5cfd82d155 (patch)
tree5ab1d1600725873a199725e50d67da9791c25d67 /src/lib
parentcb33319a820b17a05cfb2ef78ba1799f4d0c54b9 (diff)
parent43990add893eccf350f280e2dd3f947a94f3e9aa (diff)
Merge branch 'master' of /home/carl/git/dvdomatic
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/ab_transcode_job.cc4
-rw-r--r--src/lib/ab_transcode_job.h2
-rw-r--r--src/lib/ab_transcoder.cc9
-rw-r--r--src/lib/ab_transcoder.h3
-rw-r--r--src/lib/check_hashes_job.cc10
-rw-r--r--src/lib/check_hashes_job.h2
-rw-r--r--src/lib/copy_from_dvd_job.cc4
-rw-r--r--src/lib/copy_from_dvd_job.h2
-rw-r--r--src/lib/dcp_content_type.cc23
-rw-r--r--src/lib/dcp_content_type.h7
-rw-r--r--src/lib/dcp_video_frame.cc95
-rw-r--r--src/lib/dcp_video_frame.h6
-rw-r--r--src/lib/decoder.cc21
-rw-r--r--src/lib/decoder.h8
-rw-r--r--src/lib/encoder.h4
-rw-r--r--src/lib/examine_content_job.cc4
-rw-r--r--src/lib/examine_content_job.h2
-rw-r--r--src/lib/ffmpeg_decoder.cc46
-rw-r--r--src/lib/ffmpeg_decoder.h12
-rw-r--r--src/lib/film.cc88
-rw-r--r--src/lib/film.h25
-rw-r--r--src/lib/film_state.cc89
-rw-r--r--src/lib/film_state.h26
-rw-r--r--src/lib/filter.cc1
-rw-r--r--src/lib/format.cc32
-rw-r--r--src/lib/format.h12
-rw-r--r--src/lib/image.cc224
-rw-r--r--src/lib/image.h57
-rw-r--r--src/lib/imagemagick_decoder.cc20
-rw-r--r--src/lib/imagemagick_decoder.h23
-rw-r--r--src/lib/imagemagick_encoder.cc101
-rw-r--r--src/lib/imagemagick_encoder.h (renamed from src/lib/tiff_encoder.h)14
-rw-r--r--src/lib/j2k_still_encoder.cc4
-rw-r--r--src/lib/j2k_still_encoder.h2
-rw-r--r--src/lib/j2k_wav_encoder.cc5
-rw-r--r--src/lib/j2k_wav_encoder.h3
-rw-r--r--src/lib/job.cc10
-rw-r--r--src/lib/job.h9
-rw-r--r--src/lib/job_manager.cc23
-rw-r--r--src/lib/job_manager.h6
-rw-r--r--src/lib/make_dcp_job.cc4
-rw-r--r--src/lib/make_dcp_job.h2
-rw-r--r--src/lib/options.h10
-rw-r--r--src/lib/scp_dcp_job.cc4
-rw-r--r--src/lib/scp_dcp_job.h2
-rw-r--r--src/lib/server.cc21
-rw-r--r--src/lib/subtitle.cc113
-rw-r--r--src/lib/subtitle.h75
-rw-r--r--src/lib/thumbs_job.cc8
-rw-r--r--src/lib/thumbs_job.h2
-rw-r--r--src/lib/tiff_decoder.h3
-rw-r--r--src/lib/tiff_encoder.cc77
-rw-r--r--src/lib/transcode_job.cc4
-rw-r--r--src/lib/transcode_job.h2
-rw-r--r--src/lib/util.cc21
-rw-r--r--src/lib/util.h27
-rw-r--r--src/lib/wscript5
57 files changed, 1085 insertions, 333 deletions
diff --git a/src/lib/ab_transcode_job.cc b/src/lib/ab_transcode_job.cc
index d94f56d0a..fd8236bf0 100644
--- a/src/lib/ab_transcode_job.cc
+++ b/src/lib/ab_transcode_job.cc
@@ -35,8 +35,8 @@ using namespace boost;
* @param o Options.
* @Param l A log that we can write to.
*/
-ABTranscodeJob::ABTranscodeJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l)
- : Job (s, o, l)
+ABTranscodeJob::ABTranscodeJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l, shared_ptr<Job> req)
+ : Job (s, o, l, req)
{
_fs_b.reset (new FilmState (*_fs));
_fs_b->scaler = Config::instance()->reference_scaler ();
diff --git a/src/lib/ab_transcode_job.h b/src/lib/ab_transcode_job.h
index 478049068..4b80593f4 100644
--- a/src/lib/ab_transcode_job.h
+++ b/src/lib/ab_transcode_job.h
@@ -34,7 +34,7 @@
class ABTranscodeJob : public Job
{
public:
- ABTranscodeJob (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l);
+ ABTranscodeJob (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l, boost::shared_ptr<Job> req);
std::string name () const;
void run ();
diff --git a/src/lib/ab_transcoder.cc b/src/lib/ab_transcoder.cc
index 1c20ae477..54153ec76 100644
--- a/src/lib/ab_transcoder.cc
+++ b/src/lib/ab_transcoder.cc
@@ -70,7 +70,7 @@ ABTranscoder::~ABTranscoder ()
}
void
-ABTranscoder::process_video (shared_ptr<Image> yuv, int frame, int index)
+ABTranscoder::process_video (shared_ptr<Image> yuv, int frame, shared_ptr<Subtitle> sub, int index)
{
if (index == 0) {
/* Keep this image around until we get the other half */
@@ -80,19 +80,20 @@ ABTranscoder::process_video (shared_ptr<Image> yuv, int frame, int index)
for (int i = 0; i < yuv->components(); ++i) {
int const line_size = yuv->line_size()[i];
int const half_line_size = line_size / 2;
+ int const stride = yuv->stride()[i];
uint8_t* p = _image->data()[i];
uint8_t* q = yuv->data()[i];
for (int j = 0; j < yuv->lines (i); ++j) {
memcpy (p + half_line_size, q + half_line_size, half_line_size);
- p += line_size;
- q += line_size;
+ p += stride;
+ q += stride;
}
}
/* And pass it to the encoder */
- _encoder->process_video (_image, frame);
+ _encoder->process_video (_image, frame, sub);
_image.reset ();
}
diff --git a/src/lib/ab_transcoder.h b/src/lib/ab_transcoder.h
index 0310bb923..491205ef7 100644
--- a/src/lib/ab_transcoder.h
+++ b/src/lib/ab_transcoder.h
@@ -32,6 +32,7 @@ class FilmState;
class Options;
class Image;
class Log;
+class Subtitle;
/** @class ABTranscoder
* @brief A transcoder which uses one FilmState for the left half of the screen, and a different one
@@ -54,7 +55,7 @@ public:
void go ();
private:
- void process_video (boost::shared_ptr<Image>, int, int);
+ void process_video (boost::shared_ptr<Image>, int, boost::shared_ptr<Subtitle>, int);
boost::shared_ptr<const FilmState> _fs_a;
boost::shared_ptr<const FilmState> _fs_b;
diff --git a/src/lib/check_hashes_job.cc b/src/lib/check_hashes_job.cc
index f60a2d40d..f07a5ab2a 100644
--- a/src/lib/check_hashes_job.cc
+++ b/src/lib/check_hashes_job.cc
@@ -31,8 +31,8 @@
using namespace std;
using namespace boost;
-CheckHashesJob::CheckHashesJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l)
- : Job (s, o, l)
+CheckHashesJob::CheckHashesJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l, shared_ptr<Job> req)
+ : Job (s, o, l, req)
, _bad (0)
{
@@ -73,13 +73,13 @@ CheckHashesJob::run ()
shared_ptr<Job> tc;
if (_fs->dcp_ab) {
- tc.reset (new ABTranscodeJob (_fs, _opt, _log));
+ tc.reset (new ABTranscodeJob (_fs, _opt, _log, shared_from_this()));
} else {
- tc.reset (new TranscodeJob (_fs, _opt, _log));
+ tc.reset (new TranscodeJob (_fs, _opt, _log, shared_from_this()));
}
JobManager::instance()->add_after (shared_from_this(), tc);
- JobManager::instance()->add_after (tc, shared_ptr<Job> (new CheckHashesJob (_fs, _opt, _log)));
+ JobManager::instance()->add_after (tc, shared_ptr<Job> (new CheckHashesJob (_fs, _opt, _log, tc)));
}
set_progress (1);
diff --git a/src/lib/check_hashes_job.h b/src/lib/check_hashes_job.h
index b59cf031b..6a68e936c 100644
--- a/src/lib/check_hashes_job.h
+++ b/src/lib/check_hashes_job.h
@@ -22,7 +22,7 @@
class CheckHashesJob : public Job
{
public:
- CheckHashesJob (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l);
+ CheckHashesJob (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l, boost::shared_ptr<Job> req);
std::string name () const;
void run ();
diff --git a/src/lib/copy_from_dvd_job.cc b/src/lib/copy_from_dvd_job.cc
index d1000f54c..f7281fc10 100644
--- a/src/lib/copy_from_dvd_job.cc
+++ b/src/lib/copy_from_dvd_job.cc
@@ -35,8 +35,8 @@ using namespace boost;
/** @param fs FilmState for the film to write DVD data into.
* @param l Log that we can write to.
*/
-CopyFromDVDJob::CopyFromDVDJob (shared_ptr<const FilmState> fs, Log* l)
- : Job (fs, shared_ptr<Options> (), l)
+CopyFromDVDJob::CopyFromDVDJob (shared_ptr<const FilmState> fs, Log* l, shared_ptr<Job> req)
+ : Job (fs, shared_ptr<Options> (), l, req)
{
}
diff --git a/src/lib/copy_from_dvd_job.h b/src/lib/copy_from_dvd_job.h
index 6b56f6f0a..ce3837100 100644
--- a/src/lib/copy_from_dvd_job.h
+++ b/src/lib/copy_from_dvd_job.h
@@ -29,7 +29,7 @@
class CopyFromDVDJob : public Job
{
public:
- CopyFromDVDJob (boost::shared_ptr<const FilmState>, Log *);
+ CopyFromDVDJob (boost::shared_ptr<const FilmState>, Log *, boost::shared_ptr<Job> req);
std::string name () const;
void run ();
diff --git a/src/lib/dcp_content_type.cc b/src/lib/dcp_content_type.cc
index 1f50c8dc4..aae805308 100644
--- a/src/lib/dcp_content_type.cc
+++ b/src/lib/dcp_content_type.cc
@@ -28,9 +28,10 @@ using namespace std;
vector<DCPContentType const *> DCPContentType::_dcp_content_types;
-DCPContentType::DCPContentType (string p, libdcp::ContentKind k)
+DCPContentType::DCPContentType (string p, libdcp::ContentKind k, string d)
: _pretty_name (p)
, _libdcp_kind (k)
+ , _dci_name (d)
{
}
@@ -38,16 +39,16 @@ DCPContentType::DCPContentType (string p, libdcp::ContentKind k)
void
DCPContentType::setup_dcp_content_types ()
{
- _dcp_content_types.push_back (new DCPContentType ("Feature", libdcp::FEATURE));
- _dcp_content_types.push_back (new DCPContentType ("Short", libdcp::SHORT));
- _dcp_content_types.push_back (new DCPContentType ("Trailer", libdcp::TRAILER));
- _dcp_content_types.push_back (new DCPContentType ("Test", libdcp::TEST));
- _dcp_content_types.push_back (new DCPContentType ("Transitional", libdcp::TRANSITIONAL));
- _dcp_content_types.push_back (new DCPContentType ("Rating", libdcp::RATING));
- _dcp_content_types.push_back (new DCPContentType ("Teaser", libdcp::TEASER));
- _dcp_content_types.push_back (new DCPContentType ("Policy", libdcp::POLICY));
- _dcp_content_types.push_back (new DCPContentType ("Public Service Announcement", libdcp::PUBLIC_SERVICE_ANNOUNCEMENT));
- _dcp_content_types.push_back (new DCPContentType ("Advertisement", libdcp::ADVERTISEMENT));
+ _dcp_content_types.push_back (new DCPContentType ("Feature", libdcp::FEATURE, "FTR"));
+ _dcp_content_types.push_back (new DCPContentType ("Short", libdcp::SHORT, "SHR"));
+ _dcp_content_types.push_back (new DCPContentType ("Trailer", libdcp::TRAILER, "TLR"));
+ _dcp_content_types.push_back (new DCPContentType ("Test", libdcp::TEST, "TST"));
+ _dcp_content_types.push_back (new DCPContentType ("Transitional", libdcp::TRANSITIONAL, "XSN"));
+ _dcp_content_types.push_back (new DCPContentType ("Rating", libdcp::RATING, "RTG"));
+ _dcp_content_types.push_back (new DCPContentType ("Teaser", libdcp::TEASER, "TSR"));
+ _dcp_content_types.push_back (new DCPContentType ("Policy", libdcp::POLICY, "POL"));
+ _dcp_content_types.push_back (new DCPContentType ("Public Service Announcement", libdcp::PUBLIC_SERVICE_ANNOUNCEMENT, "PSA"));
+ _dcp_content_types.push_back (new DCPContentType ("Advertisement", libdcp::ADVERTISEMENT, "ADV"));
}
DCPContentType const *
diff --git a/src/lib/dcp_content_type.h b/src/lib/dcp_content_type.h
index cb858cf5a..b703970a3 100644
--- a/src/lib/dcp_content_type.h
+++ b/src/lib/dcp_content_type.h
@@ -31,7 +31,7 @@
class DCPContentType
{
public:
- DCPContentType (std::string, libdcp::ContentKind);
+ DCPContentType (std::string, libdcp::ContentKind, std::string);
/** @return user-visible `pretty' name */
std::string pretty_name () const {
@@ -42,6 +42,10 @@ public:
return _libdcp_kind;
}
+ std::string dci_name () const {
+ return _dci_name;
+ }
+
static DCPContentType const * from_pretty_name (std::string);
static DCPContentType const * from_index (int);
static int as_index (DCPContentType const *);
@@ -51,6 +55,7 @@ public:
private:
std::string _pretty_name;
libdcp::ContentKind _libdcp_kind;
+ std::string _dci_name;
/** All available DCP content types */
static std::vector<DCPContentType const *> _dcp_content_types;
diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc
index 90826a99f..04735269c 100644
--- a/src/lib/dcp_video_frame.cc
+++ b/src/lib/dcp_video_frame.cc
@@ -55,6 +55,7 @@
#include "scaler.h"
#include "image.h"
#include "log.h"
+#include "subtitle.h"
using namespace std;
using namespace boost;
@@ -72,10 +73,16 @@ using namespace boost;
* @param l Log to write to.
*/
DCPVideoFrame::DCPVideoFrame (
- shared_ptr<Image> yuv, Size out, int p, Scaler const * s, int f, float fps, string pp, int clut, int bw, Log* l)
+ shared_ptr<Image> yuv, shared_ptr<Subtitle> sub,
+ Size out, int p, int subtitle_offset, float subtitle_scale,
+ Scaler const * s, int f, float fps, string pp, int clut, int bw, Log* l
+ )
: _input (yuv)
+ , _subtitle (sub)
, _out_size (out)
, _padding (p)
+ , _subtitle_offset (subtitle_offset)
+ , _subtitle_scale (subtitle_scale)
, _scaler (s)
, _frame (f)
/* we round here; not sure if this is right */
@@ -147,18 +154,28 @@ DCPVideoFrame::~DCPVideoFrame ()
shared_ptr<EncodedData>
DCPVideoFrame::encode_locally ()
{
- shared_ptr<Image> prepared = _input;
-
if (!_post_process.empty ()) {
- prepared = prepared->post_process (_post_process);
+ _input = _input->post_process (_post_process);
}
- prepared = prepared->scale_and_convert_to_rgb (_out_size, _padding, _scaler);
+ shared_ptr<Image> prepared = _input->scale_and_convert_to_rgb (_out_size, _padding, _scaler);
+
+ if (_subtitle) {
+ list<shared_ptr<SubtitleImage> > subs = _subtitle->images ();
+ for (list<shared_ptr<SubtitleImage> >::iterator i = subs.begin(); i != subs.end(); ++i) {
+ Rectangle tx = transformed_subtitle_area (
+ float (_out_size.width) / _input->size().width,
+ float (_out_size.height) / _input->size().height,
+ (*i)->area(), _subtitle_offset, _subtitle_scale
+ );
+
+ shared_ptr<Image> im = (*i)->image()->scale (Size (tx.w, tx.h), _scaler);
+ prepared->alpha_blend (im, Position (tx.x, tx.y));
+ }
+ }
create_openjpeg_container ();
- int const size = _out_size.width * _out_size.height;
-
struct {
double r, g, b;
} s;
@@ -169,27 +186,41 @@ DCPVideoFrame::encode_locally ()
/* Copy our RGB into the openjpeg container, converting to XYZ in the process */
- uint8_t* p = prepared->data()[0];
- for (int i = 0; i < size; ++i) {
- /* In gamma LUT (converting 8-bit input to 12-bit) */
- s.r = lut_in[_colour_lut_index][*p++ << 4];
- s.g = lut_in[_colour_lut_index][*p++ << 4];
- s.b = lut_in[_colour_lut_index][*p++ << 4];
-
- /* RGB to XYZ Matrix */
- d.x = ((s.r * color_matrix[_colour_lut_index][0][0]) + (s.g * color_matrix[_colour_lut_index][0][1]) + (s.b * color_matrix[_colour_lut_index][0][2]));
- d.y = ((s.r * color_matrix[_colour_lut_index][1][0]) + (s.g * color_matrix[_colour_lut_index][1][1]) + (s.b * color_matrix[_colour_lut_index][1][2]));
- d.z = ((s.r * color_matrix[_colour_lut_index][2][0]) + (s.g * color_matrix[_colour_lut_index][2][1]) + (s.b * color_matrix[_colour_lut_index][2][2]));
-
- /* DCI companding */
- d.x = d.x * DCI_COEFFICENT * (DCI_LUT_SIZE - 1);
- d.y = d.y * DCI_COEFFICENT * (DCI_LUT_SIZE - 1);
- d.z = d.z * DCI_COEFFICENT * (DCI_LUT_SIZE - 1);
-
- /* Out gamma LUT */
- _image->comps[0].data[i] = lut_out[LO_DCI][(int) d.x];
- _image->comps[1].data[i] = lut_out[LO_DCI][(int) d.y];
- _image->comps[2].data[i] = lut_out[LO_DCI][(int) d.z];
+ int jn = 0;
+ for (int y = 0; y < _out_size.height; ++y) {
+ uint8_t* p = prepared->data()[0] + y * prepared->stride()[0];
+ for (int x = 0; x < _out_size.width; ++x) {
+
+ /* In gamma LUT (converting 8-bit input to 12-bit) */
+ s.r = lut_in[_colour_lut_index][*p++ << 4];
+ s.g = lut_in[_colour_lut_index][*p++ << 4];
+ s.b = lut_in[_colour_lut_index][*p++ << 4];
+
+ /* RGB to XYZ Matrix */
+ d.x = ((s.r * color_matrix[_colour_lut_index][0][0]) +
+ (s.g * color_matrix[_colour_lut_index][0][1]) +
+ (s.b * color_matrix[_colour_lut_index][0][2]));
+
+ d.y = ((s.r * color_matrix[_colour_lut_index][1][0]) +
+ (s.g * color_matrix[_colour_lut_index][1][1]) +
+ (s.b * color_matrix[_colour_lut_index][1][2]));
+
+ d.z = ((s.r * color_matrix[_colour_lut_index][2][0]) +
+ (s.g * color_matrix[_colour_lut_index][2][1]) +
+ (s.b * color_matrix[_colour_lut_index][2][2]));
+
+ /* DCI companding */
+ d.x = d.x * DCI_COEFFICENT * (DCI_LUT_SIZE - 1);
+ d.y = d.y * DCI_COEFFICENT * (DCI_LUT_SIZE - 1);
+ d.z = d.z * DCI_COEFFICENT * (DCI_LUT_SIZE - 1);
+
+ /* Out gamma LUT */
+ _image->comps[0].data[jn] = lut_out[LO_DCI][(int) d.x];
+ _image->comps[1].data[jn] = lut_out[LO_DCI][(int) d.y];
+ _image->comps[2].data[jn] = lut_out[LO_DCI][(int) d.z];
+
+ ++jn;
+ }
}
/* Set the max image and component sizes based on frame_rate */
@@ -289,6 +320,8 @@ DCPVideoFrame::encode_remotely (ServerDescription const * serv)
<< _input->pixel_format() << " "
<< _out_size.width << " " << _out_size.height << " "
<< _padding << " "
+ << _subtitle_offset << " "
+ << _subtitle_scale << " "
<< _scaler->id () << " "
<< _frame << " "
<< _frames_per_second << " "
@@ -296,14 +329,10 @@ DCPVideoFrame::encode_remotely (ServerDescription const * serv)
<< Config::instance()->colour_lut_index () << " "
<< Config::instance()->j2k_bandwidth () << " ";
- for (int i = 0; i < _input->components(); ++i) {
- s << _input->line_size()[i] << " ";
- }
-
socket.write ((uint8_t *) s.str().c_str(), s.str().length() + 1, 30);
for (int i = 0; i < _input->components(); ++i) {
- socket.write (_input->data()[i], _input->line_size()[i] * _input->lines(i), 30);
+ socket.write (_input->data()[i], _input->stride()[i] * _input->lines(i), 30);
}
char buffer[32];
diff --git a/src/lib/dcp_video_frame.h b/src/lib/dcp_video_frame.h
index 72f885e45..4e9a777bd 100644
--- a/src/lib/dcp_video_frame.h
+++ b/src/lib/dcp_video_frame.h
@@ -31,6 +31,7 @@ class ServerDescription;
class Scaler;
class Image;
class Log;
+class Subtitle;
/** @class EncodedData
* @brief Container for J2K-encoded data.
@@ -105,7 +106,7 @@ public:
class DCPVideoFrame
{
public:
- DCPVideoFrame (boost::shared_ptr<Image>, Size, int, Scaler const *, int, float, std::string, int, int, Log *);
+ DCPVideoFrame (boost::shared_ptr<Image>, boost::shared_ptr<Subtitle>, Size, int, int, float, Scaler const *, int, float, std::string, int, int, Log *);
virtual ~DCPVideoFrame ();
boost::shared_ptr<EncodedData> encode_locally ();
@@ -120,8 +121,11 @@ private:
void write_encoded (boost::shared_ptr<const Options>, uint8_t *, int);
boost::shared_ptr<Image> _input; ///< the input image
+ boost::shared_ptr<Subtitle> _subtitle; ///< any subtitle that should be on the image
Size _out_size; ///< the required size of the output, in pixels
int _padding;
+ int _subtitle_offset;
+ float _subtitle_scale;
Scaler const * _scaler; ///< scaler to use
int _frame; ///< frame index within the Film
int _frames_per_second; ///< Frames per second that we will use for the DCP (rounded)
diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc
index 324d1a296..1f771da2d 100644
--- a/src/lib/decoder.cc
+++ b/src/lib/decoder.cc
@@ -48,6 +48,7 @@ extern "C" {
#include "filter.h"
#include "delay_line.h"
#include "ffmpeg_compatibility.h"
+#include "subtitle.h"
using namespace std;
using namespace boost;
@@ -303,8 +304,13 @@ Decoder::process_video (AVFrame* frame)
image->make_black ();
}
+ shared_ptr<Subtitle> sub;
+ if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) {
+ sub = _subtitle;
+ }
+
TIMING ("Decoder emits %1", _video_frame);
- Video (image, _video_frame);
+ Video (image, _video_frame, sub);
++_video_frame;
}
}
@@ -405,3 +411,16 @@ Decoder::setup_video_filters ()
/* XXX: leaking `inputs' / `outputs' ? */
}
+void
+Decoder::process_subtitle (shared_ptr<Subtitle> s)
+{
+ _subtitle = s;
+
+ if (_opt->apply_crop) {
+ list<shared_ptr<SubtitleImage> > im = _subtitle->images ();
+ for (list<shared_ptr<SubtitleImage> >::iterator i = im.begin(); i != im.end(); ++i) {
+ Position const p = (*i)->position ();
+ (*i)->set_position (Position (p.x - _fs->crop.left, p.y - _fs->crop.top));
+ }
+ }
+}
diff --git a/src/lib/decoder.h b/src/lib/decoder.h
index 04ff512eb..805955b9d 100644
--- a/src/lib/decoder.h
+++ b/src/lib/decoder.h
@@ -37,6 +37,7 @@ class Options;
class Image;
class Log;
class DelayLine;
+class Subtitle;
/** @class Decoder.
* @brief Parent class for decoders of content.
@@ -66,6 +67,7 @@ public:
/** @return format of audio samples */
virtual AVSampleFormat audio_sample_format () const = 0;
virtual int64_t audio_channel_layout () const = 0;
+ virtual bool has_subtitles () const = 0;
void process_begin ();
bool pass ();
@@ -80,8 +82,9 @@ public:
/** Emitted when a video frame is ready.
* First parameter is the frame.
* Second parameter is its index within the content.
+ * Third parameter is either 0 or a subtitle that should be on this frame.
*/
- sigc::signal<void, boost::shared_ptr<Image>, int> Video;
+ sigc::signal<void, boost::shared_ptr<Image>, int, boost::shared_ptr<Subtitle> > Video;
/** Emitted when some audio data is ready.
* First parameter is the interleaved sample data, format is given in the FilmState.
@@ -100,6 +103,7 @@ protected:
void process_video (AVFrame *);
void process_audio (uint8_t *, int);
+ void process_subtitle (boost::shared_ptr<Subtitle>);
/** our FilmState */
boost::shared_ptr<const FilmState> _fs;
@@ -135,6 +139,8 @@ private:
(at the DCP sample rate).
*/
int64_t _audio_frames_processed;
+
+ boost::shared_ptr<Subtitle> _subtitle;
};
#endif
diff --git a/src/lib/encoder.h b/src/lib/encoder.h
index ea356cec4..02a2d7723 100644
--- a/src/lib/encoder.h
+++ b/src/lib/encoder.h
@@ -36,6 +36,7 @@ class FilmState;
class Options;
class Image;
class Log;
+class Subtitle;
/** @class Encoder
* @brief Parent class for classes which can encode video and audio frames.
@@ -58,8 +59,9 @@ public:
/** Called with a frame of video.
* @param i Video frame image.
* @param f Frame number within the film.
+ * @param s A subtitle that should be on this frame, or 0.
*/
- virtual void process_video (boost::shared_ptr<Image> i, int f) = 0;
+ virtual void process_video (boost::shared_ptr<Image> i, int f, boost::shared_ptr<Subtitle> s) = 0;
/** Called with some audio data.
* @param d Data.
diff --git a/src/lib/examine_content_job.cc b/src/lib/examine_content_job.cc
index d77ede2f9..36b4cbabc 100644
--- a/src/lib/examine_content_job.cc
+++ b/src/lib/examine_content_job.cc
@@ -30,8 +30,8 @@
using namespace std;
using namespace boost;
-ExamineContentJob::ExamineContentJob (shared_ptr<const FilmState> fs, Log* l)
- : Job (fs, shared_ptr<Options> (), l)
+ExamineContentJob::ExamineContentJob (shared_ptr<const FilmState> fs, Log* l, shared_ptr<Job> req)
+ : Job (fs, shared_ptr<Options> (), l, req)
{
}
diff --git a/src/lib/examine_content_job.h b/src/lib/examine_content_job.h
index d149341b4..3bbd673a8 100644
--- a/src/lib/examine_content_job.h
+++ b/src/lib/examine_content_job.h
@@ -31,7 +31,7 @@ class Decoder;
class ExamineContentJob : public Job
{
public:
- ExamineContentJob (boost::shared_ptr<const FilmState>, Log *);
+ ExamineContentJob (boost::shared_ptr<const FilmState>, Log *, boost::shared_ptr<Job> req);
~ExamineContentJob ();
std::string name () const;
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index 767299ea6..e01405191 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -27,6 +27,7 @@
#include <iomanip>
#include <iostream>
#include <stdint.h>
+#include <boost/lexical_cast.hpp>
extern "C" {
#include <tiffio.h>
#include <libavcodec/avcodec.h>
@@ -47,6 +48,7 @@ extern "C" {
#include "util.h"
#include "log.h"
#include "ffmpeg_decoder.h"
+#include "subtitle.h"
using namespace std;
using namespace boost;
@@ -56,15 +58,19 @@ FFmpegDecoder::FFmpegDecoder (boost::shared_ptr<const FilmState> s, boost::share
, _format_context (0)
, _video_stream (-1)
, _audio_stream (-1)
+ , _subtitle_stream (-1)
, _frame (0)
, _video_codec_context (0)
, _video_codec (0)
, _audio_codec_context (0)
, _audio_codec (0)
+ , _subtitle_codec_context (0)
+ , _subtitle_codec (0)
{
setup_general ();
setup_video ();
setup_audio ();
+ setup_subtitle ();
}
FFmpegDecoder::~FFmpegDecoder ()
@@ -76,6 +82,10 @@ FFmpegDecoder::~FFmpegDecoder ()
if (_video_codec_context) {
avcodec_close (_video_codec_context);
}
+
+ if (_subtitle_codec_context) {
+ avcodec_close (_subtitle_codec_context);
+ }
av_free (_frame);
avformat_close_input (&_format_context);
@@ -101,6 +111,8 @@ FFmpegDecoder::setup_general ()
_video_stream = i;
} else if (_format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
_audio_stream = i;
+ } else if (_format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+ _subtitle_stream = i;
}
}
@@ -156,6 +168,26 @@ FFmpegDecoder::setup_audio ()
}
}
+void
+FFmpegDecoder::setup_subtitle ()
+{
+ if (_subtitle_stream < 0) {
+ return;
+ }
+
+ _subtitle_codec_context = _format_context->streams[_subtitle_stream]->codec;
+ _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
+
+ if (_subtitle_codec == 0) {
+ throw DecodeError ("could not find subtitle decoder");
+ }
+
+ if (avcodec_open2 (_subtitle_codec_context, _subtitle_codec, 0) < 0) {
+ throw DecodeError ("could not open subtitle decoder");
+ }
+}
+
+
bool
FFmpegDecoder::do_pass ()
{
@@ -210,6 +242,15 @@ FFmpegDecoder::do_pass ()
assert (_audio_codec_context->channels == _fs->audio_channels);
process_audio (_frame->data[0], data_size);
}
+
+ } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _opt->decode_subtitles) {
+
+ int got_subtitle;
+ AVSubtitle sub;
+ if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) && got_subtitle) {
+ process_subtitle (shared_ptr<Subtitle> (new Subtitle (sub)));
+ avsubtitle_free (&sub);
+ }
}
av_free_packet (&_packet);
@@ -310,3 +351,8 @@ FFmpegDecoder::sample_aspect_ratio_denominator () const
return _video_codec_context->sample_aspect_ratio.den;
}
+bool
+FFmpegDecoder::has_subtitles () const
+{
+ return (_subtitle_stream != -1);
+}
diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h
index 4e5445f67..d34c22785 100644
--- a/src/lib/ffmpeg_decoder.h
+++ b/src/lib/ffmpeg_decoder.h
@@ -44,6 +44,7 @@ class FilmState;
class Options;
class Image;
class Log;
+class Subtitle;
/** @class FFmpegDecoder
* @brief A decoder using FFmpeg to decode content.
@@ -63,6 +64,7 @@ public:
int audio_sample_rate () const;
AVSampleFormat audio_sample_format () const;
int64_t audio_channel_layout () const;
+ bool has_subtitles () const;
private:
@@ -76,16 +78,22 @@ private:
void setup_general ();
void setup_video ();
void setup_audio ();
+ void setup_subtitle ();
+
+ void maybe_add_subtitle ();
AVFormatContext* _format_context;
int _video_stream;
int _audio_stream; ///< may be < 0 if there is no audio
+ int _subtitle_stream; ///< may be < 0 if there is no subtitle
AVFrame* _frame;
AVCodecContext* _video_codec_context;
AVCodec* _video_codec;
- AVCodecContext* _audio_codec_context; ///< may be 0 if there is no audio
- AVCodec* _audio_codec; ///< may be 0 if there is no audio
+ AVCodecContext* _audio_codec_context; ///< may be 0 if there is no audio
+ AVCodec* _audio_codec; ///< may be 0 if there is no audio
+ AVCodecContext* _subtitle_codec_context; ///< may be 0 if there is no subtitle
+ AVCodec* _subtitle_codec; ///< may be 0 if there is no subtitle
AVPacket _packet;
};
diff --git a/src/lib/film.cc b/src/lib/film.cc
index e2b3d4bc3..31af2f1c2 100644
--- a/src/lib/film.cc
+++ b/src/lib/film.cc
@@ -27,9 +27,10 @@
#include <unistd.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
+#include <boost/lexical_cast.hpp>
#include "film.h"
#include "format.h"
-#include "tiff_encoder.h"
+#include "imagemagick_encoder.h"
#include "job.h"
#include "filter.h"
#include "transcoder.h"
@@ -217,6 +218,7 @@ Film::set_content (string c)
_state.audio_channels = d->audio_channels ();
_state.audio_sample_rate = d->audio_sample_rate ();
_state.audio_sample_format = d->audio_sample_format ();
+ _state.has_subtitles = d->has_subtitles ();
_state.content_digest = md5_digest (s->content_path ());
_state.content = c;
@@ -395,7 +397,7 @@ Film::update_thumbs_post_gui ()
string const l = i->leaf ();
#endif
- size_t const d = l.find (".tiff");
+ size_t const d = l.find (".png");
if (d != string::npos) {
_state.thumbs.push_back (atoi (l.substr (0, d).c_str()));
}
@@ -533,17 +535,20 @@ Film::make_dcp (bool transcode, int freq)
o->decode_video_frequency = freq;
o->padding = format()->dcp_padding (this);
o->ratio = format()->ratio_as_float (this);
+ o->decode_subtitles = with_subtitles ();
+
+ shared_ptr<Job> r;
if (transcode) {
if (_state.dcp_ab) {
- JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (fs, o, log ())));
+ r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (fs, o, log(), shared_ptr<Job> ())));
} else {
- JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (fs, o, log ())));
+ r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (fs, o, log(), shared_ptr<Job> ())));
}
}
- JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (fs, o, log ())));
- JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (fs, o, log ())));
+ r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (fs, o, log(), r)));
+ JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (fs, o, log(), r)));
}
shared_ptr<FilmState>
@@ -582,7 +587,7 @@ Film::examine_content ()
return;
}
- _examine_content_job.reset (new ExamineContentJob (state_copy (), log ()));
+ _examine_content_job.reset (new ExamineContentJob (state_copy (), log(), shared_ptr<Job> ()));
_examine_content_job->Finished.connect (sigc::mem_fun (*this, &Film::examine_content_post_gui));
JobManager::instance()->add (_examine_content_job);
}
@@ -631,14 +636,14 @@ Film::set_still_duration (int d)
void
Film::send_dcp_to_tms ()
{
- shared_ptr<Job> j (new SCPDCPJob (state_copy (), log ()));
+ shared_ptr<Job> j (new SCPDCPJob (state_copy (), log(), shared_ptr<Job> ()));
JobManager::instance()->add (j);
}
void
Film::copy_from_dvd ()
{
- shared_ptr<Job> j (new CopyFromDVDJob (state_copy (), log ()));
+ shared_ptr<Job> j (new CopyFromDVDJob (state_copy (), log(), shared_ptr<Job> ()));
j->Finished.connect (sigc::mem_fun (*this, &Film::copy_from_dvd_post_gui));
JobManager::instance()->add (j);
}
@@ -658,3 +663,68 @@ Film::encoded_frames () const
return N;
}
+
+void
+Film::set_with_subtitles (bool w)
+{
+ _state.with_subtitles = w;
+ signal_changed (WITH_SUBTITLES);
+}
+
+void
+Film::set_subtitle_offset (int o)
+{
+ _state.subtitle_offset = o;
+ signal_changed (SUBTITLE_OFFSET);
+}
+
+void
+Film::set_subtitle_scale (float s)
+{
+ _state.subtitle_scale = s;
+ signal_changed (SUBTITLE_SCALE);
+}
+
+list<pair<Position, string> >
+Film::thumb_subtitles (int n) const
+{
+ string sub_file = _state.thumb_base(n) + ".sub";
+ if (!filesystem::exists (sub_file)) {
+ return list<pair<Position, string> > ();
+ }
+
+ ifstream f (sub_file.c_str ());
+ string line;
+
+ int sub_number;
+ int sub_x;
+ list<pair<Position, string> > subs;
+
+ while (getline (f, line)) {
+ if (line.empty ()) {
+ continue;
+ }
+
+ if (line[line.size() - 1] == '\r') {
+ line = line.substr (0, line.size() - 1);
+ }
+
+ size_t const s = line.find (' ');
+ if (s == string::npos) {
+ continue;
+ }
+
+ string const k = line.substr (0, s);
+ int const v = lexical_cast<int> (line.substr(s + 1));
+
+ if (k == "image") {
+ sub_number = v;
+ } else if (k == "x") {
+ sub_x = v;
+ } else if (k == "y") {
+ subs.push_back (make_pair (Position (sub_x, v), String::compose ("%1.sub.%2.png", _state.thumb_base(n), sub_number)));
+ }
+ }
+
+ return subs;
+}
diff --git a/src/lib/film.h b/src/lib/film.h
index cd3b1b8a8..c006eae36 100644
--- a/src/lib/film.h
+++ b/src/lib/film.h
@@ -119,6 +119,18 @@ public:
int still_duration () const {
return _state.still_duration;
}
+
+ bool with_subtitles () const {
+ return _state.with_subtitles;
+ }
+
+ int subtitle_offset () const {
+ return _state.subtitle_offset;
+ }
+
+ float subtitle_scale () const {
+ return _state.subtitle_scale;
+ }
void set_filters (std::vector<Filter const *> const &);
@@ -144,6 +156,9 @@ public:
void set_audio_gain (float);
void set_audio_delay (int);
void set_still_duration (int);
+ void set_with_subtitles (bool);
+ void set_subtitle_offset (int);
+ void set_subtitle_scale (float);
/** @return size, in pixels, of the source (ignoring cropping) */
Size size () const {
@@ -174,6 +189,10 @@ public:
AVSampleFormat audio_sample_format () const {
return _state.audio_sample_format;
}
+
+ bool has_subtitles () const {
+ return _state.has_subtitles;
+ }
std::string j2k_dir () const;
@@ -184,6 +203,7 @@ public:
int num_thumbs () const;
int thumb_frame (int) const;
std::string thumb_file (int) const;
+ std::list<std::pair<Position, std::string> > thumb_subtitles (int) const;
void copy_from_dvd_post_gui ();
void examine_content ();
@@ -218,7 +238,10 @@ public:
FRAMES_PER_SECOND,
AUDIO_CHANNELS,
AUDIO_SAMPLE_RATE,
- STILL_DURATION
+ STILL_DURATION,
+ WITH_SUBTITLES,
+ SUBTITLE_OFFSET,
+ SUBTITLE_SCALE
};
boost::shared_ptr<FilmState> state_copy () const;
diff --git a/src/lib/film_state.cc b/src/lib/film_state.cc
index 3d58a4fec..fed506863 100644
--- a/src/lib/film_state.cc
+++ b/src/lib/film_state.cc
@@ -29,6 +29,7 @@
#include <iomanip>
#include <sstream>
#include <boost/filesystem.hpp>
+#include <boost/date_time.hpp>
#include "film_state.h"
#include "scaler.h"
#include "filter.h"
@@ -80,6 +81,9 @@ FilmState::write_metadata (ofstream& f) const
f << "audio_gain " << audio_gain << "\n";
f << "audio_delay " << audio_delay << "\n";
f << "still_duration " << still_duration << "\n";
+ f << "with_subtitles " << with_subtitles << "\n";
+ f << "subtitle_offset " << subtitle_offset << "\n";
+ f << "subtitle_scale " << subtitle_scale << "\n";
/* Cached stuff; this is information about our content; we could
look it up each time, but that's slow.
@@ -94,6 +98,7 @@ FilmState::write_metadata (ofstream& f) const
f << "audio_sample_rate " << audio_sample_rate << "\n";
f << "audio_sample_format " << audio_sample_format_to_string (audio_sample_format) << "\n";
f << "content_digest " << content_digest << "\n";
+ f << "has_subtitles " << has_subtitles << "\n";
}
/** Read state from a key / value pair.
@@ -142,6 +147,12 @@ FilmState::read_metadata (string k, string v)
audio_delay = atoi (v.c_str ());
} else if (k == "still_duration") {
still_duration = atoi (v.c_str ());
+ } else if (k == "with_subtitles") {
+ with_subtitles = (v == "1");
+ } else if (k == "subtitle_offset") {
+ subtitle_offset = atoi (v.c_str ());
+ } else if (k == "subtitle_scale") {
+ subtitle_scale = atof (v.c_str ());
}
/* Cached stuff */
@@ -165,6 +176,8 @@ FilmState::read_metadata (string k, string v)
audio_sample_format = audio_sample_format_from_string (v);
} else if (k == "content_digest") {
content_digest = v;
+ } else if (k == "has_subtitles") {
+ has_subtitles = (v == "1");
}
}
@@ -185,9 +198,21 @@ FilmState::thumb_file (int n) const
string
FilmState::thumb_file_for_frame (int n) const
{
+ return thumb_base_for_frame(n) + ".png";
+}
+
+string
+FilmState::thumb_base (int n) const
+{
+ return thumb_base_for_frame (thumb_frame (n));
+}
+
+string
+FilmState::thumb_base_for_frame (int n) const
+{
stringstream s;
s.width (8);
- s << setfill('0') << n << ".tiff";
+ s << setfill('0') << n;
filesystem::path p;
p /= dir ("thumbs");
@@ -306,4 +331,64 @@ FilmState::dcp_length () const
return length;
}
-
+string
+FilmState::dci_name () const
+{
+ stringstream d;
+ d << dci_name_prefix << "_";
+
+ if (dcp_content_type) {
+ d << dcp_content_type->dci_name() << "_";
+ }
+
+ if (format) {
+ d << format->dci_name() << "_";
+ }
+
+ if (!audio_language.empty ()) {
+ d << audio_language;
+ if (!subtitle_language.empty ()) {
+ d << "-" << subtitle_language;
+ }
+ d << "_";
+ }
+
+ if (!territory.empty ()) {
+ d << territory;
+ if (!rating.empty ()) {
+ d << "-" << rating;
+ }
+ d << "_";
+ }
+
+ switch (audio_channels) {
+ case 1:
+ d << "1_";
+ break;
+ case 2:
+ d << "2_";
+ break;
+ case 6:
+ d << "51_";
+ break;
+ }
+
+ d << "2K_";
+
+ if (!studio.empty ()) {
+ d << studio << "_";
+ }
+
+ gregorian::date today = gregorian::day_clock::local_day ();
+ d << gregorian::to_iso_extended_string (today) << "_";
+
+ if (!facility.empty ()) {
+ d << facility << "_";
+ }
+
+ if (!package_type.empty ()) {
+ d << package_type;
+ }
+
+ return d.str ();
+}
diff --git a/src/lib/film_state.h b/src/lib/film_state.h
index 16a1b0508..e58d46b0f 100644
--- a/src/lib/film_state.h
+++ b/src/lib/film_state.h
@@ -62,10 +62,14 @@ public:
, audio_gain (0)
, audio_delay (0)
, still_duration (10)
+ , with_subtitles (false)
+ , subtitle_offset (0)
+ , subtitle_scale (1)
, length (0)
, audio_channels (0)
, audio_sample_rate (0)
, audio_sample_format (AV_SAMPLE_FMT_NONE)
+ , has_subtitles (false)
{}
std::string file (std::string f) const;
@@ -77,6 +81,7 @@ public:
bool content_is_dvd () const;
std::string thumb_file (int) const;
+ std::string thumb_base (int) const;
int thumb_frame (int) const;
int bytes_per_sample () const;
@@ -86,8 +91,8 @@ public:
void read_metadata (std::string, std::string);
Size cropped_size (Size) const;
-
int dcp_length () const;
+ std::string dci_name () const;
/** Complete path to directory containing the film metadata;
must not be relative.
@@ -126,6 +131,22 @@ public:
int audio_delay;
/** Duration to make still-sourced films (in seconds) */
int still_duration;
+ bool with_subtitles;
+ /** y offset for placing subtitles, in source pixels; +ve is further down
+ the frame, -ve is further up.
+ */
+ int subtitle_offset;
+ float subtitle_scale;
+
+ /* DCI naming stuff */
+ std::string dci_name_prefix;
+ std::string audio_language;
+ std::string subtitle_language;
+ std::string territory;
+ std::string rating;
+ std::string studio;
+ std::string facility;
+ std::string package_type;
/* Data which is cached to speed things up */
@@ -143,9 +164,12 @@ public:
AVSampleFormat audio_sample_format;
/** MD5 digest of our content file */
std::string content_digest;
+ /** true if the source has subtitles */
+ bool has_subtitles;
private:
std::string thumb_file_for_frame (int) const;
+ std::string thumb_base_for_frame (int) const;
};
#endif
diff --git a/src/lib/filter.cc b/src/lib/filter.cc
index ab5a6316f..446cc111d 100644
--- a/src/lib/filter.cc
+++ b/src/lib/filter.cc
@@ -72,6 +72,7 @@ Filter::setup_filters ()
_filters.push_back (new Filter ("ppl5", "FIR low-pass deinterlacer", "", "l5"));
_filters.push_back (new Filter ("mcdeint", "Motion compensating deinterlacer", "mcdeint", ""));
_filters.push_back (new Filter ("kerndeint", "Kernel deinterlacer", "kerndeint", ""));
+ _filters.push_back (new Filter ("yadif", "Yet Another Deinterlacing Filter", "yadif", ""));
_filters.push_back (new Filter ("pptn", "Temporal noise reducer", "", "tn"));
_filters.push_back (new Filter ("ppfq", "Force quantizer", "", "fq"));
_filters.push_back (new Filter ("gradfun", "Gradient debander", "gradfun", ""));
diff --git a/src/lib/format.cc b/src/lib/format.cc
index aaf5211f9..2eb4990da 100644
--- a/src/lib/format.cc
+++ b/src/lib/format.cc
@@ -63,18 +63,18 @@ Format::as_metadata () const
void
Format::setup_formats ()
{
- _formats.push_back (new FixedFormat (119, Size (1285, 1080), "119", "1.19"));
- _formats.push_back (new FixedFormat (133, Size (1436, 1080), "133", "1.33"));
- _formats.push_back (new FixedFormat (138, Size (1485, 1080), "138", "1.375"));
- _formats.push_back (new FixedFormat (133, Size (1998, 1080), "133-in-flat", "4:3 within Flat"));
- _formats.push_back (new FixedFormat (137, Size (1480, 1080), "137", "Academy"));
- _formats.push_back (new FixedFormat (166, Size (1793, 1080), "166", "1.66"));
- _formats.push_back (new FixedFormat (166, Size (1998, 1080), "166-in-flat", "1.66 within Flat"));
- _formats.push_back (new FixedFormat (178, Size (1998, 1080), "178-in-flat", "16:9 within Flat"));
- _formats.push_back (new FixedFormat (185, Size (1998, 1080), "185", "Flat"));
- _formats.push_back (new FixedFormat (239, Size (2048, 858), "239", "Scope"));
- _formats.push_back (new VariableFormat (Size (1998, 1080), "var-185", "Flat"));
- _formats.push_back (new VariableFormat (Size (2048, 858), "var-239", "Scope"));
+ _formats.push_back (new FixedFormat (119, Size (1285, 1080), "119", "1.19", "F"));
+ _formats.push_back (new FixedFormat (133, Size (1436, 1080), "133", "1.33", "F"));
+ _formats.push_back (new FixedFormat (138, Size (1485, 1080), "138", "1.375", "F"));
+ _formats.push_back (new FixedFormat (133, Size (1998, 1080), "133-in-flat", "4:3 within Flat", "F"));
+ _formats.push_back (new FixedFormat (137, Size (1480, 1080), "137", "Academy", "F"));
+ _formats.push_back (new FixedFormat (166, Size (1793, 1080), "166", "1.66", "F"));
+ _formats.push_back (new FixedFormat (166, Size (1998, 1080), "166-in-flat", "1.66 within Flat", "F"));
+ _formats.push_back (new FixedFormat (178, Size (1998, 1080), "178-in-flat", "16:9 within Flat", "F"));
+ _formats.push_back (new FixedFormat (185, Size (1998, 1080), "185", "Flat", "F"));
+ _formats.push_back (new FixedFormat (239, Size (2048, 858), "239", "Scope", "S"));
+ _formats.push_back (new VariableFormat (Size (1998, 1080), "var-185", "Flat", "F"));
+ _formats.push_back (new VariableFormat (Size (2048, 858), "var-239", "Scope", "S"));
}
/** @param n Nickname.
@@ -135,8 +135,8 @@ Format::all ()
* @param id ID (e.g. 185)
* @param n Nick name (e.g. Flat)
*/
-FixedFormat::FixedFormat (int r, Size dcp, string id, string n)
- : Format (dcp, id, n)
+FixedFormat::FixedFormat (int r, Size dcp, string id, string n, string d)
+ : Format (dcp, id, n, d)
, _ratio (r)
{
@@ -155,8 +155,8 @@ Format::dcp_padding (Film const * f) const
return p;
}
-VariableFormat::VariableFormat (Size dcp, string id, string n)
- : Format (dcp, id, n)
+VariableFormat::VariableFormat (Size dcp, string id, string n, string d)
+ : Format (dcp, id, n, d)
{
}
diff --git a/src/lib/format.h b/src/lib/format.h
index fd6cdbece..35dd4fb85 100644
--- a/src/lib/format.h
+++ b/src/lib/format.h
@@ -31,10 +31,11 @@ class Film;
class Format
{
public:
- Format (Size dcp, std::string id, std::string n)
+ Format (Size dcp, std::string id, std::string n, std::string d)
: _dcp_size (dcp)
, _id (id)
, _nickname (n)
+ , _dci_name (d)
{}
/** @return the aspect ratio multiplied by 100
@@ -67,6 +68,10 @@ public:
return _nickname;
}
+ std::string dci_name () const {
+ return _dci_name;
+ }
+
std::string as_metadata () const;
static Format const * from_nickname (std::string n);
@@ -85,6 +90,7 @@ protected:
std::string _id;
/** nickname (e.g. Flat, Scope) */
std::string _nickname;
+ std::string _dci_name;
private:
/** all available formats */
@@ -98,7 +104,7 @@ private:
class FixedFormat : public Format
{
public:
- FixedFormat (int, Size, std::string, std::string);
+ FixedFormat (int, Size, std::string, std::string, std::string);
int ratio_as_integer (Film const *) const {
return _ratio;
@@ -119,7 +125,7 @@ private:
class VariableFormat : public Format
{
public:
- VariableFormat (Size, std::string, std::string);
+ VariableFormat (Size, std::string, std::string, std::string);
int ratio_as_integer (Film const * f) const;
float ratio_as_float (Film const * f) const;
diff --git a/src/lib/image.cc b/src/lib/image.cc
index 2df7636af..f5aef8444 100644
--- a/src/lib/image.cc
+++ b/src/lib/image.cc
@@ -26,6 +26,7 @@
#include <iostream>
#include <sys/time.h>
#include <boost/algorithm/string.hpp>
+#include <boost/bind.hpp>
#include <openjpeg.h>
extern "C" {
#include <libavcodec/avcodec.h>
@@ -57,6 +58,7 @@ Image::lines (int n) const
}
break;
case PIX_FMT_RGB24:
+ case PIX_FMT_RGBA:
return size().height;
default:
assert (false);
@@ -73,6 +75,7 @@ Image::components () const
case PIX_FMT_YUV420P:
return 3;
case PIX_FMT_RGB24:
+ case PIX_FMT_RGBA:
return 1;
default:
assert (false);
@@ -81,11 +84,36 @@ Image::components () const
return 0;
}
+shared_ptr<Image>
+Image::scale (Size out_size, Scaler const * scaler) const
+{
+ assert (scaler);
+
+ shared_ptr<Image> scaled (new AlignedImage (pixel_format(), out_size));
+
+ struct SwsContext* scale_context = sws_getContext (
+ size().width, size().height, pixel_format(),
+ out_size.width, out_size.height, pixel_format(),
+ scaler->ffmpeg_id (), 0, 0, 0
+ );
+
+ sws_scale (
+ scale_context,
+ data(), stride(),
+ 0, size().height,
+ scaled->data(), scaled->stride()
+ );
+
+ sws_freeContext (scale_context);
+
+ return scaled;
+}
+
/** Scale this image to a given size and convert it to RGB.
* @param out_size Output image size in pixels.
* @param scaler Scaler to use.
*/
-shared_ptr<RGBFrameImage>
+shared_ptr<Image>
Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scaler) const
{
assert (scaler);
@@ -93,7 +121,7 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal
Size content_size = out_size;
content_size.width -= (padding * 2);
- shared_ptr<RGBFrameImage> rgb (new RGBFrameImage (content_size));
+ shared_ptr<Image> rgb (new AlignedImage (PIX_FMT_RGB24, content_size));
struct SwsContext* scale_context = sws_getContext (
size().width, size().height, pixel_format(),
@@ -104,9 +132,9 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal
/* Scale and convert to RGB from whatever its currently in (which may be RGB) */
sws_scale (
scale_context,
- data(), line_size(),
+ data(), stride(),
0, size().height,
- rgb->data (), rgb->line_size ()
+ rgb->data(), rgb->stride()
);
/* Put the image in the right place in a black frame if are padding; this is
@@ -114,7 +142,7 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal
scheme of things.
*/
if (padding > 0) {
- shared_ptr<RGBFrameImage> padded_rgb (new RGBFrameImage (out_size));
+ shared_ptr<Image> padded_rgb (new AlignedImage (PIX_FMT_RGB24, out_size));
padded_rgb->make_black ();
/* XXX: we are cheating a bit here; we know the frame is RGB so we can
@@ -124,8 +152,8 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal
uint8_t* q = rgb->data()[0];
for (int j = 0; j < rgb->lines(0); ++j) {
memcpy (p, q, rgb->line_size()[0]);
- p += padded_rgb->line_size()[0];
- q += rgb->line_size()[0];
+ p += padded_rgb->stride()[0];
+ q += rgb->stride()[0];
}
rgb = padded_rgb;
@@ -140,17 +168,17 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal
* @param pp Flags for the required set of post processes.
* @return Post-processed image.
*/
-shared_ptr<PostProcessImage>
+shared_ptr<Image>
Image::post_process (string pp) const
{
- shared_ptr<PostProcessImage> out (new PostProcessImage (PIX_FMT_YUV420P, size ()));
+ shared_ptr<Image> out (new AlignedImage (PIX_FMT_YUV420P, size ()));
pp_mode* mode = pp_get_mode_by_name_and_quality (pp.c_str (), PP_QUALITY_MAX);
pp_context* context = pp_get_context (size().width, size().height, PP_FORMAT_420 | PP_CPU_CAPS_MMX2);
pp_postprocess (
- (const uint8_t **) data(), line_size(),
- out->data(), out->line_size(),
+ (const uint8_t **) data(), stride(),
+ out->data(), out->stride(),
size().width, size().height,
0, 0, mode, context, 0
);
@@ -166,13 +194,13 @@ Image::make_black ()
{
switch (_pixel_format) {
case PIX_FMT_YUV420P:
- memset (data()[0], 0, lines(0) * line_size()[0]);
- memset (data()[1], 0x80, lines(1) * line_size()[1]);
- memset (data()[2], 0x80, lines(2) * line_size()[2]);
+ memset (data()[0], 0, lines(0) * stride()[0]);
+ memset (data()[1], 0x80, lines(1) * stride()[1]);
+ memset (data()[2], 0x80, lines(2) * stride()[2]);
break;
case PIX_FMT_RGB24:
- memset (data()[0], 0, lines(0) * line_size()[0]);
+ memset (data()[0], 0, lines(0) * stride()[0]);
break;
default:
@@ -180,22 +208,80 @@ Image::make_black ()
}
}
+void
+Image::alpha_blend (shared_ptr<Image> other, Position position)
+{
+ /* Only implemented for RGBA onto RGB24 so far */
+ assert (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA);
+
+ int start_tx = position.x;
+ int start_ox = 0;
+
+ if (start_tx < 0) {
+ start_ox = -start_tx;
+ start_tx = 0;
+ }
+
+ int start_ty = position.y;
+ int start_oy = 0;
+
+ if (start_ty < 0) {
+ start_oy = -start_ty;
+ start_ty = 0;
+ }
+
+ for (int ty = start_ty, oy = start_oy; ty < size().height && oy < other->size().height; ++ty, ++oy) {
+ uint8_t* tp = data()[0] + ty * stride()[0] + position.x * 3;
+ uint8_t* op = other->data()[0] + oy * other->stride()[0];
+ for (int tx = start_tx, ox = start_ox; tx < size().width && ox < other->size().width; ++tx, ++ox) {
+ float const alpha = float (op[3]) / 255;
+ tp[0] = (tp[0] * (1 - alpha)) + op[0] * alpha;
+ tp[1] = (tp[1] * (1 - alpha)) + op[1] * alpha;
+ tp[2] = (tp[2] * (1 - alpha)) + op[2] * alpha;
+ tp += 3;
+ op += 4;
+ }
+ }
+}
+
/** Construct a SimpleImage of a given size and format, allocating memory
* as required.
*
* @param p Pixel format.
* @param s Size in pixels.
*/
-SimpleImage::SimpleImage (PixelFormat p, Size s)
+SimpleImage::SimpleImage (PixelFormat p, Size s, function<int (int)> rounder)
: Image (p)
, _size (s)
{
- _data = (uint8_t **) av_malloc (components() * sizeof (uint8_t *));
- _line_size = (int *) av_malloc (components() * sizeof (int));
+ _data = (uint8_t **) av_malloc (4 * sizeof (uint8_t *));
+ _data[0] = _data[1] = _data[2] = _data[3] = 0;
+ _line_size = (int *) av_malloc (4);
+ _line_size[0] = _line_size[1] = _line_size[2] = _line_size[3] = 0;
+
+ _stride = (int *) av_malloc (4);
+ _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:
+ _line_size[0] = s.width;
+ _line_size[1] = s.width / 2;
+ _line_size[2] = s.width / 2;
+ break;
+ default:
+ assert (false);
+ }
+
for (int i = 0; i < components(); ++i) {
- _data[i] = 0;
- _line_size[i] = 0;
+ _stride[i] = rounder (_line_size[i]);
+ _data[i] = (uint8_t *) av_malloc (_stride[i] * lines (i));
}
}
@@ -208,17 +294,7 @@ SimpleImage::~SimpleImage ()
av_free (_data);
av_free (_line_size);
-}
-
-/** Set the size in bytes of each horizontal line of a given component.
- * @param i Component index.
- * @param s Size of line in bytes.
- */
-void
-SimpleImage::set_line_size (int i, int s)
-{
- _line_size[i] = s;
- _data[i] = (uint8_t *) av_malloc (s * lines (i));
+ av_free (_stride);
}
uint8_t **
@@ -233,12 +309,49 @@ SimpleImage::line_size () const
return _line_size;
}
+int *
+SimpleImage::stride () const
+{
+ return _stride;
+}
+
Size
SimpleImage::size () const
{
return _size;
}
+AlignedImage::AlignedImage (PixelFormat f, Size s)
+ : SimpleImage (f, s, boost::bind (round_up, _1, 32))
+{
+
+}
+
+CompactImage::CompactImage (PixelFormat f, Size s)
+ : SimpleImage (f, s, boost::bind (round_up, _1, 1))
+{
+
+}
+
+CompactImage::CompactImage (shared_ptr<Image> im)
+ : SimpleImage (im->pixel_format(), im->size(), boost::bind (round_up, _1, 1))
+{
+ assert (components() == im->components());
+
+ for (int c = 0; c < components(); ++c) {
+
+ assert (line_size()[c] == im->line_size()[c]);
+
+ uint8_t* t = data()[c];
+ uint8_t* o = im->data()[c];
+
+ for (int y = 0; y < lines(c); ++y) {
+ memcpy (t, o, line_size()[c]);
+ t += stride()[c];
+ o += im->stride()[c];
+ }
+ }
+}
FilterBufferImage::FilterBufferImage (PixelFormat p, AVFilterBufferRef* b)
: Image (p)
@@ -264,6 +377,13 @@ FilterBufferImage::line_size () const
return _buffer->linesize;
}
+int *
+FilterBufferImage::stride () const
+{
+ /* XXX? */
+ return _buffer->linesize;
+}
+
Size
FilterBufferImage::size () const
{
@@ -308,49 +428,15 @@ RGBFrameImage::line_size () const
return _frame->linesize;
}
-Size
-RGBFrameImage::size () const
-{
- return _size;
-}
-
-PostProcessImage::PostProcessImage (PixelFormat p, Size s)
- : Image (p)
- , _size (s)
-{
- _data = new uint8_t*[4];
- _line_size = new int[4];
-
- for (int i = 0; i < 4; ++i) {
- _data[i] = (uint8_t *) av_malloc (s.width * s.height);
- _line_size[i] = s.width;
- }
-}
-
-PostProcessImage::~PostProcessImage ()
-{
- for (int i = 0; i < 4; ++i) {
- av_free (_data[i]);
- }
-
- delete[] _data;
- delete[] _line_size;
-}
-
-uint8_t **
-PostProcessImage::data () const
-{
- return _data;
-}
-
int *
-PostProcessImage::line_size () const
+RGBFrameImage::stride () const
{
- return _line_size;
+ /* XXX? */
+ return line_size ();
}
Size
-PostProcessImage::size () const
+RGBFrameImage::size () const
{
return _size;
}
diff --git a/src/lib/image.h b/src/lib/image.h
index 0161d2b01..30c8519e7 100644
--- a/src/lib/image.h
+++ b/src/lib/image.h
@@ -26,6 +26,7 @@
#include <string>
#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
@@ -34,7 +35,7 @@ extern "C" {
class Scaler;
class RGBFrameImage;
-class PostProcessImage;
+class SimpleImage;
/** @class Image
* @brief Parent class for wrappers of some image, in some format, that
@@ -57,16 +58,21 @@ public:
/** @return Array of pointers to arrays of the component data */
virtual uint8_t ** data () const = 0;
- /** @return Array of sizes of each line, in pixels */
+ /** @return Array of sizes of the data in each line, in bytes (without any alignment padding bytes) */
virtual int * line_size () const = 0;
+ /** @return Array of strides for each line (including any alignment padding bytes) */
+ virtual int * stride () const = 0;
+
/** @return Size of the image, in pixels */
virtual Size size () const = 0;
int components () const;
int lines (int) const;
- boost::shared_ptr<RGBFrameImage> scale_and_convert_to_rgb (Size, int, Scaler const *) const;
- boost::shared_ptr<PostProcessImage> post_process (std::string) const;
+ boost::shared_ptr<Image> scale_and_convert_to_rgb (Size, int, Scaler const *) const;
+ boost::shared_ptr<Image> scale (Size, Scaler const *) const;
+ boost::shared_ptr<Image> post_process (std::string) const;
+ void alpha_blend (boost::shared_ptr<Image> image, Position pos);
void make_black ();
@@ -89,6 +95,7 @@ public:
uint8_t ** data () const;
int * line_size () const;
+ int * stride () const;
Size size () const;
private:
@@ -101,19 +108,33 @@ private:
class SimpleImage : public Image
{
public:
- SimpleImage (PixelFormat, Size);
+ SimpleImage (PixelFormat, Size, boost::function<int (int)> rounder);
~SimpleImage ();
uint8_t ** data () const;
int * line_size () const;
+ int * stride () const;
Size size () const;
- void set_line_size (int, int);
-
private:
+
Size _size; ///< size in pixels
uint8_t** _data; ///< array of pointers to components
- int* _line_size; ///< array of widths of each line, in bytes
+ int* _line_size; ///< array of sizes of the data in each line, in pixels (without any alignment padding bytes)
+ int* _stride; ///< array of strides for each line (including any alignment padding bytes)
+};
+
+class AlignedImage : public SimpleImage
+{
+public:
+ AlignedImage (PixelFormat, Size);
+};
+
+class CompactImage : public SimpleImage
+{
+public:
+ CompactImage (PixelFormat, Size);
+ CompactImage (boost::shared_ptr<Image>);
};
/** @class RGBFrameImage
@@ -127,6 +148,7 @@ public:
uint8_t ** data () const;
int * line_size () const;
+ int * stride () const;
Size size () const;
AVFrame * frame () const {
return _frame;
@@ -138,23 +160,4 @@ private:
uint8_t* _data;
};
-/** @class PostProcessImage
- * @brief An image that is the result of an FFmpeg post-processing run.
- */
-class PostProcessImage : public Image
-{
-public:
- PostProcessImage (PixelFormat, Size);
- ~PostProcessImage ();
-
- uint8_t ** data () const;
- int * line_size () const;
- Size size () const;
-
-private:
- Size _size;
- uint8_t** _data;
- int* _line_size;
-};
-
#endif
diff --git a/src/lib/imagemagick_decoder.cc b/src/lib/imagemagick_decoder.cc
index 7cee01ec5..32c433d09 100644
--- a/src/lib/imagemagick_decoder.cc
+++ b/src/lib/imagemagick_decoder.cc
@@ -1,3 +1,22 @@
+/*
+ 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.
+
+*/
+
#include <iostream>
#include <Magick++/Image.h>
#include "imagemagick_decoder.h"
@@ -5,6 +24,7 @@
#include "image.h"
using namespace std;
+using namespace boost;
ImageMagickDecoder::ImageMagickDecoder (
boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Job* j, Log* l, bool minimal, bool ignore_length)
diff --git a/src/lib/imagemagick_decoder.h b/src/lib/imagemagick_decoder.h
index aca91ef55..809f3aecd 100644
--- a/src/lib/imagemagick_decoder.h
+++ b/src/lib/imagemagick_decoder.h
@@ -1,3 +1,22 @@
+/*
+ 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.
+
+*/
+
#include "decoder.h"
namespace Magick {
@@ -35,6 +54,10 @@ public:
return 0;
}
+ bool has_subtitles () const {
+ return false;
+ }
+
static float static_frames_per_second () {
return 24;
}
diff --git a/src/lib/imagemagick_encoder.cc b/src/lib/imagemagick_encoder.cc
new file mode 100644
index 000000000..9bd8162f8
--- /dev/null
+++ b/src/lib/imagemagick_encoder.cc
@@ -0,0 +1,101 @@
+/*
+ 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.
+
+*/
+
+/** @file src/imagemagick_encoder.cc
+ * @brief An encoder that writes image files using ImageMagick (and does nothing with audio).
+ */
+
+#include <stdexcept>
+#include <vector>
+#include <sstream>
+#include <iomanip>
+#include <iostream>
+#include <fstream>
+#include <boost/filesystem.hpp>
+#include <Magick++/Image.h>
+#include "imagemagick_encoder.h"
+#include "film.h"
+#include "film_state.h"
+#include "options.h"
+#include "exceptions.h"
+#include "image.h"
+#include "subtitle.h"
+
+using namespace std;
+using namespace boost;
+
+/** @param s FilmState of the film that we are encoding.
+ * @param o Options.
+ * @param l Log.
+ */
+ImageMagickEncoder::ImageMagickEncoder (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l)
+ : Encoder (s, o, l)
+{
+
+}
+
+void
+ImageMagickEncoder::process_video (shared_ptr<Image> image, int frame, shared_ptr<Subtitle> sub)
+{
+ shared_ptr<Image> scaled = image->scale_and_convert_to_rgb (_opt->out_size, _opt->padding, _fs->scaler);
+ shared_ptr<Image> compact (new CompactImage (scaled));
+
+ string tmp_file = _opt->frame_out_path (frame, true);
+ Magick::Image thumb (compact->size().width, compact->size().height, "RGB", MagickCore::CharPixel, compact->data()[0]);
+ thumb.magick ("PNG");
+ thumb.write (tmp_file);
+ filesystem::rename (tmp_file, _opt->frame_out_path (frame, false));
+
+ if (sub) {
+ float const x_scale = float (_opt->out_size.width) / _fs->size.width;
+ float const y_scale = float (_opt->out_size.height) / _fs->size.height;
+
+ string tmp_metadata_file = _opt->frame_out_path (frame, false, ".sub");
+ ofstream metadata (tmp_metadata_file.c_str ());
+
+ list<shared_ptr<SubtitleImage> > images = sub->images ();
+ int n = 0;
+ for (list<shared_ptr<SubtitleImage> >::iterator i = images.begin(); i != images.end(); ++i) {
+ stringstream ext;
+ ext << ".sub." << n << ".png";
+
+ Size new_size = (*i)->image()->size ();
+ new_size.width *= x_scale;
+ new_size.height *= y_scale;
+ shared_ptr<Image> scaled = (*i)->image()->scale (new_size, _fs->scaler);
+ shared_ptr<Image> compact (new CompactImage (scaled));
+
+ string tmp_sub_file = _opt->frame_out_path (frame, true, ext.str ());
+ Magick::Image sub_thumb (compact->size().width, compact->size().height, "RGBA", MagickCore::CharPixel, compact->data()[0]);
+ sub_thumb.magick ("PNG");
+ sub_thumb.write (tmp_sub_file);
+ filesystem::rename (tmp_sub_file, _opt->frame_out_path (frame, false, ext.str ()));
+
+ metadata << "image " << n << "\n"
+ << "x " << (*i)->position().x << "\n"
+ << "y " << (*i)->position().y << "\n";
+
+ metadata.close ();
+ filesystem::rename (tmp_metadata_file, _opt->frame_out_path (frame, false, ".sub"));
+ }
+
+ }
+
+ frame_done (frame);
+}
diff --git a/src/lib/tiff_encoder.h b/src/lib/imagemagick_encoder.h
index ef1ce25d2..ce6ca3e8f 100644
--- a/src/lib/tiff_encoder.h
+++ b/src/lib/imagemagick_encoder.h
@@ -17,8 +17,8 @@
*/
-/** @file src/tiff_encoder.h
- * @brief An encoder that writes TIFF files (and does nothing with audio).
+/** @file src/imagemagick_encoder.h
+ * @brief An encoder that writes image files using ImageMagick (and does nothing with audio).
*/
#include <string>
@@ -28,16 +28,16 @@
class FilmState;
class Log;
-/** @class TIFFEncoder
- * @brief An encoder that writes TIFF files (and does nothing with audio).
+/** @class ImageMagickEncoder
+ * @brief An encoder that writes image files using ImageMagick files (and does nothing with audio).
*/
-class TIFFEncoder : public Encoder
+class ImageMagickEncoder : public Encoder
{
public:
- TIFFEncoder (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l);
+ ImageMagickEncoder (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l);
void process_begin (int64_t audio_channel_layout, AVSampleFormat audio_sample_format) {}
- void process_video (boost::shared_ptr<Image>, int);
+ void process_video (boost::shared_ptr<Image>, int, boost::shared_ptr<Subtitle>);
void process_audio (uint8_t *, int) {}
void process_end () {}
};
diff --git a/src/lib/j2k_still_encoder.cc b/src/lib/j2k_still_encoder.cc
index d218d08fe..2b8aca649 100644
--- a/src/lib/j2k_still_encoder.cc
+++ b/src/lib/j2k_still_encoder.cc
@@ -48,11 +48,11 @@ J2KStillEncoder::J2KStillEncoder (shared_ptr<const FilmState> s, shared_ptr<cons
}
void
-J2KStillEncoder::process_video (shared_ptr<Image> yuv, int frame)
+J2KStillEncoder::process_video (shared_ptr<Image> yuv, int frame, shared_ptr<Subtitle> sub)
{
pair<string, string> const s = Filter::ffmpeg_strings (_fs->filters);
DCPVideoFrame* f = new DCPVideoFrame (
- yuv, _opt->out_size, _opt->padding, _fs->scaler, 0, _fs->frames_per_second, s.second,
+ yuv, sub, _opt->out_size, _opt->padding, _fs->subtitle_offset, _fs->subtitle_scale, _fs->scaler, 0, _fs->frames_per_second, s.second,
Config::instance()->colour_lut_index(), Config::instance()->j2k_bandwidth(),
_log
);
diff --git a/src/lib/j2k_still_encoder.h b/src/lib/j2k_still_encoder.h
index 7a03e1195..c48b9e69c 100644
--- a/src/lib/j2k_still_encoder.h
+++ b/src/lib/j2k_still_encoder.h
@@ -37,7 +37,7 @@ public:
J2KStillEncoder (boost::shared_ptr<const FilmState>, boost::shared_ptr<const Options>, Log *);
void process_begin (int64_t audio_channel_layout, AVSampleFormat audio_sample_format) {}
- void process_video (boost::shared_ptr<Image>, int);
+ void process_video (boost::shared_ptr<Image>, int, boost::shared_ptr<Subtitle>);
void process_audio (uint8_t *, int) {}
void process_end () {}
};
diff --git a/src/lib/j2k_wav_encoder.cc b/src/lib/j2k_wav_encoder.cc
index a1f70a08a..e2a3a5ed7 100644
--- a/src/lib/j2k_wav_encoder.cc
+++ b/src/lib/j2k_wav_encoder.cc
@@ -105,7 +105,7 @@ J2KWAVEncoder::close_sound_files ()
}
void
-J2KWAVEncoder::process_video (shared_ptr<Image> yuv, int frame)
+J2KWAVEncoder::process_video (shared_ptr<Image> yuv, int frame, shared_ptr<Subtitle> sub)
{
boost::mutex::scoped_lock lock (_worker_mutex);
@@ -126,7 +126,8 @@ J2KWAVEncoder::process_video (shared_ptr<Image> yuv, int frame)
TIMING ("adding to queue of %1", _queue.size ());
_queue.push_back (boost::shared_ptr<DCPVideoFrame> (
new DCPVideoFrame (
- yuv, _opt->out_size, _opt->padding, _fs->scaler, frame, _fs->frames_per_second, s.second,
+ yuv, sub, _opt->out_size, _opt->padding, _fs->subtitle_offset, _fs->subtitle_scale,
+ _fs->scaler, frame, _fs->frames_per_second, s.second,
Config::instance()->colour_lut_index (), Config::instance()->j2k_bandwidth (),
_log
)
diff --git a/src/lib/j2k_wav_encoder.h b/src/lib/j2k_wav_encoder.h
index e11358c2c..87068ad3d 100644
--- a/src/lib/j2k_wav_encoder.h
+++ b/src/lib/j2k_wav_encoder.h
@@ -38,6 +38,7 @@ class ServerDescription;
class DCPVideoFrame;
class Image;
class Log;
+class Subtitle;
/** @class J2KWAVEncoder
* @brief An encoder which writes JPEG2000 and WAV files.
@@ -49,7 +50,7 @@ public:
~J2KWAVEncoder ();
void process_begin (int64_t audio_channel_layout, AVSampleFormat audio_sample_format);
- void process_video (boost::shared_ptr<Image>, int);
+ void process_video (boost::shared_ptr<Image>, int, boost::shared_ptr<Subtitle>);
void process_audio (uint8_t *, int);
void process_end ();
diff --git a/src/lib/job.cc b/src/lib/job.cc
index 39ce4173a..d3871bf72 100644
--- a/src/lib/job.cc
+++ b/src/lib/job.cc
@@ -34,10 +34,11 @@ using namespace boost;
* @param o Options.
* @param l A log that we can write to.
*/
-Job::Job (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l)
+Job::Job (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l, shared_ptr<Job> req)
: _fs (s)
, _opt (o)
, _log (l)
+ , _required (req)
, _state (NEW)
, _start_time (0)
, _progress_unknown (false)
@@ -80,6 +81,13 @@ Job::run_wrapper ()
}
}
+bool
+Job::is_new () const
+{
+ boost::mutex::scoped_lock lm (_state_mutex);
+ return _state == NEW;
+}
+
/** @return true if the job is running */
bool
Job::running () const
diff --git a/src/lib/job.h b/src/lib/job.h
index 802bf468d..f50ed0784 100644
--- a/src/lib/job.h
+++ b/src/lib/job.h
@@ -39,7 +39,7 @@ class Options;
class Job : public boost::enable_shared_from_this<Job>
{
public:
- Job (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l);
+ Job (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l, boost::shared_ptr<Job> req);
/** @return user-readable name of this job */
virtual std::string name () const = 0;
@@ -48,6 +48,7 @@ public:
void start ();
+ bool is_new () const;
bool running () const;
bool finished () const;
bool finished_ok () const;
@@ -66,6 +67,10 @@ public:
void emit_finished ();
+ boost::shared_ptr<Job> required () const {
+ return _required;
+ }
+
/** Emitted from the GUI thread */
sigc::signal0<void> Finished;
@@ -95,6 +100,8 @@ private:
void run_wrapper ();
+ boost::shared_ptr<Job> _required;
+
/** mutex for _state and _error */
mutable boost::mutex _state_mutex;
/** current state of the job */
diff --git a/src/lib/job_manager.cc b/src/lib/job_manager.cc
index 76fcc6c5d..2db91a177 100644
--- a/src/lib/job_manager.cc
+++ b/src/lib/job_manager.cc
@@ -37,11 +37,12 @@ JobManager::JobManager ()
boost::thread (boost::bind (&JobManager::scheduler, this));
}
-void
+shared_ptr<Job>
JobManager::add (shared_ptr<Job> j)
{
boost::mutex::scoped_lock lm (_mutex);
_jobs.push_back (j);
+ return j;
}
void
@@ -93,19 +94,21 @@ JobManager::scheduler ()
while (1) {
{
boost::mutex::scoped_lock lm (_mutex);
- int running = 0;
- shared_ptr<Job> first_new;
for (list<shared_ptr<Job> >::iterator i = _jobs.begin(); i != _jobs.end(); ++i) {
if ((*i)->running ()) {
- ++running;
- } else if (!(*i)->finished () && first_new == 0) {
- first_new = *i;
- }
-
- if (running == 0 && first_new) {
- first_new->start ();
+ /* Something is already happening */
break;
}
+
+ if ((*i)->is_new()) {
+ shared_ptr<Job> r = (*i)->required ();
+ if (!r || r->finished_ok ()) {
+ (*i)->start ();
+
+ /* Only start one job at once */
+ break;
+ }
+ }
}
}
diff --git a/src/lib/job_manager.h b/src/lib/job_manager.h
index 8b79fd67d..4b70738f0 100644
--- a/src/lib/job_manager.h
+++ b/src/lib/job_manager.h
@@ -28,16 +28,12 @@ class Job;
/** @class JobManager
* @brief A simple scheduler for jobs.
- *
- * JobManager simply keeps a list of pending jobs, and assumes that all the jobs
- * are sufficiently CPU intensive that there is no point running them in parallel;
- * so jobs are just run one after the other.
*/
class JobManager
{
public:
- void add (boost::shared_ptr<Job>);
+ boost::shared_ptr<Job> add (boost::shared_ptr<Job>);
void add_after (boost::shared_ptr<Job> after, boost::shared_ptr<Job> j);
std::list<boost::shared_ptr<Job> > get () const;
bool work_to_do () const;
diff --git a/src/lib/make_dcp_job.cc b/src/lib/make_dcp_job.cc
index ae4bb4fbe..b42a38429 100644
--- a/src/lib/make_dcp_job.cc
+++ b/src/lib/make_dcp_job.cc
@@ -43,8 +43,8 @@ using namespace boost;
* @param o Options.
* @param l Log.
*/
-MakeDCPJob::MakeDCPJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l)
- : Job (s, o, l)
+MakeDCPJob::MakeDCPJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l, shared_ptr<Job> req)
+ : Job (s, o, l, req)
{
}
diff --git a/src/lib/make_dcp_job.h b/src/lib/make_dcp_job.h
index 677bed424..c350a819c 100644
--- a/src/lib/make_dcp_job.h
+++ b/src/lib/make_dcp_job.h
@@ -29,7 +29,7 @@
class MakeDCPJob : public Job
{
public:
- MakeDCPJob (boost::shared_ptr<const FilmState>, boost::shared_ptr<const Options>, Log *);
+ MakeDCPJob (boost::shared_ptr<const FilmState>, boost::shared_ptr<const Options>, Log *, boost::shared_ptr<Job> req);
std::string name () const;
void run ();
diff --git a/src/lib/options.h b/src/lib/options.h
index 39068c24f..86db35210 100644
--- a/src/lib/options.h
+++ b/src/lib/options.h
@@ -42,6 +42,7 @@ public:
, black_after (0)
, decode_video_frequency (0)
, decode_audio (true)
+ , decode_subtitles (false)
, _frame_out_path (f)
, _frame_out_extension (e)
, _multichannel_audio_out_path (m)
@@ -56,11 +57,15 @@ public:
* @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 (int f, bool t) const {
+ std::string frame_out_path (int f, bool t, std::string e = "") const {
+ if (e.empty ()) {
+ e = _frame_out_extension;
+ }
+
std::stringstream s;
s << _frame_out_path << "/";
s.width (8);
- s << std::setfill('0') << f << _frame_out_extension;
+ s << std::setfill('0') << f << e;
if (t) {
s << ".tmp";
@@ -95,6 +100,7 @@ public:
int black_after; ///< first frame for which to output a black frame, rather than the actual video content, or 0 for none
int decode_video_frequency; ///< skip frames so that this many are decoded in all (or 0) (for generating thumbnails)
bool decode_audio; ///< true to decode audio, otherwise false
+ bool decode_subtitles;
private:
/** Path of the directory to write video frames to */
diff --git a/src/lib/scp_dcp_job.cc b/src/lib/scp_dcp_job.cc
index dac4a602c..90122cea7 100644
--- a/src/lib/scp_dcp_job.cc
+++ b/src/lib/scp_dcp_job.cc
@@ -91,8 +91,8 @@ public:
};
-SCPDCPJob::SCPDCPJob (shared_ptr<const FilmState> s, Log* l)
- : Job (s, shared_ptr<const Options> (), l)
+SCPDCPJob::SCPDCPJob (shared_ptr<const FilmState> s, Log* l, shared_ptr<Job> req)
+ : Job (s, shared_ptr<const Options> (), l, req)
, _status ("Waiting")
{
diff --git a/src/lib/scp_dcp_job.h b/src/lib/scp_dcp_job.h
index 1c795be47..b457fdf5b 100644
--- a/src/lib/scp_dcp_job.h
+++ b/src/lib/scp_dcp_job.h
@@ -26,7 +26,7 @@
class SCPDCPJob : public Job
{
public:
- SCPDCPJob (boost::shared_ptr<const FilmState>, Log *);
+ SCPDCPJob (boost::shared_ptr<const FilmState>, Log *, boost::shared_ptr<Job> req);
std::string name () const;
void run ();
diff --git a/src/lib/server.cc b/src/lib/server.cc
index 28236e3e0..b5eda2eb8 100644
--- a/src/lib/server.cc
+++ b/src/lib/server.cc
@@ -88,6 +88,8 @@ Server::process (shared_ptr<Socket> socket)
int pixel_format_int;
Size out_size;
int padding;
+ int subtitle_offset;
+ int subtitle_scale;
string scaler_id;
int frame;
float frames_per_second;
@@ -99,6 +101,8 @@ Server::process (shared_ptr<Socket> socket)
>> pixel_format_int
>> out_size.width >> out_size.height
>> padding
+ >> subtitle_offset
+ >> subtitle_scale
>> scaler_id
>> frame
>> frames_per_second
@@ -112,19 +116,18 @@ Server::process (shared_ptr<Socket> socket)
post_process = "";
}
- shared_ptr<SimpleImage> image (new SimpleImage (pixel_format, in_size));
+ shared_ptr<Image> image (new AlignedImage (pixel_format, in_size));
for (int i = 0; i < image->components(); ++i) {
- int line_size;
- s >> line_size;
- image->set_line_size (i, line_size);
- }
-
- for (int i = 0; i < image->components(); ++i) {
- socket->read_definite_and_consume (image->data()[i], image->line_size()[i] * image->lines(i), 30);
+ socket->read_definite_and_consume (image->data()[i], image->stride()[i] * image->lines(i), 30);
}
+
+ /* XXX: subtitle */
+ DCPVideoFrame dcp_video_frame (
+ image, shared_ptr<Subtitle> (), out_size, padding, subtitle_offset, subtitle_scale,
+ scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, _log
+ );
- DCPVideoFrame dcp_video_frame (image, out_size, padding, scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, _log);
shared_ptr<EncodedData> encoded = dcp_video_frame.encode_locally ();
encoded->send (socket);
diff --git a/src/lib/subtitle.cc b/src/lib/subtitle.cc
new file mode 100644
index 000000000..0eb40b14e
--- /dev/null
+++ b/src/lib/subtitle.cc
@@ -0,0 +1,113 @@
+/*
+ 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.
+
+*/
+
+#include "subtitle.h"
+#include "image.h"
+#include "exceptions.h"
+#include "film_state.h"
+
+using namespace std;
+using namespace boost;
+
+Subtitle::Subtitle (AVSubtitle const & sub)
+{
+ /* subtitle PTS in seconds */
+ float const packet_time = (sub.pts / AV_TIME_BASE) + float (sub.pts % AV_TIME_BASE) / 1e6;
+
+ /* hence start time for this sub */
+ _from = packet_time + (double (sub.start_display_time) / 1e3);
+ _to = packet_time + (double (sub.end_display_time) / 1e3);
+
+ for (unsigned int i = 0; i < sub.num_rects; ++i) {
+ _images.push_back (shared_ptr<SubtitleImage> (new SubtitleImage (sub.rects[i])));
+ }
+}
+
+/** @param t Time in seconds from the start of the film */
+bool
+Subtitle::displayed_at (double t)
+{
+ return t >= _from && t <= _to;
+}
+
+SubtitleImage::SubtitleImage (AVSubtitleRect const * rect)
+ : _position (rect->x, rect->y)
+ , _image (new AlignedImage (PIX_FMT_RGBA, Size (rect->w, rect->h)))
+{
+ if (rect->type != SUBTITLE_BITMAP) {
+ throw DecodeError ("non-bitmap subtitles not yet supported");
+ }
+
+ /* Start of the first line in the subtitle */
+ uint8_t* sub_p = rect->pict.data[0];
+ /* sub_p looks up into a RGB palette which is here */
+ 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;
+ for (int x = 0; x < rect->w; ++x) {
+ *out_line_p++ = palette[*sub_line_p++];
+ }
+ sub_p += rect->pict.linesize[0];
+ out_p += _image->stride()[0] / sizeof (uint32_t);
+ }
+}
+
+Rectangle
+transformed_subtitle_area (
+ float target_x_scale, float target_y_scale,
+ Rectangle sub_area, int subtitle_offset, float subtitle_scale
+ )
+{
+ Rectangle tx;
+
+ sub_area.y += subtitle_offset;
+
+ /* We will scale the subtitle by the same amount as the video frame, and also by the additional
+ subtitle_scale
+ */
+ tx.w = sub_area.w * target_x_scale * subtitle_scale;
+ tx.h = sub_area.h * target_y_scale * subtitle_scale;
+
+ /* Then we need a corrective translation, consisting of two parts:
+ *
+ * 1. that which is the result of the scaling of the subtitle by target_x_scale and target_y_scale; this will be
+ * sub_area.x * target_x_scale and sub_area.y * target_y_scale.
+ *
+ * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
+ * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
+ * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
+ *
+ * Combining these two translations gives these expressions.
+ */
+
+ tx.x = target_x_scale * (sub_area.x + (sub_area.w * (1 - subtitle_scale) / 2));
+ tx.y = target_y_scale * (sub_area.y + (sub_area.h * (1 - subtitle_scale) / 2));
+
+ return tx;
+}
+
+Rectangle
+SubtitleImage::area () const
+{
+ return Rectangle (_position.x, _position.y, _image->size().width, _image->size().height);
+}
diff --git a/src/lib/subtitle.h b/src/lib/subtitle.h
new file mode 100644
index 000000000..6fd0d8772
--- /dev/null
+++ b/src/lib/subtitle.h
@@ -0,0 +1,75 @@
+/*
+ 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.
+
+*/
+
+#include <list>
+#include <boost/shared_ptr.hpp>
+#include "util.h"
+
+struct AVSubtitle;
+class SubtitleImage;
+class Image;
+class FilmState;
+
+class Subtitle
+{
+public:
+ Subtitle (AVSubtitle const &);
+
+ bool displayed_at (double t);
+
+ std::list<boost::shared_ptr<SubtitleImage> > images () const {
+ return _images;
+ }
+
+private:
+ /** display from time in seconds from the start of the film */
+ double _from;
+ /** display to time in seconds from the start of the film */
+ double _to;
+ std::list<boost::shared_ptr<SubtitleImage> > _images;
+};
+
+extern Rectangle transformed_subtitle_area (
+ float target_x_scale, float target_y_scale,
+ Rectangle sub_area, int subtitle_offset, float subtitle_scale
+ );
+
+class SubtitleImage
+{
+public:
+ SubtitleImage (AVSubtitleRect const *);
+
+ void set_position (Position p) {
+ _position = p;
+ }
+
+ Position position () const {
+ return _position;
+ }
+
+ boost::shared_ptr<Image> image () const {
+ return _image;
+ }
+
+ Rectangle area () const;
+
+private:
+ Position _position;
+ boost::shared_ptr<Image> _image;
+};
diff --git a/src/lib/thumbs_job.cc b/src/lib/thumbs_job.cc
index f6ed75ff7..16a8a7b01 100644
--- a/src/lib/thumbs_job.cc
+++ b/src/lib/thumbs_job.cc
@@ -24,7 +24,7 @@
#include <exception>
#include "thumbs_job.h"
#include "film_state.h"
-#include "tiff_encoder.h"
+#include "imagemagick_encoder.h"
#include "transcoder.h"
#include "options.h"
@@ -35,8 +35,8 @@ using namespace boost;
* @param o Options.
* @param l A log that we can write to.
*/
-ThumbsJob::ThumbsJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l)
- : Job (s, o, l)
+ThumbsJob::ThumbsJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l, shared_ptr<Job> req)
+ : Job (s, o, l, req)
{
}
@@ -51,7 +51,7 @@ void
ThumbsJob::run ()
{
try {
- shared_ptr<TIFFEncoder> e (new TIFFEncoder (_fs, _opt, _log));
+ shared_ptr<ImageMagickEncoder> e (new ImageMagickEncoder (_fs, _opt, _log));
Transcoder w (_fs, _opt, this, _log, e);
w.go ();
set_progress (1);
diff --git a/src/lib/thumbs_job.h b/src/lib/thumbs_job.h
index 1dd69a0f9..f7e30d576 100644
--- a/src/lib/thumbs_job.h
+++ b/src/lib/thumbs_job.h
@@ -31,7 +31,7 @@ class FilmState;
class ThumbsJob : public Job
{
public:
- ThumbsJob (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l);
+ ThumbsJob (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l, boost::shared_ptr<Job> req);
std::string name () const;
void run ();
};
diff --git a/src/lib/tiff_decoder.h b/src/lib/tiff_decoder.h
index d9b5b3969..b9849a259 100644
--- a/src/lib/tiff_decoder.h
+++ b/src/lib/tiff_decoder.h
@@ -53,6 +53,9 @@ public:
int audio_sample_rate () const;
AVSampleFormat audio_sample_format () const;
int64_t audio_channel_layout () const;
+ bool has_subtitles () const {
+ return false;
+ }
private:
bool do_pass ();
diff --git a/src/lib/tiff_encoder.cc b/src/lib/tiff_encoder.cc
deleted file mode 100644
index 19e34741d..000000000
--- a/src/lib/tiff_encoder.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- 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.
-
-*/
-
-/** @file src/tiff_encoder.h
- * @brief An encoder that writes TIFF files (and does nothing with audio).
- */
-
-#include <stdexcept>
-#include <vector>
-#include <sstream>
-#include <iomanip>
-#include <iostream>
-#include <boost/filesystem.hpp>
-#include <tiffio.h>
-#include "tiff_encoder.h"
-#include "film.h"
-#include "film_state.h"
-#include "options.h"
-#include "exceptions.h"
-#include "image.h"
-
-using namespace std;
-using namespace boost;
-
-/** @param s FilmState of the film that we are encoding.
- * @param o Options.
- * @param l Log.
- */
-TIFFEncoder::TIFFEncoder (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l)
- : Encoder (s, o, l)
-{
-
-}
-
-void
-TIFFEncoder::process_video (shared_ptr<Image> image, int frame)
-{
- shared_ptr<Image> scaled = image->scale_and_convert_to_rgb (_opt->out_size, _opt->padding, _fs->scaler);
- string tmp_file = _opt->frame_out_path (frame, true);
- TIFF* output = TIFFOpen (tmp_file.c_str (), "w");
- if (output == 0) {
- throw CreateFileError (tmp_file);
- }
-
- TIFFSetField (output, TIFFTAG_IMAGEWIDTH, _opt->out_size.width);
- TIFFSetField (output, TIFFTAG_IMAGELENGTH, _opt->out_size.height);
- TIFFSetField (output, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
- TIFFSetField (output, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
- TIFFSetField (output, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
- TIFFSetField (output, TIFFTAG_BITSPERSAMPLE, 8);
- TIFFSetField (output, TIFFTAG_SAMPLESPERPIXEL, 3);
-
- if (TIFFWriteEncodedStrip (output, 0, scaled->data()[0], _opt->out_size.width * _opt->out_size.height * 3) == 0) {
- throw WriteFileError (tmp_file, 0);
- }
-
- TIFFClose (output);
-
- boost::filesystem::rename (tmp_file, _opt->frame_out_path (frame, false));
- frame_done (frame);
-}
diff --git a/src/lib/transcode_job.cc b/src/lib/transcode_job.cc
index e1ba82359..a53a4b6ad 100644
--- a/src/lib/transcode_job.cc
+++ b/src/lib/transcode_job.cc
@@ -39,8 +39,8 @@ using namespace boost;
* @param o Options.
* @param l A log that we can write to.
*/
-TranscodeJob::TranscodeJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l)
- : Job (s, o, l)
+TranscodeJob::TranscodeJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l, shared_ptr<Job> req)
+ : Job (s, o, l, req)
{
}
diff --git a/src/lib/transcode_job.h b/src/lib/transcode_job.h
index 737f10de9..fe68a4910 100644
--- a/src/lib/transcode_job.h
+++ b/src/lib/transcode_job.h
@@ -32,7 +32,7 @@ class Encoder;
class TranscodeJob : public Job
{
public:
- TranscodeJob (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l);
+ TranscodeJob (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l, boost::shared_ptr<Job> req);
std::string name () const;
void run ();
diff --git a/src/lib/util.cc b/src/lib/util.cc
index 935566440..fbe77461e 100644
--- a/src/lib/util.cc
+++ b/src/lib/util.cc
@@ -600,3 +600,24 @@ Socket::read_indefinite (uint8_t* data, int size, int timeout)
assert (size >= _buffer_data);
memcpy (data, _buffer, size);
}
+
+Rectangle
+Rectangle::intersection (Rectangle const & other) const
+{
+ int const tx = max (x, other.x);
+ int const ty = max (y, other.y);
+
+ return Rectangle (
+ tx, ty,
+ min (x + w, other.x + other.w) - tx,
+ min (y + h, other.y + other.h) - ty
+ );
+}
+
+int
+round_up (int a, int t)
+{
+ a += (t - 1);
+ return a - (a % t);
+}
+
diff --git a/src/lib/util.h b/src/lib/util.h
index 3eac06e97..bd7675a8a 100644
--- a/src/lib/util.h
+++ b/src/lib/util.h
@@ -121,9 +121,35 @@ struct Position
int y;
};
+/** A rectangle */
+struct Rectangle
+{
+ Rectangle ()
+ : x (0)
+ , y (0)
+ , w (0)
+ , h (0)
+ {}
+
+ Rectangle (int x_, int y_, int w_, int h_)
+ : x (x_)
+ , y (y_)
+ , w (w_)
+ , h (h_)
+ {}
+
+ int x;
+ int y;
+ int w;
+ int h;
+
+ Rectangle intersection (Rectangle const & other) const;
+};
+
extern std::string crop_string (Position, Size);
extern int dcp_audio_sample_rate (int);
extern std::string colour_lut_index_to_name (int index);
+extern int round_up (int, int);
/** @class Socket
* @brief A class to wrap a boost::asio::ip::tcp::socket with some things
@@ -166,3 +192,4 @@ private:
};
#endif
+
diff --git a/src/lib/wscript b/src/lib/wscript
index c809226ce..63847224c 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -2,7 +2,7 @@ def build(bld):
obj = bld(features = 'cxx cxxshlib')
obj.name = 'libdvdomatic'
obj.export_includes = ['.']
- obj.uselib = 'AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE SNDFILE BOOST_FILESYSTEM BOOST_THREAD OPENJPEG POSTPROC TIFF SIGC++ MAGICK SSH DCP GLIB'
+ obj.uselib = 'AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE SNDFILE BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME OPENJPEG POSTPROC TIFF SIGC++ MAGICK SSH DCP GLIB'
if bld.env.TARGET_WINDOWS:
obj.uselib += ' WINSOCK2'
obj.source = """
@@ -30,6 +30,7 @@ def build(bld):
format.cc
image.cc
imagemagick_decoder.cc
+ imagemagick_encoder.cc
j2k_still_encoder.cc
j2k_wav_encoder.cc
job.cc
@@ -42,9 +43,9 @@ def build(bld):
screen.cc
server.cc
sound_processor.cc
+ subtitle.cc
thumbs_job.cc
tiff_decoder.cc
- tiff_encoder.cc
timer.cc
transcode_job.cc
transcoder.cc