summaryrefslogtreecommitdiff
path: root/src/lib/j2k_encoder_remote_backend.cc
blob: 40d9a187109604341e381fdfdd56dc5fb9d9d4be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include "config.h"
#include "cross.h"
#include "dcp_video.h"
#include "dcpomatic_log.h"
#include "dcpomatic_socket.h"
#include "encode_server_description.h"
#include "j2k_encoder_remote_backend.h"
#include "player_video.h"
#include "warnings.h"
#include <dcp/raw_convert.h>
DCPOMATIC_DISABLE_WARNINGS
#include <libxml++/libxml++.h>
DCPOMATIC_ENABLE_WARNINGS
#include <boost/asio.hpp>
#include <boost/thread.hpp>

#include "i18n.h"


using std::string;
using std::vector;
using boost::optional;
using boost::shared_ptr;
using dcp::Data;
using dcp::raw_convert;


J2KEncoderRemoteBackend::J2KEncoderRemoteBackend (EncodeServerDescription server)
	: _server (server)
	, _backoff (0)
{

}


vector<Data>
J2KEncoderRemoteBackend::encode (vector<shared_ptr<DCPVideo> > all_video)
{
	DCPOMATIC_ASSERT (all_video.size() == 1);
	shared_ptr<DCPVideo> video = all_video.front();

	int const timeout = 30;

	try {
		boost::asio::io_service io_service;
		boost::asio::ip::tcp::resolver resolver (io_service);
		boost::asio::ip::tcp::resolver::query query (_server.host_name(), raw_convert<string>(ENCODE_FRAME_PORT));
		boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve (query);

		shared_ptr<Socket> socket (new Socket(timeout));

		socket->connect (*endpoint_iterator);

		/* Collect all XML metadata */
		xmlpp::Document doc;
		xmlpp::Element* root = doc.create_root_node ("EncodingRequest");
		root->add_child("Version")->add_child_text(raw_convert<string> (SERVER_LINK_VERSION));
		video->add_metadata (root);

		LOG_DEBUG_ENCODE (N_("Sending frame %1 to remote"), video->index());

		{
			Socket::WriteDigestScope ds (socket);

			/* Send XML metadata */
			string xml = doc.write_to_string ("UTF-8");
			socket->write (xml.length() + 1);
			socket->write ((uint8_t *) xml.c_str(), xml.length() + 1);

			/* Send binary data */
			LOG_TIMING("start-remote-send thread=%1", thread_id());
			video->frame()->write_to_socket(socket);
		}

		/* Read the response (JPEG2000-encoded data); this blocks until the data
		   is ready and sent back.
		*/
		Socket::ReadDigestScope ds (socket);
		LOG_TIMING("start-remote-encode thread=%1", thread_id());
		Data e (socket->read_uint32 ());
		LOG_TIMING("start-remote-receive thread=%1", thread_id());
		socket->read (e.data().get(), e.size());
		LOG_TIMING("finish-remote-receive thread=%1", thread_id());
		if (!ds.check()) {
			throw NetworkError ("Checksums do not match");
		}

		LOG_DEBUG_ENCODE (N_("Finished remotely-encoded frame %1"), video->index());

		if (_backoff > 0) {
			LOG_GENERAL ("%1 was lost, but now she is found; removing backoff", _server.host_name());
		}

		/* This job succeeded, so remove any backoff */
		_backoff = 0;

		vector<Data> data;
		data.push_back(e);
		return data;

	} catch (std::exception& e) {
		if (_backoff < 60) {
			_backoff += 10;
		}
		LOG_ERROR (
			N_("Remote encode of %1 on %2 failed (%3); thread sleeping for %4s"),
			video->index(), _server.host_name(), e.what(), _backoff
			);

		boost::this_thread::sleep (boost::posix_time::seconds(_backoff));
		return vector<Data>();
	}
}