print >>f,''
def dependencies(target):
- return (('ffmpeg-cdist', '5ac3a6af077c10f07c31954c372a8f29e4e18e2a'),
+ return (('ffmpeg-cdist', '08827fa4e1d483511e6135c424d2ca9c56a9ed50'),
- ('libdcp', '5839998'))
+ ('libdcp', '1.0'))
def build(target, options):
cmd = './waf configure --prefix=%s' % target.work_dir_cscript()
return _("Analyse audio");
}
+ string
+ AnalyseAudioJob::json_name () const
+ {
+ return N_("analyse_audio");
+ }
+
void
AnalyseAudioJob::run ()
{
_analysis.reset (new AudioAnalysis (_film->audio_channels ()));
_done = 0;
- OutputAudioFrame const len = _film->time_to_audio_frames (_film->length ());
+ AudioFrame const len = _film->time_to_audio_frames (_film->length ());
while (!player->pass ()) {
set_progress (double (_done) / len);
}
}
void
-AnalyseAudioJob::audio (shared_ptr<const AudioBuffers> b, Time)
+AnalyseAudioJob::audio (shared_ptr<const AudioBuffers> b, DCPTime)
{
for (int i = 0; i < b->frames(); ++i) {
for (int j = 0; j < b->channels(); ++j) {
AnalyseAudioJob (boost::shared_ptr<const Film>, boost::shared_ptr<AudioContent>);
std::string name () const;
+ std::string json_name () const;
void run ();
private:
- void audio (boost::shared_ptr<const AudioBuffers>, Time);
+ void audio (boost::shared_ptr<const AudioBuffers>, DCPTime);
boost::weak_ptr<AudioContent> _content;
- OutputAudioFrame _done;
+ AudioFrame _done;
int64_t _samples_per_point;
std::vector<AudioPoint> _current;
}
-Content::Content (shared_ptr<const Film> f, Time p)
+Content::Content (shared_ptr<const Film> f, DCPTime p)
: _film (f)
, _position (p)
, _trim_start (0)
_paths.push_back ((*i)->content ());
}
_digest = node->string_child ("Digest");
- _position = node->number_child<Time> ("Position");
- _trim_start = node->number_child<Time> ("TrimStart");
- _trim_end = node->number_child<Time> ("TrimEnd");
+ _position = node->number_child<DCPTime> ("Position");
+ _trim_start = node->number_child<DCPTime> ("TrimStart");
+ _trim_end = node->number_child<DCPTime> ("TrimEnd");
}
Content::Content (shared_ptr<const Film> f, vector<shared_ptr<Content> > c)
}
void
-Content::set_position (Time p)
+Content::set_position (DCPTime p)
{
{
boost::mutex::scoped_lock lm (_mutex);
}
void
-Content::set_trim_start (Time t)
+Content::set_trim_start (DCPTime t)
{
{
boost::mutex::scoped_lock lm (_mutex);
}
void
-Content::set_trim_end (Time t)
+Content::set_trim_end (DCPTime t)
{
{
boost::mutex::scoped_lock lm (_mutex);
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
return String::compose ("%1 %2 %3", path_summary(), digest(), position());
}
-Time
+DCPTime
Content::length_after_trim () const
{
return full_length() - trim_start() - trim_end();
}
-/** @param t A time relative to the start of this content (not the position).
- * @return true if this time is trimmed by our trim settings.
- */
-bool
-Content::trimmed (Time t) const
-{
- return (t < trim_start() || t > (full_length() - trim_end ()));
-}
-
/** @return string which includes everything about how this content affects
* its playlist.
*/
{
public:
Content (boost::shared_ptr<const Film>);
- Content (boost::shared_ptr<const Film>, Time);
+ Content (boost::shared_ptr<const Film>, DCPTime);
Content (boost::shared_ptr<const Film>, boost::filesystem::path);
Content (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
Content (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
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;
- virtual Time full_length () const = 0;
+ virtual DCPTime full_length () const = 0;
virtual std::string identifier () const;
boost::shared_ptr<Content> clone () const;
return _digest;
}
- void set_position (Time);
+ void set_position (DCPTime);
- /** Time that this content starts; i.e. the time that the first
+ /** DCPTime that this content starts; i.e. the time that the first
* bit of the content (trimmed or not) will happen.
*/
- Time position () const {
+ DCPTime position () const {
boost::mutex::scoped_lock lm (_mutex);
return _position;
}
- void set_trim_start (Time);
+ void set_trim_start (DCPTime);
- Time trim_start () const {
+ DCPTime trim_start () const {
boost::mutex::scoped_lock lm (_mutex);
return _trim_start;
}
- void set_trim_end (Time);
+ void set_trim_end (DCPTime);
- Time trim_end () const {
+ DCPTime trim_end () const {
boost::mutex::scoped_lock lm (_mutex);
return _trim_end;
}
- Time end () const {
+ DCPTime end () const {
return position() + length_after_trim() - 1;
}
- Time length_after_trim () const;
+ DCPTime length_after_trim () const;
void set_change_signals_frequent (bool f) {
_change_signals_frequent = f;
}
- bool trimmed (Time) const;
-
boost::signals2::signal<void (boost::weak_ptr<Content>, int, bool)> Changed;
protected:
private:
std::string _digest;
- Time _position;
- Time _trim_start;
- Time _trim_end;
+ DCPTime _position;
+ DCPTime _trim_start;
+ DCPTime _trim_end;
bool _change_signals_frequent;
};
{
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 ();
}
shared_ptr<FFmpegExaminer> examiner (new FFmpegExaminer (shared_from_this ()));
- VideoContent::Frame video_length = 0;
+ VideoFrame video_length = 0;
video_length = examiner->video_length ();
film->log()->log (String::compose ("Video length obtained from header as %1 frames", video_length));
{
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);
signal_changed (FFmpegContentProperty::AUDIO_STREAM);
}
-AudioContent::Frame
+AudioFrame
FFmpegContent::audio_length () const
{
int const cafr = content_audio_frame_rate ();
int const vfr = video_frame_rate ();
- VideoContent::Frame const vl = video_length ();
+ VideoFrame const vl = video_length ();
boost::mutex::scoped_lock lm (_mutex);
if (!_audio_stream) {
/* Resample to a DCI-approved sample rate */
double t = dcp_audio_frame_rate (content_audio_frame_rate ());
- FrameRateConversion frc (video_frame_rate(), film->video_frame_rate());
+ FrameRateChange frc (video_frame_rate(), film->video_frame_rate());
/* Compensate if the DCP is being run at a different frame rate
to the source; that is, if the video is run such that it will
look different in the DCP compared to the source (slower or faster).
- skip/repeat doesn't come into effect here.
*/
if (frc.change_speed) {
- t *= video_frame_rate() * frc.factor() / film->video_frame_rate();
+ t /= frc.speed_up;
}
return rint (t);
}
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");
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;
* @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)
{
}
FFmpegStream::as_xml (root);
}
-Time
+DCPTime
FFmpegContent::full_length () const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
- FrameRateConversion frc (video_frame_rate (), film->video_frame_rate ());
+ FrameRateChange frc (video_frame_rate (), film->video_frame_rate ());
return video_length() * frc.factor() * TIME_HZ / film->video_frame_rate ();
}
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) {
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
{}
};
- extern bool operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b);
- extern bool operator!= (FFmpegAudioStream const & a, FFmpegAudioStream const & b);
-
class FFmpegSubtitleStream : public FFmpegStream
{
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:
std::string technical_summary () const;
std::string information () const;
void as_xml (xmlpp::Node *) const;
- Time full_length () const;
+ DCPTime full_length () const;
std::string identifier () const;
/* AudioContent */
int audio_channels () const;
- AudioContent::Frame audio_length () const;
+ AudioFrame audio_length () const;
int content_audio_frame_rate () const;
int output_audio_frame_rate () const;
AudioMapping audio_mapping () const;
using boost::shared_ptr;
using boost::optional;
using boost::dynamic_pointer_cast;
-using libdcp::Size;
+using dcp::Size;
FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegContent> c, bool video, bool audio)
: Decoder (f)
, _decode_video (video)
, _decode_audio (audio)
, _pts_offset (0)
- , _just_sought (false)
{
setup_subtitle ();
Then we remove big initial gaps in PTS and we allow our
insertion of black frames to work.
- We will do:
- audio_pts_to_use = audio_pts_from_ffmpeg + pts_offset;
- video_pts_to_use = video_pts_from_ffmpeg + pts_offset;
+ We will do pts_to_use = pts_from_ffmpeg + pts_offset;
*/
bool const have_video = video && c->first_video();
- bool const have_audio = audio && c->audio_stream() && c->audio_stream()->first_audio;
+ bool const have_audio = _decode_audio && c->audio_stream () && c->audio_stream()->first_audio;
/* First, make one of them start at 0 */
decode_audio_packet ();
}
- /* Stop us being asked for any more data */
- _video_position = _ffmpeg_content->video_length ();
- _audio_position = _ffmpeg_content->audio_length ();
+ AudioDecoder::flush ();
}
-void
+bool
FFmpegDecoder::pass ()
{
int r = av_read_frame (_format_context, &_packet);
}
flush ();
- return;
+ return true;
}
shared_ptr<const Film> film = _film.lock ();
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 ();
}
av_free_packet (&_packet);
+ return false;
}
/** @param data pointer to array of pointers to buffers.
return av_get_bytes_per_sample (audio_sample_format ());
}
-void
-FFmpegDecoder::seek (VideoContent::Frame frame, bool accurate)
+int
+FFmpegDecoder::minimal_run (boost::function<bool (optional<ContentTime>, optional<ContentTime>, int)> finished)
{
- double const time_base = av_q2d (_format_context->streams[_video_stream]->time_base);
+ int frames_read = 0;
+ optional<ContentTime> last_video;
+ optional<ContentTime> last_audio;
- /* If we are doing an accurate seek, our initial shot will be 5 frames (5 being
- a number plucked from the air) earlier than we want to end up. The loop below
- will hopefully then step through to where we want to be.
- */
- int initial = frame;
+ while (!finished (last_video, last_audio, frames_read)) {
+ int r = av_read_frame (_format_context, &_packet);
+ if (r < 0) {
+ /* We should flush our decoders here, possibly yielding a few more frames,
+ but the consequence of having to do that is too hideous to contemplate.
+ Instead we give up and say that you can't seek too close to the end
+ of a file.
+ */
+ return frames_read;
+ }
- if (accurate) {
- initial -= 5;
+ ++frames_read;
+
+ double const time_base = av_q2d (_format_context->streams[_packet.stream_index]->time_base);
+
+ if (_packet.stream_index == _video_stream) {
+
+ avcodec_get_frame_defaults (_frame);
+
+ int finished = 0;
+ r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet);
+ if (r >= 0 && finished) {
+ last_video = rint (
+ (av_frame_get_best_effort_timestamp (_frame) * time_base + _pts_offset) * TIME_HZ
+ );
+ }
+
+ } else if (_ffmpeg_content->audio_stream() && _packet.stream_index == _ffmpeg_content->audio_stream()->index (_format_context)) {
+ AVPacket copy_packet = _packet;
+ while (copy_packet.size > 0) {
+
+ int finished;
+ r = avcodec_decode_audio4 (audio_codec_context(), _frame, &finished, &_packet);
+ if (r >= 0 && finished) {
+ last_audio = rint (
+ (av_frame_get_best_effort_timestamp (_frame) * time_base + _pts_offset) * TIME_HZ
+ );
+ }
+
+ copy_packet.data += r;
+ copy_packet.size -= r;
+ }
+ }
+
+ av_free_packet (&_packet);
}
- if (initial < 0) {
- initial = 0;
+ return frames_read;
+}
+
+bool
+FFmpegDecoder::seek_overrun_finished (ContentTime seek, optional<ContentTime> last_video, optional<ContentTime> last_audio) const
+{
+ return (last_video && last_video.get() >= seek) || (last_audio && last_audio.get() >= seek);
+}
+
+bool
+FFmpegDecoder::seek_final_finished (int n, int done) const
+{
+ return n == done;
+}
+
+void
+FFmpegDecoder::seek_and_flush (ContentTime t)
+{
+ int64_t s = ((double (t) / TIME_HZ) - _pts_offset) /
+ av_q2d (_format_context->streams[_video_stream]->time_base);
+
+ if (_ffmpeg_content->audio_stream ()) {
+ s = min (
+ s, int64_t (
+ ((double (t) / TIME_HZ) - _pts_offset) /
+ av_q2d (_ffmpeg_content->audio_stream()->stream(_format_context)->time_base)
+ )
+ );
}
- /* Initial seek time in the stream's timebase */
- int64_t const initial_vt = ((initial / _ffmpeg_content->video_frame_rate()) - _pts_offset) / time_base;
+ /* Ridiculous empirical hack */
+ s--;
- av_seek_frame (_format_context, _video_stream, initial_vt, AVSEEK_FLAG_BACKWARD);
+ av_seek_frame (_format_context, _video_stream, s, AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers (video_codec_context());
+ if (audio_codec_context ()) {
+ avcodec_flush_buffers (audio_codec_context ());
+ }
if (_subtitle_codec_context) {
avcodec_flush_buffers (_subtitle_codec_context);
}
+}
- /* This !accurate is piling hack upon hack; setting _just_sought to true
- even with accurate == true defeats our attempt to align the start
- of the video and audio. Here we disable that defeat when accurate == true
- i.e. when we are making a DCP rather than just previewing one.
- Ewww. This should be gone in 2.0.
+void
+FFmpegDecoder::seek (ContentTime time, bool accurate)
+{
+ Decoder::seek (time, accurate);
+ AudioDecoder::seek (time, accurate);
+
+ /* If we are doing an accurate seek, our initial shot will be 200ms (200 being
+ a number plucked from the air) earlier than we want to end up. The loop below
+ will hopefully then step through to where we want to be.
*/
- if (!accurate) {
- _just_sought = true;
+
+ ContentTime pre_roll = accurate ? (0.2 * TIME_HZ) : 0;
+ ContentTime initial_seek = time - pre_roll;
+ if (initial_seek < 0) {
+ initial_seek = 0;
}
-
- _video_position = frame;
-
- if (frame == 0 || !accurate) {
- /* We're already there, or we're as close as we need to be */
+
+ /* Initial seek time in the video stream's timebase */
+
+ seek_and_flush (initial_seek);
+
+ if (!accurate) {
+ /* That'll do */
return;
}
- while (1) {
- int r = av_read_frame (_format_context, &_packet);
- if (r < 0) {
- return;
- }
-
- if (_packet.stream_index != _video_stream) {
- av_free_packet (&_packet);
- continue;
- }
-
- int finished = 0;
- r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet);
- if (r >= 0 && finished) {
- _video_position = rint (
- (av_frame_get_best_effort_timestamp (_frame) * time_base + _pts_offset) * _ffmpeg_content->video_frame_rate()
- );
+ int const N = minimal_run (boost::bind (&FFmpegDecoder::seek_overrun_finished, this, time, _1, _2));
- if (_video_position >= (frame - 1)) {
- av_free_packet (&_packet);
- break;
- }
- }
-
- av_free_packet (&_packet);
+ seek_and_flush (initial_seek);
+ if (N > 0) {
+ minimal_run (boost::bind (&FFmpegDecoder::seek_final_finished, this, N - 1, _3));
}
}
int frame_finished;
int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, ©_packet);
+
if (decode_result < 0) {
shared_ptr<const Film> film = _film.lock ();
assert (film);
}
if (frame_finished) {
-
- if (_audio_position == 0) {
- /* Where we are in the source, in seconds */
- double const pts = av_q2d (_format_context->streams[copy_packet.stream_index]->time_base)
- * av_frame_get_best_effort_timestamp(_frame) + _pts_offset;
-
- if (pts > 0) {
- /* Emit some silence */
- shared_ptr<AudioBuffers> silence (
- new AudioBuffers (
- _ffmpeg_content->audio_channels(),
- pts * _ffmpeg_content->content_audio_frame_rate()
- )
- );
-
- silence->make_silent ();
- audio (silence, _audio_position);
- }
- }
+ ContentTime const ct = (
+ av_frame_get_best_effort_timestamp (_frame) *
+ av_q2d (_ffmpeg_content->audio_stream()->stream (_format_context)->time_base)
+ + _pts_offset
+ ) * TIME_HZ;
int const data_size = av_samples_get_buffer_size (
0, audio_codec_context()->channels, _frame->nb_samples, audio_sample_format (), 1
);
-
- audio (deinterleave_audio (_frame->data, data_size), _audio_position);
+
+ audio (deinterleave_audio (_frame->data, data_size), ct);
}
copy_packet.data += decode_result;
shared_ptr<FilterGraph> graph;
list<shared_ptr<FilterGraph> >::iterator i = _filter_graphs.begin();
- while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) {
+ while (i != _filter_graphs.end() && !(*i)->can_process (dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) {
++i;
}
shared_ptr<const Film> film = _film.lock ();
assert (film);
- graph.reset (new FilterGraph (_ffmpeg_content, libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
+ graph.reset (new FilterGraph (_ffmpeg_content, dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
_filter_graphs.push_back (graph);
film->log()->log (String::compose (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format));
}
if (i->second != AV_NOPTS_VALUE) {
-
double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset;
-
- if (_just_sought) {
- /* We just did a seek, so disable any attempts to correct for where we
- are / should be.
- */
- _video_position = rint (pts * _ffmpeg_content->video_frame_rate ());
- _just_sought = false;
- }
-
- double const next = _video_position / _ffmpeg_content->video_frame_rate();
- double const one_frame = 1 / _ffmpeg_content->video_frame_rate ();
- double delta = pts - next;
-
- while (delta > one_frame) {
- /* This PTS is more than one frame forward in time of where we think we should be; emit
- a black frame.
- */
-
- /* XXX: I think this should be a copy of the last frame... */
- boost::shared_ptr<Image> black (
- new Image (
- static_cast<AVPixelFormat> (_frame->format),
- libdcp::Size (video_codec_context()->width, video_codec_context()->height),
- true
- )
- );
-
- black->make_black ();
- video (image, false, _video_position);
- delta -= one_frame;
- }
-
- if (delta > -one_frame) {
- /* This PTS is within a frame of being right; emit this (otherwise it will be dropped) */
- video (image, false, _video_position);
- }
-
+ VideoFrame const f = rint (pts * _ffmpeg_content->video_frame_rate ());
+ video (image, false, f);
} else {
shared_ptr<const Film> film = _film.lock ();
assert (film);
{
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) {
}
}
-bool
-FFmpegDecoder::done () const
-{
- bool const vd = !_decode_video || (_video_position >= _ffmpeg_content->video_length());
- bool const ad = !_decode_audio || !_ffmpeg_content->audio_stream() || (_audio_position >= _ffmpeg_content->audio_length());
- return vd && ad;
-}
-
void
FFmpegDecoder::decode_subtitle_packet ()
{
indicate that the previous subtitle should stop.
*/
if (sub.num_rects <= 0) {
- subtitle (shared_ptr<Image> (), dcpomatic::Rect<double> (), 0, 0);
+ image_subtitle (shared_ptr<Image> (), dcpomatic::Rect<double> (), 0, 0);
return;
} else if (sub.num_rects > 1) {
throw DecodeError (_("multi-part subtitles not yet supported"));
/* Subtitle PTS in seconds (within the source, not taking into account any of the
source that we may have chopped off for the DCP)
*/
- double const packet_time = (static_cast<double> (sub.pts ) / AV_TIME_BASE) + _pts_offset;
-
+ double const packet_time = (static_cast<double> (sub.pts) / AV_TIME_BASE) + _pts_offset;
+
/* hence start time for this sub */
- Time const from = (packet_time + (double (sub.start_display_time) / 1e3)) * TIME_HZ;
- Time const to = (packet_time + (double (sub.end_display_time) / 1e3)) * TIME_HZ;
+ ContentTime const from = (packet_time + (double (sub.start_display_time) / 1e3)) * TIME_HZ;
+ ContentTime const to = (packet_time + (double (sub.end_display_time) / 1e3)) * TIME_HZ;
AVSubtitleRect const * rect = sub.rects[0];
/* Note RGBA is expressed little-endian, so the first byte in the word is R, second
G, third B, fourth A.
*/
- shared_ptr<Image> image (new Image (PIX_FMT_RGBA, libdcp::Size (rect->w, rect->h), true));
+ shared_ptr<Image> image (new Image (PIX_FMT_RGBA, dcp::Size (rect->w, rect->h), true));
/* Start of the first line in the subtitle */
uint8_t* sub_p = rect->pict.data[0];
out_p += image->stride()[0] / sizeof (uint32_t);
}
- libdcp::Size const vs = _ffmpeg_content->video_size ();
+ dcp::Size const vs = _ffmpeg_content->video_size ();
- subtitle (
+ image_subtitle (
image,
dcpomatic::Rect<double> (
static_cast<double> (rect->x) / vs.width,
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));
}
}
}
}
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;
return av_q2d (s->r_frame_rate);
}
-libdcp::Size
+dcp::Size
FFmpegExaminer::video_size () const
{
- return libdcp::Size (video_codec_context()->width, video_codec_context()->height);
+ return dcp::Size (video_codec_context()->width, video_codec_context()->height);
}
/** @return Length (in video frames) according to our content's header */
-VideoContent::Frame
+VideoFrame
FFmpegExaminer::video_length () const
{
- VideoContent::Frame const length = (double (_format_context->duration) / AV_TIME_BASE) * video_frame_rate();
+ VideoFrame const length = (double (_format_context->duration) / AV_TIME_BASE) * video_frame_rate();
return max (1, length);
}
FFmpegExaminer (boost::shared_ptr<const FFmpegContent>);
float video_frame_rate () const;
- libdcp::Size video_size () const;
- VideoContent::Frame video_length () const;
+ dcp::Size video_size () const;
+ VideoFrame video_length () const;
std::vector<boost::shared_ptr<FFmpegSubtitleStream> > subtitle_streams () const {
return _subtitle_streams;
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;
using boost::ends_with;
using boost::starts_with;
using boost::optional;
-using libdcp::Size;
-using libdcp::Signer;
+using dcp::Size;
+using dcp::Signer;
/* 5 -> 6
* AudioMapping XML changed.
* 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 ())
, _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 ();
}
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);
}
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");
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;
}
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");
_sequence_video = f.bool_child ("SequenceVideo");
_three_d = f.bool_child ("ThreeD");
_interop = f.bool_child ("Interop");
- _key = libdcp::Key (f.string_child ("Key"));
+ _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;
}
return file (p);
}
-/** @return List of subdirectories (not full paths) containing DCPs that can be successfully libdcp::DCP::read() */
+/** @return List of subdirectories (not full paths) containing DCPs that can be successfully dcp::DCP::read() */
list<boost::filesystem::path>
Film::dcps () const
{
) {
try {
- libdcp::DCP dcp (*i);
+ dcp::DCP dcp (*i);
dcp.read ();
out.push_back (i->path().leaf ());
} catch (...) {
_playlist->move_later (c);
}
-Time
+DCPTime
Film::length () const
{
return _playlist->length ();
return _playlist->has_subtitles ();
}
-OutputVideoFrame
+VideoFrame
Film::best_video_frame_rate () const
{
return _playlist->best_dcp_frame_rate ();
}
+FrameRateChange
+Film::active_frame_rate_change (DCPTime t) const
+{
+ return _playlist->active_frame_rate_change (t, video_frame_rate ());
+}
+
void
Film::playlist_content_changed (boost::weak_ptr<Content> c, int p)
{
signal_changed (CONTENT);
}
-OutputAudioFrame
-Film::time_to_audio_frames (Time t) const
+AudioFrame
+Film::time_to_audio_frames (DCPTime t) const
{
return t * audio_frame_rate () / TIME_HZ;
}
-OutputVideoFrame
-Film::time_to_video_frames (Time t) const
+VideoFrame
+Film::time_to_video_frames (DCPTime t) const
{
return t * video_frame_rate () / TIME_HZ;
}
-Time
-Film::audio_frames_to_time (OutputAudioFrame f) const
+DCPTime
+Film::audio_frames_to_time (AudioFrame f) const
{
return f * TIME_HZ / audio_frame_rate ();
}
-Time
-Film::video_frames_to_time (OutputVideoFrame f) const
+DCPTime
+Film::video_frames_to_time (VideoFrame f) const
{
return f * TIME_HZ / video_frame_rate ();
}
-OutputAudioFrame
+AudioFrame
Film::audio_frame_rate () const
{
/* XXX */
signal_changed (SEQUENCE_VIDEO);
}
-libdcp::Size
+dcp::Size
Film::full_frame () const
{
switch (_resolution) {
case RESOLUTION_2K:
- return libdcp::Size (2048, 1080);
+ return dcp::Size (2048, 1080);
case RESOLUTION_4K:
- return libdcp::Size (4096, 2160);
+ return dcp::Size (4096, 2160);
}
assert (false);
- return libdcp::Size ();
+ return dcp::Size ();
}
-libdcp::KDM
+dcp::KDM
Film::make_kdm (
- shared_ptr<libdcp::Certificate> target,
+ shared_ptr<dcp::Certificate> target,
boost::filesystem::path dcp_dir,
boost::posix_time::ptime from,
boost::posix_time::ptime until
{
shared_ptr<const Signer> signer = make_signer ();
- libdcp::DCP dcp (dir (dcp_dir.string ()));
+ dcp::DCP dcp (dir (dcp_dir.string ()));
try {
dcp.read ();
time_t now = time (0);
struct tm* tm = localtime (&now);
- string const issue_date = libdcp::tm_to_string (tm);
+ string const issue_date = dcp::tm_to_string (tm);
dcp.cpls().front()->set_mxf_keys (key ());
- return libdcp::KDM (dcp.cpls().front(), signer, target, from, until, "DCP-o-matic", issue_date);
+ return dcp::KDM (dcp.cpls().front(), signer, target, from, until, "DCP-o-matic", issue_date);
}
-list<libdcp::KDM>
+list<dcp::KDM>
Film::make_kdms (
list<shared_ptr<Screen> > screens,
boost::filesystem::path dcp,
boost::posix_time::ptime until
) const
{
- list<libdcp::KDM> kdms;
+ list<dcp::KDM> kdms;
for (list<shared_ptr<Screen> >::iterator i = screens.begin(); i != screens.end(); ++i) {
kdms.push_back (make_kdm ((*i)->certificate, dcp, from, until));
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;
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;
return _dirty;
}
- libdcp::Size full_frame () const;
+ dcp::Size full_frame () const;
std::list<boost::filesystem::path> dcps () const;
boost::shared_ptr<Player> make_player () const;
boost::shared_ptr<Playlist> playlist () const;
- OutputAudioFrame audio_frame_rate () const;
+ AudioFrame audio_frame_rate () const;
- OutputAudioFrame time_to_audio_frames (Time) const;
- OutputVideoFrame time_to_video_frames (Time) const;
- Time video_frames_to_time (OutputVideoFrame) const;
- Time audio_frames_to_time (OutputAudioFrame) const;
+ AudioFrame time_to_audio_frames (DCPTime) const;
+ VideoFrame time_to_video_frames (DCPTime) const;
+ DCPTime video_frames_to_time (VideoFrame) const;
+ DCPTime audio_frames_to_time (AudioFrame) const;
uint64_t required_disk_space () const;
bool should_be_enough_disk_space (double &, double &) const;
/* Proxies for some Playlist methods */
ContentList content () const;
- Time length () const;
+ DCPTime length () const;
bool has_subtitles () const;
- OutputVideoFrame best_video_frame_rate () const;
+ VideoFrame best_video_frame_rate () const;
+ FrameRateChange active_frame_rate_change (DCPTime) const;
- libdcp::KDM
+ dcp::KDM
make_kdm (
- boost::shared_ptr<libdcp::Certificate> target,
+ boost::shared_ptr<dcp::Certificate> target,
boost::filesystem::path dcp,
boost::posix_time::ptime from,
boost::posix_time::ptime until
) const;
- std::list<libdcp::KDM> make_kdms (
+ std::list<dcp::KDM> make_kdms (
std::list<boost::shared_ptr<Screen> >,
boost::filesystem::path dcp,
boost::posix_time::ptime from,
boost::posix_time::ptime until
) const;
- libdcp::Key key () const {
+ dcp::Key key () const {
return _key;
}
+ int state_version () const {
+ return _state_version;
+ }
+
/** Identifiers for the parts of our state;
used for signalling changes.
*/
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:
bool _three_d;
bool _sequence_video;
bool _interop;
- libdcp::Key _key;
+ dcp::Key _key;
+ int _state_version;
+
/** true if our state has changed since we last saved it */
mutable bool _dirty;
run ();
- } catch (libdcp::FileError& e) {
+ } catch (dcp::FileError& e) {
string m = String::compose (_("An error occurred whilst handling the file %1."), boost::filesystem::path (e.filename()).leaf());
}
}
-/** @return Time (in seconds) that this sub-job has been running */
+/** @return DCPTime (in seconds) that this sub-job has been running */
int
Job::elapsed_time () const
{
}
}
- /** @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
{
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
return String::compose (_("Transcode %1"), _film->name());
}
+ string
+ TranscodeJob::json_name () const
+ {
+ return N_("transcode");
+ }
+
void
TranscodeJob::run ()
{
}
/* Compute approximate proposed length here, as it's only here that we need it */
- OutputVideoFrame const left = _film->time_to_video_frames (_film->length ()) - t->video_frames_out();
+ VideoFrame const left = _film->time_to_video_frames (_film->length ()) - t->video_frames_out();
return left / fps;
}
#include <openssl/md5.h>
#include <magick/MagickCore.h>
#include <magick/version.h>
+#include <pangomm/init.h>
#include <libdcp/version.h>
#include <libdcp/util.h>
#include <libdcp/signer_chain.h>
using std::max;
using std::list;
using std::multimap;
+ using std::map;
using std::istream;
using std::numeric_limits;
using std::pair;
using boost::thread;
using boost::lexical_cast;
using boost::optional;
-using libdcp::Size;
+using dcp::Size;
static boost::thread::id ui_thread;
static boost::filesystem::path backtrace_file;
<< N_("libswscale ") << ffmpeg_version_to_string (swscale_version()) << N_(", ")
<< MagickVersion << N_(", ")
<< N_("libssh ") << ssh_version (0) << N_(", ")
- << N_("libdcp ") << libdcp::version << N_(" git ") << libdcp::git_commit;
+ << N_("libdcp ") << dcp::version << N_(" git ") << dcp::git_commit;
return s.str ();
}
set_terminate (terminate);
- libdcp::init ();
+ Pango::init ();
+ dcp::init ();
Ratio::setup_ratios ();
DCPContentType::setup_dcp_content_types ();
* @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)
+video_frames_to_audio_frames (VideoFrame 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)
{
- 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"),
_("Lfe (sub)"),
_("Left surround"),
_("Right surround"),
+ _("HI"),
+ _("VI")
};
return channels[c];
}
-FrameRateConversion::FrameRateConversion (float source, int dcp)
+FrameRateChange::FrameRateChange (float source, int dcp)
: skip (false)
, repeat (1)
, change_speed (false)
repeat = round (dcp / source);
}
- change_speed = !about_equal (source * factor(), dcp);
+ speed_up = dcp / (source * factor());
+ change_speed = !about_equal (speed_up, 1.0);
if (!skip && repeat == 1 && !change_speed) {
description = _("Content and DCP have the same rate.\n");
return t;
}
-shared_ptr<const libdcp::Signer>
+shared_ptr<const dcp::Signer>
make_signer ()
{
boost::filesystem::path const sd = Config::instance()->signer_chain_directory ();
if (!boost::filesystem::exists (p)) {
boost::filesystem::remove_all (sd);
boost::filesystem::create_directories (sd);
- libdcp::make_signer_chain (sd, openssl_path ());
+ dcp::make_signer_chain (sd, openssl_path ());
break;
}
++i;
}
- libdcp::CertificateChain chain;
+ dcp::CertificateChain chain;
{
boost::filesystem::path p (sd);
p /= "ca.self-signed.pem";
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
+ chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (p)));
}
{
boost::filesystem::path p (sd);
p /= "intermediate.signed.pem";
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
+ chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (p)));
}
{
boost::filesystem::path p (sd);
p /= "leaf.signed.pem";
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
+ chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (p)));
}
boost::filesystem::path signer_key (sd);
signer_key /= "leaf.key";
- return shared_ptr<const libdcp::Signer> (new libdcp::Signer (chain, signer_key));
+ 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 libdcp::Size (rint (full_frame.height * ratio), full_frame.height);
+ return dcp::Size (rint (full_frame.height * ratio), full_frame.height);
}
- return libdcp::Size (full_frame.width, rint (full_frame.width / ratio));
+ return dcp::Size (full_frame.width, rint (full_frame.width / ratio));
+}
+
+DCPTime
+time_round_up (DCPTime t, DCPTime nearest)
+{
+ DCPTime const a = t + nearest - 1;
+ return a - (a % nearest);
}
void *
}
return p;
}
+
+ string
+ entities_to_text (string e)
+ {
+ boost::algorithm::replace_all (e, "%3A", ":");
+ boost::algorithm::replace_all (e, "%2F", "/");
+ return e;
+ }
#include <boost/optional.hpp>
#include <boost/filesystem.hpp>
#include <libdcp/util.h>
+#include <libdcp/signer.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#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"
extern boost::filesystem::path mo_path ();
#endif
extern std::string tidy_for_filename (std::string);
-extern boost::shared_ptr<const libdcp::Signer> make_signer ();
-extern libdcp::Size fit_ratio_within (float ratio, libdcp::Size);
+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 FrameRateConversion
+struct FrameRateChange
{
- FrameRateConversion (float, int);
+ FrameRateChange (float, int);
/** @return factor by which to multiply a source frame rate
to get the effective rate after any skip or repeat has happened.
*/
bool change_speed;
+ /** Amount by which the video is being sped-up in the DCP; e.g. for a
+ * 24fps source in a 25fps DCP this would be 25/24.
+ */
+ float speed_up;
+
std::string description;
};
extern int dcp_audio_frame_rate (int);
extern int stride_round_up (int, int const *, int);
+extern DCPTime time_round_up (DCPTime, DCPTime);
extern std::multimap<std::string, std::string> read_key_value (std::istream& s);
extern int get_required_int (std::multimap<std::string, std::string> const & kv, std::string k);
extern float get_required_float (std::multimap<std::string, std::string> const & kv, std::string k);
int _timeout;
};
-extern int64_t video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second);
+extern int64_t video_frames_to_audio_frames (VideoFrame v, float audio_sample_rate, float frames_per_second);
class LocaleGuard
{
job.cc
job_manager.cc
kdm.cc
+ json_server.cc
log.cc
player.cc
playlist.cc
ratio.cc
+ render_subtitles.cc
resampler.cc
scp_dcp_job.cc
scaler.cc
sndfile_content.cc
sndfile_decoder.cc
sound_processor.cc
+ subrip.cc
+ subrip_content.cc
+ subrip_decoder.cc
subtitle_content.cc
subtitle_decoder.cc
timer.cc
AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE
BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2
SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XML++
- CURL ZIP QUICKMAIL
+ CURL ZIP QUICKMAIL PANGOMM CAIROMM
"""
obj.source = sources + ' version.cc'
return;
}
- libdcp::Channel d = static_cast<libdcp::Channel> (ev.GetCol() - 1);
+ dcp::Channel d = static_cast<dcp::Channel> (ev.GetCol() - 1);
if (_map.get (ev.GetRow(), d) > 0) {
_map.set (ev.GetRow(), d, 0);
void
AudioMappingView::off ()
{
- _map.set (_menu_row, static_cast<libdcp::Channel> (_menu_column - 1), 0);
+ _map.set (_menu_row, static_cast<dcp::Channel> (_menu_column - 1), 0);
map_changed ();
}
void
AudioMappingView::full ()
{
- _map.set (_menu_row, static_cast<libdcp::Channel> (_menu_column - 1), 1);
+ _map.set (_menu_row, static_cast<dcp::Channel> (_menu_column - 1), 1);
map_changed ();
}
void
AudioMappingView::minus3dB ()
{
- _map.set (_menu_row, static_cast<libdcp::Channel> (_menu_column - 1), 1 / sqrt (2));
+ _map.set (_menu_row, static_cast<dcp::Channel> (_menu_column - 1), 1 / sqrt (2));
map_changed ();
}
void
AudioMappingView::edit ()
{
- libdcp::Channel d = static_cast<libdcp::Channel> (_menu_column - 1);
+ dcp::Channel d = static_cast<dcp::Channel> (_menu_column - 1);
AudioGainDialog* dialog = new AudioGainDialog (this, _menu_row, _menu_column - 1, _map.get (_menu_row, d));
if (dialog->ShowModal () == wxID_OK) {
_grid->SetCellValue (i, 0, wxString::Format (wxT("%d"), i + 1));
for (int j = 1; j < _grid->GetNumberCols(); ++j) {
- _grid->SetCellValue (i, j, std_to_wx (lexical_cast<string> (_map.get (i, static_cast<libdcp::Channel> (j - 1)))));
+ _grid->SetCellValue (i, j, std_to_wx (lexical_cast<string> (_map.get (i, static_cast<dcp::Channel> (j - 1)))));
}
}
int const c = _grid->GetNumberCols ();
_grid->SetColLabelValue (0, _("Content channel"));
+
+ #if MAX_AUDIO_CHANNELS != 8
+ #warning AudioMappingView::set_column_labels() is expecting the wrong MAX_AUDIO_CHANNELS
+ #endif
if (c > 0) {
_grid->SetColLabelValue (1, _("L"));
_grid->SetColLabelValue (6, _("Rs"));
}
+ if (c > 6) {
+ _grid->SetColLabelValue (7, _("HI"));
+ }
+
+ if (c > 7) {
+ _grid->SetColLabelValue (8, _("VI"));
+ }
+
_grid->AutoSize ();
}
if (row != _last_tooltip_row || column != _last_tooltip_column) {
wxString s;
- float const gain = _map.get (row, static_cast<libdcp::Channel> (column - 1));
+ float const gain = _map.get (row, static_cast<dcp::Channel> (column - 1));
if (gain == 0) {
s = wxString::Format (_("No audio will be passed from content channel %d to DCP channel %d."), row + 1, column);
} else if (gain == 1) {
FFmpegAudioStream a (shared_ptr<cxml::Node> (new cxml::Node (root)), 5);
- BOOST_CHECK_EQUAL (a.id, 4);
+ BOOST_CHECK_EQUAL (a.identifier(), "4");
BOOST_CHECK_EQUAL (a.frame_rate, 44100);
BOOST_CHECK_EQUAL (a.channels, 2);
BOOST_CHECK_EQUAL (a.name, "hello there world");
BOOST_CHECK_EQUAL (a.mapping.content_channels(), 2);
- BOOST_CHECK_EQUAL (a.mapping.get (0, libdcp::LEFT), 1);
- BOOST_CHECK_EQUAL (a.mapping.get (0, libdcp::RIGHT), 0);
- BOOST_CHECK_EQUAL (a.mapping.get (0, libdcp::CENTRE), 1);
- BOOST_CHECK_EQUAL (a.mapping.get (1, libdcp::LEFT), 0);
- BOOST_CHECK_EQUAL (a.mapping.get (1, libdcp::RIGHT), 1);
- BOOST_CHECK_EQUAL (a.mapping.get (1, libdcp::CENTRE), 1);
+ BOOST_CHECK_EQUAL (a.mapping.get (0, dcp::LEFT), 1);
+ BOOST_CHECK_EQUAL (a.mapping.get (0, dcp::RIGHT), 0);
+ BOOST_CHECK_EQUAL (a.mapping.get (0, dcp::CENTRE), 1);
+ BOOST_CHECK_EQUAL (a.mapping.get (1, dcp::LEFT), 0);
+ BOOST_CHECK_EQUAL (a.mapping.get (1, dcp::RIGHT), 1);
+ BOOST_CHECK_EQUAL (a.mapping.get (1, dcp::CENTRE), 1);
}
import sys
APPNAME = 'dcpomatic'
-VERSION = '1.64.15devel'
+VERSION = '2.0.0devel'
def options(opt):
opt.load('compiler_cxx')
conf.check_cfg(package='libopenjpeg', args='--cflags --libs', max_version='1.5.1', mandatory=True)
def static_dcp(conf, static_boost, static_xmlpp, static_xmlsec, static_ssh):
-- conf.check_cfg(package='libdcp', atleast_version='0.92', args='--cflags', uselib_store='DCP', mandatory=True)
++ conf.check_cfg(package='libdcp-1.0', atleast_version='0.92', args='--cflags', uselib_store='DCP', mandatory=True)
conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP]
-- conf.env.STLIB_DCP = ['dcp', 'asdcp-libdcp', 'kumu-libdcp']
++ conf.env.STLIB_DCP = ['dcp-1.0', 'asdcp-libdcp', 'kumu-libdcp']
conf.env.LIB_DCP = ['glibmm-2.4', 'ssl', 'crypto', 'bz2', 'xslt']
if static_boost:
conf.env.LIB_DCP.append('ssh')
def dynamic_dcp(conf):
-- conf.check_cfg(package='libdcp', atleast_version='0.92', args='--cflags --libs', uselib_store='DCP', mandatory=True)
++ conf.check_cfg(package='libdcp-1.0', atleast_version='0.92', args='--cflags --libs', uselib_store='DCP', mandatory=True)
conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP]
def dynamic_ssh(conf):
conf.check_cfg(package='glib-2.0', args='--cflags --libs', uselib_store='GLIB', mandatory=True)
conf.check_cfg(package= '', path=conf.options.magickpp_config, args='--cppflags --cxxflags --libs', uselib_store='MAGICK', mandatory=True)
conf.check_cfg(package='libzip', args='--cflags --libs', uselib_store='ZIP', mandatory=True)
+ conf.check_cfg(package='pangomm-1.4', args='--cflags --libs', uselib_store='PANGOMM', mandatory=True)
+ conf.check_cfg(package='cairomm-1.0', args='--cflags --libs', uselib_store='CAIROMM', mandatory=True)
conf.check_cc(fragment="""
#include <glib.h>