+Socket::Socket (int timeout)
+ : _deadline (_io_service)
+ , _socket (_io_service)
+ , _acceptor (0)
+ , _timeout (timeout)
+{
+ _deadline.expires_at (boost::posix_time::pos_infin);
+ check ();
+}
+
+Socket::~Socket ()
+{
+ delete _acceptor;
+}
+
+void
+Socket::check ()
+{
+ if (_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now ()) {
+ if (_acceptor) {
+ _acceptor->cancel ();
+ } else {
+ _socket.close ();
+ }
+ _deadline.expires_at (boost::posix_time::pos_infin);
+ }
+
+ _deadline.async_wait (boost::bind (&Socket::check, this));
+}
+
+/** Blocking connect.
+ * @param endpoint End-point to connect to.
+ */
+void
+Socket::connect (boost::asio::ip::tcp::endpoint endpoint)
+{
+ _deadline.expires_from_now (boost::posix_time::seconds (_timeout));
+ boost::system::error_code ec = boost::asio::error::would_block;
+ _socket.async_connect (endpoint, boost::lambda::var(ec) = boost::lambda::_1);
+ do {
+ _io_service.run_one();
+ } while (ec == boost::asio::error::would_block);
+
+ if (ec) {
+ throw NetworkError (String::compose (_("error during async_connect (%1)"), ec.value ()));
+ }
+
+ if (!_socket.is_open ()) {
+ throw NetworkError (_("connect timed out"));
+ }
+}
+
+void
+Socket::accept (int port)
+{
+ _acceptor = new boost::asio::ip::tcp::acceptor (_io_service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port));
+
+ _deadline.expires_from_now (boost::posix_time::seconds (_timeout));
+ boost::system::error_code ec = boost::asio::error::would_block;
+ _acceptor->async_accept (_socket, boost::lambda::var(ec) = boost::lambda::_1);
+ do {
+ _io_service.run_one ();
+ } while (ec == boost::asio::error::would_block );
+
+ delete _acceptor;
+ _acceptor = 0;
+
+ if (ec) {
+ throw NetworkError (String::compose (_("error during async_accept (%1)"), ec.value ()));
+ }
+}
+
+/** Blocking write.
+ * @param data Buffer to write.
+ * @param size Number of bytes to write.
+ */
+void
+Socket::write (uint8_t const * data, int size)
+{
+ _deadline.expires_from_now (boost::posix_time::seconds (_timeout));
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ boost::asio::async_write (_socket, boost::asio::buffer (data, size), boost::lambda::var(ec) = boost::lambda::_1);
+
+ do {
+ _io_service.run_one ();
+ } while (ec == boost::asio::error::would_block);
+
+ if (ec) {
+ throw NetworkError (String::compose (_("error during async_write (%1)"), ec.value ()));
+ }
+}
+
+void
+Socket::write (uint32_t v)
+{
+ v = htonl (v);
+ write (reinterpret_cast<uint8_t*> (&v), 4);
+}
+
+/** Blocking read.
+ * @param data Buffer to read to.
+ * @param size Number of bytes to read.
+ */
+void
+Socket::read (uint8_t* data, int size)
+{
+ _deadline.expires_from_now (boost::posix_time::seconds (_timeout));
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ boost::asio::async_read (_socket, boost::asio::buffer (data, size), boost::lambda::var(ec) = boost::lambda::_1);
+
+ do {
+ _io_service.run_one ();
+ } while (ec == boost::asio::error::would_block);
+
+ if (ec) {
+ throw NetworkError (String::compose (_("error during async_read (%1)"), ec.value ()));
+ }
+}
+
+uint32_t
+Socket::read_uint32 ()
+{
+ uint32_t v;
+ read (reinterpret_cast<uint8_t *> (&v), 4);
+ return ntohl (v);
+}
+
+/** Round a number up to the nearest multiple of another number.
+ * @param c Index.
+ * @param s Array of numbers to round, indexed by c.
+ * @param t Multiple to round to.
+ * @return Rounded number.
+ */
+int
+stride_round_up (int c, int const * stride, int t)
+{
+ int const a = stride[c] + (t - 1);
+ return a - (a % t);
+}
+
+/** Read a sequence of key / value pairs from a text stream;
+ * the keys are the first words on the line, and the values are
+ * the remainder of the line following the key. Lines beginning
+ * with # are ignored.
+ * @param s Stream to read.
+ * @return key/value pairs.
+ */
+multimap<string, string>
+read_key_value (istream &s)
+{
+ multimap<string, string> kv;
+
+ string line;
+ while (getline (s, line)) {
+ if (line.empty ()) {
+ continue;
+ }
+
+ if (line[0] == '#') {
+ 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;
+ }
+
+ kv.insert (make_pair (line.substr (0, s), line.substr (s + 1)));
+ }
+
+ return kv;
+}
+
+string
+get_required_string (multimap<string, string> const & kv, string k)
+{
+ if (kv.count (k) > 1) {
+ throw StringError (N_("unexpected multiple keys in key-value set"));
+ }
+
+ multimap<string, string>::const_iterator i = kv.find (k);
+
+ if (i == kv.end ()) {
+ throw StringError (String::compose (_("missing key %1 in key-value set"), k));
+ }
+
+ return i->second;
+}
+
+int
+get_required_int (multimap<string, string> const & kv, string k)
+{
+ string const v = get_required_string (kv, k);
+ return lexical_cast<int> (v);
+}
+
+float
+get_required_float (multimap<string, string> const & kv, string k)
+{
+ string const v = get_required_string (kv, k);
+ return lexical_cast<float> (v);
+}
+
+string
+get_optional_string (multimap<string, string> const & kv, string k)
+{
+ if (kv.count (k) > 1) {
+ throw StringError (N_("unexpected multiple keys in key-value set"));
+ }
+
+ multimap<string, string>::const_iterator i = kv.find (k);
+ if (i == kv.end ()) {
+ return N_("");
+ }
+
+ return i->second;
+}
+
+int
+get_optional_int (multimap<string, string> const & kv, string k)
+{
+ if (kv.count (k) > 1) {
+ throw StringError (N_("unexpected multiple keys in key-value set"));
+ }
+
+ multimap<string, string>::const_iterator i = kv.find (k);
+ if (i == kv.end ()) {
+ return 0;
+ }
+
+ return lexical_cast<int> (i->second);
+}
+
+/** Trip an assert if the caller is not in the UI thread */
+void
+ensure_ui_thread ()
+{
+ assert (boost::this_thread::get_id() == ui_thread);
+}
+
+/** @param v Content video frame.
+ * @param audio_sample_rate Source audio sample rate.
+ * @param frames_per_second Number of video frames per second.
+ * @return Equivalent number of audio frames for `v'.
+ */
+int64_t
+video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second)
+{
+ return ((int64_t) v * audio_sample_rate / frames_per_second);
+}
+
+string
+audio_channel_name (int c)