summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2014-02-24 12:19:50 +0000
committerCarl Hetherington <cth@carlh.net>2014-02-24 12:19:50 +0000
commit85c65bd422742813992686c17a5e1b718cc3c449 (patch)
tree21750399bcb19e1fb6242bba7595773513a80912 /src/lib
parente2be8234013335379bd49a53854218039348c7a4 (diff)
parenteed40e4e5ca46bbc31a9833d2b766c96c11b0254 (diff)
Merge master; specify libdcp-1.0.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/analyse_audio_job.cc6
-rw-r--r--src/lib/analyse_audio_job.h1
-rw-r--r--src/lib/content.cc2
-rw-r--r--src/lib/content.h3
-rw-r--r--src/lib/examine_content_job.cc6
-rw-r--r--src/lib/examine_content_job.h1
-rw-r--r--src/lib/ffmpeg_content.cc71
-rw-r--r--src/lib/ffmpeg_content.h33
-rw-r--r--src/lib/ffmpeg_decoder.cc12
-rw-r--r--src/lib/ffmpeg_examiner.cc10
-rw-r--r--src/lib/ffmpeg_examiner.h2
-rw-r--r--src/lib/film.cc46
-rw-r--r--src/lib/film.h11
-rw-r--r--src/lib/job.cc25
-rw-r--r--src/lib/job.h6
-rw-r--r--src/lib/json_server.cc193
-rw-r--r--src/lib/json_server.h31
-rw-r--r--src/lib/scp_dcp_job.cc7
-rw-r--r--src/lib/scp_dcp_job.h1
-rw-r--r--src/lib/send_kdm_email_job.cc6
-rw-r--r--src/lib/send_kdm_email_job.h1
-rw-r--r--src/lib/transcode_job.cc6
-rw-r--r--src/lib/transcode_job.h1
-rw-r--r--src/lib/util.cc66
-rw-r--r--src/lib/util.h4
-rw-r--r--src/lib/wscript1
26 files changed, 447 insertions, 105 deletions
diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc
index 872947b55..a6926bc14 100644
--- a/src/lib/analyse_audio_job.cc
+++ b/src/lib/analyse_audio_job.cc
@@ -48,6 +48,12 @@ AnalyseAudioJob::name () const
return _("Analyse audio");
}
+string
+AnalyseAudioJob::json_name () const
+{
+ return N_("analyse_audio");
+}
+
void
AnalyseAudioJob::run ()
{
diff --git a/src/lib/analyse_audio_job.h b/src/lib/analyse_audio_job.h
index 6ed236d85..3d3900a51 100644
--- a/src/lib/analyse_audio_job.h
+++ b/src/lib/analyse_audio_job.h
@@ -30,6 +30,7 @@ public:
AnalyseAudioJob (boost::shared_ptr<const Film>, boost::shared_ptr<AudioContent>);
std::string name () const;
+ std::string json_name () const;
void run ();
private:
diff --git a/src/lib/content.cc b/src/lib/content.cc
index ea1c19acd..8e3b99da8 100644
--- a/src/lib/content.cc
+++ b/src/lib/content.cc
@@ -195,7 +195,7 @@ Content::clone () const
xmlpp::Document doc;
xmlpp::Node* node = doc.create_root_node ("Content");
as_xml (node);
- return content_factory (film, cxml::NodePtr (new cxml::Node (node)), Film::state_version);
+ return content_factory (film, cxml::NodePtr (new cxml::Node (node)), Film::current_state_version);
}
string
diff --git a/src/lib/content.h b/src/lib/content.h
index 3172b9c8d..78a41e306 100644
--- a/src/lib/content.h
+++ b/src/lib/content.h
@@ -57,6 +57,9 @@ public:
virtual void examine (boost::shared_ptr<Job>);
virtual std::string summary () const = 0;
+ /** @return Technical details of this content; these are written to logs to
+ * help with debugging.
+ */
virtual std::string technical_summary () const;
virtual std::string information () const = 0;
virtual void as_xml (xmlpp::Node *) const;
diff --git a/src/lib/examine_content_job.cc b/src/lib/examine_content_job.cc
index cbf180ffc..8f16e2e5c 100644
--- a/src/lib/examine_content_job.cc
+++ b/src/lib/examine_content_job.cc
@@ -46,6 +46,12 @@ ExamineContentJob::name () const
return _("Examine content");
}
+string
+ExamineContentJob::json_name () const
+{
+ return N_("examine_content");
+}
+
void
ExamineContentJob::run ()
{
diff --git a/src/lib/examine_content_job.h b/src/lib/examine_content_job.h
index b6903b86b..c8037224f 100644
--- a/src/lib/examine_content_job.h
+++ b/src/lib/examine_content_job.h
@@ -30,6 +30,7 @@ public:
~ExamineContentJob ();
std::string name () const;
+ std::string json_name () const;
void run ();
private:
diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc
index 3bee49146..3df1ba57e 100644
--- a/src/lib/ffmpeg_content.cc
+++ b/src/lib/ffmpeg_content.cc
@@ -66,7 +66,7 @@ FFmpegContent::FFmpegContent (shared_ptr<const Film> f, shared_ptr<const cxml::N
{
list<cxml::NodePtr> c = node->node_children ("SubtitleStream");
for (list<cxml::NodePtr>::const_iterator i = c.begin(); i != c.end(); ++i) {
- _subtitle_streams.push_back (shared_ptr<FFmpegSubtitleStream> (new FFmpegSubtitleStream (*i, version)));
+ _subtitle_streams.push_back (shared_ptr<FFmpegSubtitleStream> (new FFmpegSubtitleStream (*i)));
if ((*i)->optional_number_child<int> ("Selected")) {
_subtitle_stream = _subtitle_streams.back ();
}
@@ -207,12 +207,12 @@ FFmpegContent::technical_summary () const
{
string as = "none";
if (_audio_stream) {
- as = String::compose ("id %1", _audio_stream->id);
+ as = _audio_stream->technical_summary ();
}
string ss = "none";
if (_subtitle_stream) {
- ss = String::compose ("id %1", _subtitle_stream->id);
+ ss = _subtitle_stream->technical_summary ();
}
pair<string, string> filt = Filter::ffmpeg_strings (_filters);
@@ -325,54 +325,33 @@ FFmpegContent::output_audio_frame_rate () const
}
bool
-operator== (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b)
+operator== (FFmpegStream const & a, FFmpegStream const & b)
{
- return a.id == b.id;
+ return a._id == b._id;
}
bool
-operator!= (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b)
+operator!= (FFmpegStream const & a, FFmpegStream const & b)
{
- return a.id != b.id;
+ return a._id != b._id;
}
-bool
-operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b)
-{
- return a.id == b.id;
-}
-
-bool
-operator!= (FFmpegAudioStream const & a, FFmpegAudioStream const & b)
+FFmpegStream::FFmpegStream (shared_ptr<const cxml::Node> node)
+ : name (node->string_child ("Name"))
+ , _id (node->number_child<int> ("Id"))
{
- return a.id != b.id;
-}
-FFmpegStream::FFmpegStream (shared_ptr<const cxml::Node> node, int version)
- : _legacy_id (false)
-{
- name = node->string_child ("Name");
- id = node->number_child<int> ("Id");
- if (version == 4 || node->optional_bool_child ("LegacyId")) {
- _legacy_id = true;
- }
}
void
FFmpegStream::as_xml (xmlpp::Node* root) const
{
root->add_child("Name")->add_child_text (name);
- root->add_child("Id")->add_child_text (lexical_cast<string> (id));
- if (_legacy_id) {
- /* Write this so that version > 4 files are read in correctly
- if the Id came originally from a version <= 4 file.
- */
- root->add_child("LegacyId")->add_child_text ("1");
- }
+ root->add_child("Id")->add_child_text (lexical_cast<string> (_id));
}
FFmpegAudioStream::FFmpegAudioStream (shared_ptr<const cxml::Node> node, int version)
- : FFmpegStream (node, version)
+ : FFmpegStream (node)
, mapping (node->node_child ("Mapping"), version)
{
frame_rate = node->number_child<int> ("FrameRate");
@@ -392,34 +371,26 @@ FFmpegAudioStream::as_xml (xmlpp::Node* root) const
mapping.as_xml (root->add_child("Mapping"));
}
-int
-FFmpegStream::index (AVFormatContext const * fc) const
+bool
+FFmpegStream::uses_index (AVFormatContext const * fc, int index) const
{
- if (_legacy_id) {
- return id;
- }
-
size_t i = 0;
while (i < fc->nb_streams) {
- if (fc->streams[i]->id == id) {
- return i;
+ if (fc->streams[i]->id == _id) {
+ return int (i) == index;
}
++i;
}
- assert (false);
+ return false;
}
AVStream *
FFmpegStream::stream (AVFormatContext const * fc) const
{
- if (_legacy_id) {
- return fc->streams[id];
- }
-
size_t i = 0;
while (i < fc->nb_streams) {
- if (fc->streams[i]->id == id) {
+ if (fc->streams[i]->id == _id) {
return fc->streams[i];
}
++i;
@@ -433,8 +404,8 @@ FFmpegStream::stream (AVFormatContext const * fc) const
* @param t String returned from to_string().
* @param v State file version.
*/
-FFmpegSubtitleStream::FFmpegSubtitleStream (shared_ptr<const cxml::Node> node, int version)
- : FFmpegStream (node, version)
+FFmpegSubtitleStream::FFmpegSubtitleStream (shared_ptr<const cxml::Node> node)
+ : FFmpegStream (node)
{
}
@@ -495,7 +466,7 @@ FFmpegContent::identifier () const
boost::mutex::scoped_lock lm (_mutex);
if (_subtitle_stream) {
- s << "_" << _subtitle_stream->id;
+ s << "_" << _subtitle_stream->identifier ();
}
for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h
index e637faf47..d588fd2ee 100644
--- a/src/lib/ffmpeg_content.h
+++ b/src/lib/ffmpeg_content.h
@@ -37,26 +37,35 @@ class FFmpegStream
public:
FFmpegStream (std::string n, int i)
: name (n)
- , id (i)
- , _legacy_id (false)
+ , _id (i)
{}
- FFmpegStream (boost::shared_ptr<const cxml::Node>, int);
+ FFmpegStream (boost::shared_ptr<const cxml::Node>);
void as_xml (xmlpp::Node *) const;
/** @param c An AVFormatContext.
- * @return Stream index within the AVFormatContext.
+ * @param index A stream index within the AVFormatContext.
+ * @return true if this FFmpegStream uses the given stream index.
*/
- int index (AVFormatContext const * c) const;
+ bool uses_index (AVFormatContext const * c, int index) const;
AVStream* stream (AVFormatContext const * c) const;
+ std::string technical_summary () const {
+ return "id " + boost::lexical_cast<std::string> (_id);
+ }
+
+ std::string identifier () const {
+ return boost::lexical_cast<std::string> (_id);
+ }
+
std::string name;
- int id;
+
+ friend bool operator== (FFmpegStream const & a, FFmpegStream const & b);
+ friend bool operator!= (FFmpegStream const & a, FFmpegStream const & b);
private:
- /** If this is true, id is in fact the index */
- bool _legacy_id;
+ int _id;
};
class FFmpegAudioStream : public FFmpegStream
@@ -92,9 +101,6 @@ private:
{}
};
-extern bool operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b);
-extern bool operator!= (FFmpegAudioStream const & a, FFmpegAudioStream const & b);
-
class FFmpegSubtitleStream : public FFmpegStream
{
public:
@@ -102,14 +108,11 @@ public:
: FFmpegStream (n, i)
{}
- FFmpegSubtitleStream (boost::shared_ptr<const cxml::Node>, int);
+ FFmpegSubtitleStream (boost::shared_ptr<const cxml::Node>);
void as_xml (xmlpp::Node *) const;
};
-extern bool operator== (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b);
-extern bool operator!= (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b);
-
class FFmpegContentProperty : public VideoContentProperty
{
public:
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index fff982489..26b713dd5 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -167,9 +167,9 @@ FFmpegDecoder::pass ()
if (si == _video_stream && _decode_video) {
decode_video_packet ();
- } else if (_ffmpeg_content->audio_stream() && si == _ffmpeg_content->audio_stream()->index (_format_context) && _decode_audio) {
+ } else if (_ffmpeg_content->audio_stream() && _ffmpeg_content->audio_stream()->uses_index (_format_context, si) && _decode_audio) {
decode_audio_packet ();
- } else if (_ffmpeg_content->subtitle_stream() && si == _ffmpeg_content->subtitle_stream()->index (_format_context) && film->with_subtitles ()) {
+ } else if (_ffmpeg_content->subtitle_stream() && _ffmpeg_content->subtitle_stream()->uses_index (_format_context, si) && film->with_subtitles ()) {
decode_subtitle_packet ();
}
@@ -521,15 +521,19 @@ FFmpegDecoder::setup_subtitle ()
{
boost::mutex::scoped_lock lm (_mutex);
- if (!_ffmpeg_content->subtitle_stream() || _ffmpeg_content->subtitle_stream()->index (_format_context) >= int (_format_context->nb_streams)) {
+ if (!_ffmpeg_content->subtitle_stream()) {
return;
}
_subtitle_codec_context = _ffmpeg_content->subtitle_stream()->stream(_format_context)->codec;
+ if (_subtitle_codec_context == 0) {
+ throw DecodeError (N_("could not find subtitle stream"));
+ }
+
_subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
if (_subtitle_codec == 0) {
- throw DecodeError (_("could not find subtitle decoder"));
+ throw DecodeError (N_("could not find subtitle decoder"));
}
if (avcodec_open2 (_subtitle_codec_context, _subtitle_codec, 0) < 0) {
diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc
index 86dec9a8f..e439566a1 100644
--- a/src/lib/ffmpeg_examiner.cc
+++ b/src/lib/ffmpeg_examiner.cc
@@ -75,13 +75,13 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c)
if (_packet.stream_index == _video_stream && !_first_video) {
if (avcodec_decode_video2 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
- _first_video = frame_time (_video_stream);
+ _first_video = frame_time (_format_context->streams[_video_stream]);
}
} else {
for (size_t i = 0; i < _audio_streams.size(); ++i) {
- if (_packet.stream_index == _audio_streams[i]->index (_format_context) && !_audio_streams[i]->first_audio) {
+ if (_audio_streams[i]->uses_index (_format_context, _packet.stream_index) && !_audio_streams[i]->first_audio) {
if (avcodec_decode_audio4 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
- _audio_streams[i]->first_audio = frame_time (_audio_streams[i]->index (_format_context));
+ _audio_streams[i]->first_audio = frame_time (_audio_streams[i]->stream (_format_context));
}
}
}
@@ -103,13 +103,13 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c)
}
optional<double>
-FFmpegExaminer::frame_time (int stream) const
+FFmpegExaminer::frame_time (AVStream* s) const
{
optional<double> t;
int64_t const bet = av_frame_get_best_effort_timestamp (_frame);
if (bet != AV_NOPTS_VALUE) {
- t = bet * av_q2d (_format_context->streams[stream]->time_base);
+ t = bet * av_q2d (s->time_base);
}
return t;
diff --git a/src/lib/ffmpeg_examiner.h b/src/lib/ffmpeg_examiner.h
index 40d7dbf1d..81275a9e1 100644
--- a/src/lib/ffmpeg_examiner.h
+++ b/src/lib/ffmpeg_examiner.h
@@ -49,7 +49,7 @@ private:
std::string stream_name (AVStream* s) const;
std::string audio_stream_name (AVStream* s) const;
std::string subtitle_stream_name (AVStream* s) const;
- boost::optional<double> frame_time (int) const;
+ boost::optional<double> frame_time (AVStream* s) const;
std::vector<boost::shared_ptr<FFmpegSubtitleStream> > _subtitle_streams;
std::vector<boost::shared_ptr<FFmpegAudioStream> > _audio_streams;
diff --git a/src/lib/film.cc b/src/lib/film.cc
index 774c1b392..901c51284 100644
--- a/src/lib/film.cc
+++ b/src/lib/film.cc
@@ -86,14 +86,14 @@ using dcp::Signer;
* 6 -> 7
* Subtitle offset changed to subtitle y offset, and subtitle x offset added.
*/
-int const Film::state_version = 7;
+int const Film::current_state_version = 7;
/** Construct a Film object in a given directory.
*
* @param dir Film directory.
*/
-Film::Film (boost::filesystem::path dir)
+Film::Film (boost::filesystem::path dir, bool log)
: _playlist (new Playlist)
, _use_dci_name (true)
, _dcp_content_type (Config::instance()->default_dcp_content_type ())
@@ -106,10 +106,11 @@ Film::Film (boost::filesystem::path dir)
, _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
, _dci_metadata (Config::instance()->default_dci_metadata ())
, _video_frame_rate (24)
- , _audio_channels (MAX_AUDIO_CHANNELS)
+ , _audio_channels (6)
, _three_d (false)
, _sequence_video (true)
, _interop (false)
+ , _state_version (current_state_version)
, _dirty (false)
{
set_dci_date_today ();
@@ -136,7 +137,11 @@ Film::Film (boost::filesystem::path dir)
}
set_directory (result);
- _log.reset (new FileLog (file ("log")));
+ if (log) {
+ _log.reset (new FileLog (file ("log")));
+ } else {
+ _log.reset (new NullLog);
+ }
_playlist->set_sequence_video (_sequence_video);
}
@@ -327,22 +332,15 @@ Film::encoded_frames () const
return N;
}
-/** Write state to our `metadata' file */
-void
-Film::write_metadata () const
+shared_ptr<xmlpp::Document>
+Film::metadata () const
{
- if (!boost::filesystem::exists (directory ())) {
- boost::filesystem::create_directory (directory ());
- }
-
LocaleGuard lg;
- boost::filesystem::create_directories (directory ());
-
- xmlpp::Document doc;
- xmlpp::Element* root = doc.create_root_node ("Metadata");
+ shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
+ xmlpp::Element* root = doc->create_root_node ("Metadata");
- root->add_child("Version")->add_child_text (lexical_cast<string> (state_version));
+ root->add_child("Version")->add_child_text (lexical_cast<string> (current_state_version));
root->add_child("Name")->add_child_text (_name);
root->add_child("UseDCIName")->add_child_text (_use_dci_name ? "1" : "0");
@@ -370,8 +368,16 @@ Film::write_metadata () const
root->add_child("Key")->add_child_text (_key.hex ());
_playlist->as_xml (root->add_child ("Playlist"));
- doc.write_to_file_formatted (file("metadata.xml").string ());
-
+ return doc;
+}
+
+/** Write state to our `metadata' file */
+void
+Film::write_metadata () const
+{
+ boost::filesystem::create_directories (directory ());
+ shared_ptr<xmlpp::Document> doc = metadata ();
+ doc->write_to_file_formatted (file("metadata.xml").string ());
_dirty = false;
}
@@ -388,7 +394,7 @@ Film::read_metadata ()
cxml::Document f ("Metadata");
f.read_file (file ("metadata.xml"));
- int const version = f.number_child<int> ("Version");
+ _state_version = f.number_child<int> ("Version");
_name = f.string_child ("Name");
_use_dci_name = f.bool_child ("UseDCIName");
@@ -421,7 +427,7 @@ Film::read_metadata ()
_three_d = f.bool_child ("ThreeD");
_interop = f.bool_child ("Interop");
_key = dcp::Key (f.string_child ("Key"));
- _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), version);
+ _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version);
_dirty = false;
}
diff --git a/src/lib/film.h b/src/lib/film.h
index 68916c7b0..0a474bab7 100644
--- a/src/lib/film.h
+++ b/src/lib/film.h
@@ -56,7 +56,7 @@ class Screen;
class Film : public boost::enable_shared_from_this<Film>, public boost::noncopyable
{
public:
- Film (boost::filesystem::path);
+ Film (boost::filesystem::path, bool log = true);
boost::filesystem::path info_dir () const;
boost::filesystem::path j2c_path (int, Eyes, bool) const;
@@ -85,6 +85,7 @@ public:
void read_metadata ();
void write_metadata () const;
+ boost::shared_ptr<xmlpp::Document> metadata () const;
std::string dci_name (bool if_created_now) const;
std::string dcp_name (bool if_created_now = false) const;
@@ -138,6 +139,10 @@ public:
return _key;
}
+ int state_version () const {
+ return _state_version;
+ }
+
/** Identifiers for the parts of our state;
used for signalling changes.
*/
@@ -271,7 +276,7 @@ public:
mutable boost::signals2::signal<void (boost::weak_ptr<Content>, int)> ContentChanged;
/** Current version number of the state file */
- static int const state_version;
+ static int const current_state_version;
private:
@@ -325,6 +330,8 @@ private:
bool _interop;
dcp::Key _key;
+ int _state_version;
+
/** true if our state has changed since we last saved it */
mutable bool _dirty;
diff --git a/src/lib/job.cc b/src/lib/job.cc
index ce97ba2b2..a312e7381 100644
--- a/src/lib/job.cc
+++ b/src/lib/job.cc
@@ -239,7 +239,7 @@ Job::set_progress (float p, bool force)
}
}
-/** @return fractional progress of this sub-job, or -1 if not known */
+/** @return fractional progress of the current sub-job, or -1 if not known */
float
Job::progress () const
{
@@ -325,6 +325,29 @@ Job::status () const
return s.str ();
}
+string
+Job::json_status () const
+{
+ boost::mutex::scoped_lock lm (_state_mutex);
+
+ switch (_state) {
+ case NEW:
+ return N_("new");
+ case RUNNING:
+ return N_("running");
+ case PAUSED:
+ return N_("paused");
+ case FINISHED_OK:
+ return N_("finished_ok");
+ case FINISHED_ERROR:
+ return N_("finished_error");
+ case FINISHED_CANCELLED:
+ return N_("finished_cancelled");
+ }
+
+ return "";
+}
+
/** @return An estimate of the remaining time for this sub-job, in seconds */
int
Job::remaining_time () const
diff --git a/src/lib/job.h b/src/lib/job.h
index 6310da32a..5e3127dc1 100644
--- a/src/lib/job.h
+++ b/src/lib/job.h
@@ -43,6 +43,7 @@ public:
/** @return user-readable name of this job */
virtual std::string name () const = 0;
+ virtual std::string json_name () const = 0;
/** Run this job in the current thread. */
virtual void run () = 0;
@@ -64,6 +65,7 @@ public:
int elapsed_time () const;
virtual std::string status () const;
+ std::string json_status () const;
std::string sub_name () const {
return _sub_name;
}
@@ -76,6 +78,10 @@ public:
return !_progress;
}
+ boost::shared_ptr<const Film> film () const {
+ return _film;
+ }
+
boost::signals2::signal<void()> Progress;
/** Emitted from the UI thread when the job is finished */
boost::signals2::signal<void()> Finished;
diff --git a/src/lib/json_server.cc b/src/lib/json_server.cc
new file mode 100644
index 000000000..f4aa292d7
--- /dev/null
+++ b/src/lib/json_server.cc
@@ -0,0 +1,193 @@
+/*
+ Copyright (C) 2014 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 <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+#include "json_server.h"
+#include "job_manager.h"
+#include "job.h"
+#include "util.h"
+#include "film.h"
+#include "transcode_job.h"
+
+using std::string;
+using std::stringstream;
+using std::cout;
+using std::map;
+using std::list;
+using boost::thread;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+using boost::asio::ip::tcp;
+
+#define MAX_LENGTH 512
+
+enum State {
+ AWAITING_G,
+ AWAITING_E,
+ AWAITING_T,
+ AWAITING_SPACE,
+ READING_URL,
+};
+
+JSONServer::JSONServer (int port)
+{
+ new thread (boost::bind (&JSONServer::run, this, port));
+}
+
+void
+JSONServer::run (int port)
+try
+{
+ boost::asio::io_service io_service;
+ tcp::acceptor a (io_service, tcp::endpoint (tcp::v4 (), port));
+ while (1) {
+ try {
+ shared_ptr<tcp::socket> s (new tcp::socket (io_service));
+ a.accept (*s);
+ handle (s);
+ }
+ catch (...) {
+
+ }
+ }
+}
+catch (...)
+{
+
+}
+
+void
+JSONServer::handle (shared_ptr<tcp::socket> socket)
+{
+ string url;
+ State state = AWAITING_G;
+
+ while (1) {
+ char data[MAX_LENGTH];
+ boost::system::error_code error;
+ size_t len = socket->read_some (boost::asio::buffer (data), error);
+ if (error) {
+ cout << "error.\n";
+ break;
+ }
+
+ char* p = data;
+ char* e = data + len;
+ while (p != e) {
+
+ State old_state = state;
+ switch (state) {
+ case AWAITING_G:
+ if (*p == 'G') {
+ state = AWAITING_E;
+ }
+ break;
+ case AWAITING_E:
+ if (*p == 'E') {
+ state = AWAITING_T;
+ }
+ break;
+ case AWAITING_T:
+ if (*p == 'T') {
+ state = AWAITING_SPACE;
+ }
+ break;
+ case AWAITING_SPACE:
+ if (*p == ' ') {
+ state = READING_URL;
+ }
+ break;
+ case READING_URL:
+ if (*p == ' ') {
+ request (url, socket);
+ state = AWAITING_G;
+ url = "";
+ } else {
+ url += *p;
+ }
+ break;
+ }
+
+ if (state == old_state && state != READING_URL) {
+ state = AWAITING_G;
+ }
+
+ ++p;
+ }
+ }
+}
+
+void
+JSONServer::request (string url, shared_ptr<tcp::socket> socket)
+{
+ cout << "request: " << url << "\n";
+
+ map<string, string> r = split_get_request (url);
+ for (map<string, string>::iterator i = r.begin(); i != r.end(); ++i) {
+ cout << i->first << " => " << i->second << "\n";
+ }
+
+ string action;
+ if (r.find ("action") != r.end ()) {
+ action = r["action"];
+ }
+
+ stringstream json;
+ if (action == "status") {
+
+ list<shared_ptr<Job> > jobs = JobManager::instance()->get ();
+
+ json << "{ \"jobs\": [";
+ for (list<shared_ptr<Job> >::iterator i = jobs.begin(); i != jobs.end(); ++i) {
+
+ json << "{ ";
+
+ if ((*i)->film()) {
+ json << "\"dcp\": \"" << (*i)->film()->dcp_name() << "\", ";
+ }
+
+ json << "\"name\": \"" << (*i)->json_name() << "\", "
+ << "\"progress\": " << (*i)->progress () << ", "
+ << "\"status\": \"" << (*i)->json_status() << "\"";
+ json << " }";
+
+ list<shared_ptr<Job> >::iterator j = i;
+ ++j;
+ if (j != jobs.end ()) {
+ json << ", ";
+ }
+ }
+ json << "] }";
+
+ if (json.str().empty ()) {
+ json << "{ }";
+ }
+ }
+
+ stringstream reply;
+ reply << "HTTP/1.1 200 OK\r\n"
+ << "Content-Length: " << json.str().length() << "\r\n"
+ << "Content-Type: application/json\r\n"
+ << "\r\n"
+ << json.str () << "\r\n";
+ cout << "reply: " << json.str() << "\n";
+ boost::asio::write (*socket, boost::asio::buffer (reply.str().c_str(), reply.str().length()));
+}
diff --git a/src/lib/json_server.h b/src/lib/json_server.h
new file mode 100644
index 000000000..623067558
--- /dev/null
+++ b/src/lib/json_server.h
@@ -0,0 +1,31 @@
+/*
+ Copyright (C) 2014 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.
+
+*/
+
+class JSONServer
+{
+public:
+ JSONServer (int port);
+
+private:
+ void run (int port);
+ void handle (boost::shared_ptr<boost::asio::ip::tcp::socket> socket);
+ void request (std::string url, boost::shared_ptr<boost::asio::ip::tcp::socket> socket);
+};
+
+
diff --git a/src/lib/scp_dcp_job.cc b/src/lib/scp_dcp_job.cc
index 22715978a..0b713b042 100644
--- a/src/lib/scp_dcp_job.cc
+++ b/src/lib/scp_dcp_job.cc
@@ -110,6 +110,12 @@ SCPDCPJob::name () const
return _("Copy DCP to TMS");
}
+string
+SCPDCPJob::json_name () const
+{
+ return N_("scp_dcp");
+}
+
void
SCPDCPJob::run ()
{
@@ -223,4 +229,3 @@ SCPDCPJob::set_status (string s)
boost::mutex::scoped_lock lm (_status_mutex);
_status = s;
}
-
diff --git a/src/lib/scp_dcp_job.h b/src/lib/scp_dcp_job.h
index bdc83af18..e3960d73b 100644
--- a/src/lib/scp_dcp_job.h
+++ b/src/lib/scp_dcp_job.h
@@ -29,6 +29,7 @@ public:
SCPDCPJob (boost::shared_ptr<const Film>);
std::string name () const;
+ std::string json_name () const;
void run ();
std::string status () const;
diff --git a/src/lib/send_kdm_email_job.cc b/src/lib/send_kdm_email_job.cc
index 89bce9a14..8af0b556a 100644
--- a/src/lib/send_kdm_email_job.cc
+++ b/src/lib/send_kdm_email_job.cc
@@ -50,6 +50,12 @@ SendKDMEmailJob::name () const
return String::compose (_("Email KDMs for %1"), _film->name());
}
+string
+SendKDMEmailJob::json_name () const
+{
+ return N_("send_kdm_email");
+}
+
void
SendKDMEmailJob::run ()
{
diff --git a/src/lib/send_kdm_email_job.h b/src/lib/send_kdm_email_job.h
index fcab56ce5..f4d154a91 100644
--- a/src/lib/send_kdm_email_job.h
+++ b/src/lib/send_kdm_email_job.h
@@ -34,6 +34,7 @@ public:
);
std::string name () const;
+ std::string json_name () const;
void run ();
private:
diff --git a/src/lib/transcode_job.cc b/src/lib/transcode_job.cc
index 46fc97fb3..a0537cd42 100644
--- a/src/lib/transcode_job.cc
+++ b/src/lib/transcode_job.cc
@@ -50,6 +50,12 @@ TranscodeJob::name () const
return String::compose (_("Transcode %1"), _film->name());
}
+string
+TranscodeJob::json_name () const
+{
+ return N_("transcode");
+}
+
void
TranscodeJob::run ()
{
diff --git a/src/lib/transcode_job.h b/src/lib/transcode_job.h
index 9128206d2..6e3c1ead9 100644
--- a/src/lib/transcode_job.h
+++ b/src/lib/transcode_job.h
@@ -35,6 +35,7 @@ public:
TranscodeJob (boost::shared_ptr<const Film> f);
std::string name () const;
+ std::string json_name () const;
void run ();
std::string status () const;
diff --git a/src/lib/util.cc b/src/lib/util.cc
index fd3a318b0..63b1a5395 100644
--- a/src/lib/util.cc
+++ b/src/lib/util.cc
@@ -90,6 +90,7 @@ using std::min;
using std::max;
using std::list;
using std::multimap;
+using std::map;
using std::istream;
using std::numeric_limits;
using std::pair;
@@ -791,10 +792,11 @@ video_frames_to_audio_frames (VideoFrame v, float audio_sample_rate, float frame
string
audio_channel_name (int c)
{
- assert (MAX_AUDIO_CHANNELS == 6);
+ assert (MAX_AUDIO_CHANNELS == 8);
/* TRANSLATORS: these are the names of audio channels; Lfe (sub) is the low-frequency
- enhancement channel (sub-woofer).
+ enhancement channel (sub-woofer). HI is the hearing-impaired audio track and
+ VI is the visually-impaired audio track (audio describe).
*/
string const channels[] = {
_("Left"),
@@ -803,6 +805,8 @@ audio_channel_name (int c)
_("Lfe (sub)"),
_("Left surround"),
_("Right surround"),
+ _("HI"),
+ _("VI")
};
return channels[c];
@@ -942,8 +946,54 @@ make_signer ()
return shared_ptr<const dcp::Signer> (new dcp::Signer (chain, signer_key));
}
-dcp::Size
-fit_ratio_within (float ratio, dcp::Size full_frame)
+map<string, string>
+split_get_request (string url)
+{
+ enum {
+ AWAITING_QUESTION_MARK,
+ KEY,
+ VALUE
+ } state = AWAITING_QUESTION_MARK;
+
+ map<string, string> r;
+ string k;
+ string v;
+ for (size_t i = 0; i < url.length(); ++i) {
+ switch (state) {
+ case AWAITING_QUESTION_MARK:
+ if (url[i] == '?') {
+ state = KEY;
+ }
+ break;
+ case KEY:
+ if (url[i] == '=') {
+ v.clear ();
+ state = VALUE;
+ } else {
+ k += url[i];
+ }
+ break;
+ case VALUE:
+ if (url[i] == '&') {
+ r.insert (make_pair (k, v));
+ k.clear ();
+ state = KEY;
+ } else {
+ v += url[i];
+ }
+ break;
+ }
+ }
+
+ if (state == VALUE) {
+ r.insert (make_pair (k, v));
+ }
+
+ return r;
+}
+
+libdcp::Size
+fit_ratio_within (float ratio, libdcp::Size full_frame)
{
if (ratio < full_frame.ratio ()) {
return dcp::Size (rint (full_frame.height * ratio), full_frame.height);
@@ -968,3 +1018,11 @@ wrapped_av_malloc (size_t s)
}
return p;
}
+
+string
+entities_to_text (string e)
+{
+ boost::algorithm::replace_all (e, "%3A", ":");
+ boost::algorithm::replace_all (e, "%2F", "/");
+ return e;
+}
diff --git a/src/lib/util.h b/src/lib/util.h
index b89c71eee..76dbda190 100644
--- a/src/lib/util.h
+++ b/src/lib/util.h
@@ -50,7 +50,7 @@ extern "C" {
#undef check
/** The maximum number of audio channels that we can cope with */
-#define MAX_AUDIO_CHANNELS 6
+#define MAX_AUDIO_CHANNELS 8
#define DCPOMATIC_HELLO "Boys, you gotta learn not to talk to nuns that way"
@@ -79,6 +79,8 @@ extern boost::filesystem::path mo_path ();
extern std::string tidy_for_filename (std::string);
extern boost::shared_ptr<const dcp::Signer> make_signer ();
extern dcp::Size fit_ratio_within (float ratio, dcp::Size);
+extern std::string entities_to_text (std::string e);
+extern std::map<std::string, std::string> split_get_request (std::string url);
struct FrameRateChange
{
diff --git a/src/lib/wscript b/src/lib/wscript
index a5b069184..688f4047d 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -37,6 +37,7 @@ sources = """
job.cc
job_manager.cc
kdm.cc
+ json_server.cc
log.cc
player.cc
playlist.cc