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));
- }
+ Rectangle tx = subtitle_transformed_area (
+ float (_out_size.width) / _input->size().width,
+ float (_out_size.height) / _input->size().height,
+ _subtitle->area(), _subtitle_offset, _subtitle_scale
+ );
+
+ shared_ptr<Image> im = _subtitle->image()->scale (Size (tx.w, tx.h), _scaler);
+ prepared->alpha_blend (im, Position (tx.x, tx.y));
}
create_openjpeg_container ();
asio::ip::tcp::resolver::query query (serv->host_name(), boost::lexical_cast<string> (Config::instance()->server_port ()));
asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve (query);
- Socket socket;
+ shared_ptr<Socket> socket (new Socket);
- socket.connect (*endpoint_iterator, 30);
+ socket->connect (*endpoint_iterator, 30);
stringstream s;
s << "encode "
<< Config::instance()->colour_lut_index () << " "
<< Config::instance()->j2k_bandwidth () << " ";
- socket.write ((uint8_t *) s.str().c_str(), s.str().length() + 1, 30);
+ if (_subtitle) {
+ s << _subtitle->position().x << " " << _subtitle->position().y << " "
+ << _subtitle->image()->size().width << " " << _subtitle->image()->size().height;
+ } else {
+ s << "-1 -1 0 0";
+ }
+
+ 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->stride()[i] * _input->lines(i), 30);
+ _input->write_to_socket (socket);
+ if (_subtitle) {
+ _subtitle->image()->write_to_socket (socket);
}
char buffer[32];
- socket.read_indefinite ((uint8_t *) buffer, sizeof (buffer), 30);
- socket.consume (strlen (buffer) + 1);
+ socket->read_indefinite ((uint8_t *) buffer, sizeof (buffer), 30);
+ socket->consume (strlen (buffer) + 1);
shared_ptr<EncodedData> e (new RemotelyEncodedData (atoi (buffer)));
/* now read the rest */
- socket.read_definite_and_consume (e->data(), e->size(), 30);
+ socket->read_definite_and_consume (e->data(), e->size(), 30);
_log->log (String::compose ("Finished remotely-encoded frame %1", _frame));
_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));
- }
+ Position const p = _subtitle->position ();
+ _subtitle->set_position (Position (p.x - _fs->crop.left, p.y - _fs->crop.top));
}
}
signal_changed (SUBTITLE_SCALE);
}
-list<pair<Position, string> >
-Film::thumb_subtitles (int n) const
+pair<Position, string>
+Film::thumb_subtitle (int n) const
{
string sub_file = _state.thumb_base(n) + ".sub";
if (!filesystem::exists (sub_file)) {
- return list<pair<Position, string> > ();
+ return pair<Position, string> ();
}
ifstream f (sub_file.c_str ());
string line;
- int sub_number;
- int sub_x;
- list<pair<Position, string> > subs;
+ pair<Position, string> sub;
while (getline (f, line)) {
if (line.empty ()) {
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;
+ if (k == "x") {
+ sub.first.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)));
+ sub.first.y = v;
+ sub.second = String::compose ("%1.sub.png", _state.thumb_base(n));
}
}
- return subs;
+ return sub;
}
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;
+ std::pair<Position, std::string> thumb_subtitle (int) const;
void copy_from_dvd_post_gui ();
void examine_content ();
}
}
+void
+Image::read_from_socket (shared_ptr<Socket> socket)
+{
+ for (int i = 0; i < components(); ++i) {
+ uint8_t* p = data()[i];
+ for (int y = 0; y < lines(i); ++y) {
+ socket->read_definite_and_consume (p, line_size()[i], 30);
+ p += stride()[i];
+ }
+ }
+}
+
+void
+Image::write_to_socket (shared_ptr<Socket> socket) const
+{
+ for (int i = 0; i < components(); ++i) {
+ uint8_t* p = data()[i];
+ for (int y = 0; y < lines(i); ++y) {
+ socket->write (p, line_size()[i], 30);
+ p += stride()[i];
+ }
+ }
+}
+
/** Construct a SimpleImage of a given size and format, allocating memory
* as required.
*
void alpha_blend (boost::shared_ptr<Image> image, Position pos);
void make_black ();
+
+ void read_from_socket (boost::shared_ptr<Socket>);
+ void write_to_socket (boost::shared_ptr<Socket>) const;
PixelFormat pixel_format () const {
return _pixel_format;
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";
+ Size new_size = sub->image()->size ();
+ new_size.width *= x_scale;
+ new_size.height *= y_scale;
+ shared_ptr<Image> scaled = sub->image()->scale (new_size, _fs->scaler);
+ shared_ptr<Image> compact (new CompactImage (scaled));
+
+ string tmp_sub_file = _opt->frame_out_path (frame, true, ".sub.png");
+ 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, ".sub.png"));
- metadata.close ();
- filesystem::rename (tmp_metadata_file, _opt->frame_out_path (frame, false, ".sub"));
- }
+ metadata << "x " << sub->position().x << "\n"
+ << "y " << sub->position().y << "\n";
+ metadata.close ();
+ filesystem::rename (tmp_metadata_file, _opt->frame_out_path (frame, false, ".sub.png"));
}
frame_done (frame);
#include "image.h"
#include "dcp_video_frame.h"
#include "config.h"
+#include "subtitle.h"
using namespace std;
using namespace boost;
int
Server::process (shared_ptr<Socket> socket)
{
- char buffer[128];
+ char buffer[256];
socket->read_indefinite ((uint8_t *) buffer, sizeof (buffer), 30);
socket->consume (strlen (buffer) + 1);
stringstream s (buffer);
-
+
string command;
s >> command;
if (command != "encode") {
return -1;
}
-
+
Size in_size;
int pixel_format_int;
Size out_size;
string post_process;
int colour_lut_index;
int j2k_bandwidth;
+ Position subtitle_position;
+ Size subtitle_size;
s >> in_size.width >> in_size.height
>> pixel_format_int
>> frames_per_second
>> post_process
>> colour_lut_index
- >> j2k_bandwidth;
-
+ >> j2k_bandwidth
+ >> subtitle_position.x >> subtitle_position.y
+ >> subtitle_size.width >> subtitle_size.height;
+
PixelFormat pixel_format = (PixelFormat) pixel_format_int;
Scaler const * scaler = Scaler::from_id (scaler_id);
if (post_process == "none") {
}
shared_ptr<Image> image (new AlignedImage (pixel_format, in_size));
-
- for (int i = 0; i < image->components(); ++i) {
- socket->read_definite_and_consume (image->data()[i], image->stride()[i] * image->lines(i), 30);
+
+ image->read_from_socket (socket);
+
+ shared_ptr<Subtitle> sub;
+ if (subtitle_position.x != -1) {
+ shared_ptr<Image> subtitle_image (new AlignedImage (pixel_format, subtitle_size));
+ subtitle_image->read_from_socket (socket);
+ sub.reset (new Subtitle (subtitle_position, subtitle_image));
}
- /* XXX: subtitle */
DCPVideoFrame dcp_video_frame (
- image, shared_ptr<Subtitle> (), out_size, padding, subtitle_offset, subtitle_scale,
+ image, sub, out_size, padding, subtitle_offset, subtitle_scale,
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);
-
+
return frame;
}
_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])));
+ if (sub.num_rects > 1) {
+ throw DecodeError ("multi-part subtitles not yet supported");
}
-}
-/** @param t Time in seconds from the start of the film */
-bool
-Subtitle::displayed_at (double t)
-{
- return t >= _from && t <= _to;
-}
+ AVSubtitleRect const * rect = sub.rects[0];
-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");
}
+
+ _position = Position (rect->x, rect->y);
+ _image.reset (new AlignedImage (PIX_FMT_RGBA, Size (rect->w, rect->h)));
/* Start of the first line in the subtitle */
uint8_t* sub_p = rect->pict.data[0];
}
}
+Subtitle::Subtitle (Position p, shared_ptr<Image> i)
+ : _from (0)
+ , _to (0)
+ , _position (p)
+ , _image (i)
+{
+
+}
+
+/** @param t Time in seconds from the start of the film */
+bool
+Subtitle::displayed_at (double t)
+{
+ return t >= _from && t <= _to;
+}
+
Rectangle
-transformed_subtitle_area (
+subtitle_transformed_area (
float target_x_scale, float target_y_scale,
Rectangle sub_area, int subtitle_offset, float subtitle_scale
)
}
Rectangle
-SubtitleImage::area () const
+Subtitle::area () const
{
return Rectangle (_position.x, _position.y, _image->size().width, _image->size().height);
}
#include "util.h"
struct AVSubtitle;
-class SubtitleImage;
class Image;
-class FilmState;
class Subtitle
{
public:
Subtitle (AVSubtitle const &);
+ Subtitle (Position p, boost::shared_ptr<Image> i);
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;
}
}
Rectangle area () const;
-
+
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;
Position _position;
boost::shared_ptr<Image> _image;
};
+
+Rectangle
+subtitle_transformed_area (
+ float target_x_scale, float target_y_scale,
+ Rectangle sub_area, int subtitle_offset, float subtitle_scale
+ );
+
boost::asio::deadline_timer _deadline;
boost::asio::ip::tcp::socket _socket;
/** a buffer for small reads */
- uint8_t _buffer[256];
+ uint8_t _buffer[512];
/** amount of valid data in the buffer */
int _buffer_data;
};
if (_frame_rebuild_needed) {
_image.reset (new wxImage (std_to_wx (_film->thumb_file (_index))));
- _subtitles.clear ();
- list<pair<Position, string> > s = _film->thumb_subtitles (_index);
- for (list<pair<Position, string> >::iterator i = s.begin(); i != s.end(); ++i) {
- _subtitles.push_back (SubtitleView (i->first, std_to_wx (i->second)));
+ _subtitle.reset ();
+ pair<Position, string> s = _film->thumb_subtitle (_index);
+ if (!s.second.empty ()) {
+ _subtitle.reset (new SubtitleView (s.first, std_to_wx (s.second)));
}
_frame_rebuild_needed = false;
-
compose ();
- _composition_needed = false;
}
if (_composition_needed) {
compose ();
- _composition_needed = false;
}
wxPaintDC dc (this);
dc.DrawBitmap (*_bitmap, 0, 0, false);
}
- if (_film->with_subtitles ()) {
- for (list<SubtitleView>::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
- dc.DrawBitmap (*i->bitmap, i->transformed_area.x, i->transformed_area.y, true);
- }
+ if (_film->with_subtitles() && _subtitle) {
+ dc.DrawBitmap (*_subtitle->bitmap, _subtitle->transformed_area.x, _subtitle->transformed_area.y, true);
}
}
{
_bitmap.reset ();
_image.reset ();
- _subtitles.clear ();
+ _subtitle.reset ();
}
void recompose ()
void compose ()
{
+ _composition_needed = false;
+
if (!_film || !_image) {
return;
}
_bitmap.reset (new wxBitmap (_transformed_image));
- for (list<SubtitleView>::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
+ if (_subtitle) {
- i->transformed_area = transformed_subtitle_area (
- x_scale, y_scale, i->base_area, _film->subtitle_offset(), _film->subtitle_scale()
+ _subtitle->transformed_area = subtitle_transformed_area (
+ x_scale, y_scale, _subtitle->base_area, _film->subtitle_offset(), _film->subtitle_scale()
);
- i->transformed_image = i->base_image;
- i->transformed_image.Rescale (i->transformed_area.w, i->transformed_area.h, wxIMAGE_QUALITY_HIGH);
- i->transformed_area.x -= _film->crop().left;
- i->transformed_area.y -= _film->crop().top;
- i->bitmap.reset (new wxBitmap (i->transformed_image));
+ _subtitle->transformed_image = _subtitle->base_image;
+ _subtitle->transformed_image.Rescale (_subtitle->transformed_area.w, _subtitle->transformed_area.h, wxIMAGE_QUALITY_HIGH);
+ _subtitle->transformed_area.x -= _film->crop().left;
+ _subtitle->transformed_area.y -= _film->crop().top;
+ _subtitle->bitmap.reset (new wxBitmap (_subtitle->transformed_image));
}
}
shared_ptr<wxBitmap> bitmap;
};
- list<SubtitleView> _subtitles;
+ shared_ptr<SubtitleView> _subtitle;
};
BEGIN_EVENT_TABLE (ThumbPanel, wxPanel)
using namespace std;
using namespace boost;
+void
+setup_test_config ()
+{
+ Config::instance()->set_num_local_encoding_threads (1);
+ Config::instance()->set_colour_lut_index (0);
+ Config::instance()->set_j2k_bandwidth (200000000);
+ Config::instance()->set_servers (vector<ServerDescription*> ());
+ Config::instance()->set_server_port (61920);
+}
+
BOOST_AUTO_TEST_CASE (film_metadata_test)
{
dvdomatic_setup ();
+ setup_test_config ();
string const test_film = "build/test/film";
}
void
-do_remote_encode (shared_ptr<DCPVideoFrame> frame, ServerDescription* description, shared_ptr<EncodedData> locally_encoded)
+do_remote_encode (shared_ptr<DCPVideoFrame> frame, ServerDescription* description, shared_ptr<EncodedData> locally_encoded, int N)
{
shared_ptr<EncodedData> remotely_encoded;
BOOST_CHECK_NO_THROW (remotely_encoded = frame->encode_remotely (description));
);
shared_ptr<EncodedData> locally_encoded = frame->encode_locally ();
+ BOOST_ASSERT (locally_encoded);
- Config::instance()->set_server_port (61920);
Server* server = new Server (&log);
new thread (boost::bind (&Server::run, server, 2));
list<thread*> threads;
for (int i = 0; i < 8; ++i) {
- threads.push_back (new thread (boost::bind (do_remote_encode, frame, &description, locally_encoded)));
+ threads.push_back (new thread (boost::bind (do_remote_encode, frame, &description, locally_encoded, i)));
}
for (list<thread*>::iterator i = threads.begin(); i != threads.end(); ++i) {
(*i)->join ();
}
+
+ for (list<thread*>::iterator i = threads.begin(); i != threads.end(); ++i) {
+ delete *i;
+ }
}
BOOST_AUTO_TEST_CASE (make_dcp_test)