/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-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
FFmpegContent::FFmpegContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
: Content (f, node)
- , VideoContent (f, node)
+ , VideoContent (f, node, version)
, AudioContent (f, node)
, SubtitleContent (f, node, version)
{
shared_ptr<FFmpegExaminer> examiner (new FFmpegExaminer (shared_from_this ()));
- VideoContent::Frame video_length = 0;
- video_length = examiner->video_length ();
- film->log()->log (String::compose ("Video length obtained from header as %1 frames", video_length));
+ ContentTime video_length = examiner->video_length ();
+ film->log()->log (String::compose ("Video length obtained from header as %1 frames", video_length.frames (video_frame_rate ())));
{
boost::mutex::scoped_lock lm (_mutex);
string
FFmpegContent::information () const
{
- if (video_length() == 0 || video_frame_rate() == 0) {
+ if (video_length() == ContentTime (0) || video_frame_rate() == ContentTime (0)) {
return "";
}
signal_changed (FFmpegContentProperty::AUDIO_STREAM);
}
-AudioContent::Frame
+ContentTime
FFmpegContent::audio_length () const
{
- int const cafr = content_audio_frame_rate ();
- int const vfr = video_frame_rate ();
- VideoContent::Frame const vl = video_length ();
-
- boost::mutex::scoped_lock lm (_mutex);
- if (!_audio_stream) {
- return 0;
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ if (!_audio_stream) {
+ return ContentTime ();
+ }
}
-
- return video_frames_to_audio_frames (vl, cafr, vfr);
+
+ return video_length();
}
int
/* 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);
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 ());
- return video_length() * frc.factor() * TIME_HZ / film->video_frame_rate ();
+ return DCPTime (video_length(), FrameRateChange (video_frame_rate (), film->video_frame_rate ()));
}
AudioMapping
#include <boost/date_time.hpp>
#include <libxml++/libxml++.h>
#include <libcxml/cxml.h>
-#include <libdcp/signer_chain.h>
-#include <libdcp/cpl.h>
-#include <libdcp/signer.h>
-#include <libdcp/util.h>
-#include <libdcp/kdm.h>
+#include <dcp/signer_chain.h>
+#include <dcp/cpl.h>
+#include <dcp/signer.h>
+#include <dcp/util.h>
+#include <dcp/kdm.h>
#include "film.h"
#include "job.h"
#include "util.h"
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.
+ * 7 -> 8
+ * Use <Scale> tag in <VideoContent> rather than <Ratio>.
*/
- int const Film::current_state_version = 7;
+ int const Film::current_state_version = 8;
/** Construct a Film object in a given directory.
*
_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"), _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
+int
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
-{
- return divide_with_round (t * audio_frame_rate (), TIME_HZ);
-}
-
-OutputVideoFrame
-Film::time_to_video_frames (Time t) const
-{
- return divide_with_round (t * video_frame_rate (), TIME_HZ);
-}
-
-Time
-Film::audio_frames_to_time (OutputAudioFrame f) const
-{
- return divide_with_round (f * TIME_HZ, audio_frame_rate ());
-}
-
-Time
-Film::video_frames_to_time (OutputVideoFrame f) const
-{
- return divide_with_round (f * TIME_HZ, video_frame_rate ());
-}
-
-OutputAudioFrame
+int
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));
uint64_t
Film::required_disk_space () const
{
- return uint64_t (j2k_bandwidth() / 8) * length() / TIME_HZ;
+ return uint64_t (j2k_bandwidth() / 8) * length().seconds();
}
/** This method checks the disk that the Film is on and tries to decide whether or not
/*
-- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
++ Copyright (C) 2012-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
#include "image.h"
#include "exceptions.h"
#include "scaler.h"
+#include "timer.h"
+ #include "i18n.h"
+
using std::string;
using std::min;
using std::cout;
+ using std::cerr;
using boost::shared_ptr;
-using libdcp::Size;
+using dcp::Size;
int
Image::line_factor (int n) const
/** Crop this image, scale it to `inter_size' and then place it in a black frame of `out_size' */
shared_ptr<Image>
-Image::crop_scale_window (Crop crop, libdcp::Size inter_size, libdcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const
+Image::crop_scale_window (Crop crop, dcp::Size inter_size, dcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const
{
assert (scaler);
/* Empirical testing suggests that sws_scale() will crash if
*/
assert (aligned ());
+ assert (out_size.width >= inter_size.width);
+ assert (out_size.height >= inter_size.height);
+
+ /* Here's an image of out_size */
shared_ptr<Image> out (new Image (out_format, out_size, out_aligned));
out->make_black ();
-
- dcp::Size cropped_size = crop.apply (size ());
- libdcp::Size const cropped_size = crop.apply (size ());
+ /* Size of the image after any crop */
++ dcp::Size const cropped_size = crop.apply (size ());
+
+ /* Scale context for a scale from cropped_size to inter_size */
struct SwsContext* scale_context = sws_getContext (
- cropped_size.width, cropped_size.height, pixel_format(),
- inter_size.width, inter_size.height, out_format,
- scaler->ffmpeg_id (), 0, 0, 0
+ cropped_size.width, cropped_size.height, pixel_format(),
+ inter_size.width, inter_size.height, out_format,
+ scaler->ffmpeg_id (), 0, 0, 0
);
+ if (!scale_context) {
+ throw StringError (N_("Could not allocate SwsContext"));
+ }
+
+ /* Prepare input data pointers with crop */
uint8_t* scale_in_data[components()];
for (int c = 0; c < components(); ++c) {
scale_in_data[c] = data()[c] + int (rint (bytes_per_pixel(c) * crop.left)) + stride()[c] * (crop.top / line_factor(c));
}
+ /* Corner of the image within out_size */
Position<int> const corner ((out_size.width - inter_size.width) / 2, (out_size.height - inter_size.height) / 2);
- uint8_t* scale_out_data[components()];
- for (int c = 0; c < components(); ++c) {
+ uint8_t* scale_out_data[out->components()];
+ for (int c = 0; c < out->components(); ++c) {
scale_out_data[c] = out->data()[c] + int (rint (out->bytes_per_pixel(c) * corner.x)) + out->stride()[c] * corner.y;
}
}
shared_ptr<Image>
-Image::scale (libdcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const
+Image::scale (dcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const
{
assert (scaler);
/* Empirical testing suggests that sws_scale() will crash if
shared_ptr<Image>
Image::crop (Crop crop, bool aligned) const
{
- libdcp::Size cropped_size = crop.apply (size ());
+ dcp::Size cropped_size = crop.apply (size ());
shared_ptr<Image> out (new Image (pixel_format(), cropped_size, aligned));
for (int c = 0; c < components(); ++c) {
void
Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
{
- /* Only implemented for RGBA onto RGB24 so far */
- assert (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA);
+ int this_bpp = 0;
+ int other_bpp = 0;
+
+ if (_pixel_format == PIX_FMT_BGRA && other->pixel_format() == PIX_FMT_RGBA) {
+ this_bpp = 4;
+ other_bpp = 4;
+ } else if (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA) {
+ this_bpp = 3;
+ other_bpp = 4;
+ } else {
+ assert (false);
+ }
int start_tx = position.x;
int start_ox = 0;
}
for (int ty = start_ty, oy = start_oy; ty < size().height && oy < other->size().height; ++ty, ++oy) {
- uint8_t* tp = data()[0] + ty * stride()[0] + position.x * 3;
+ uint8_t* tp = data()[0] + ty * stride()[0] + position.x * this_bpp;
uint8_t* op = other->data()[0] + oy * other->stride()[0];
for (int tx = start_tx, ox = start_ox; tx < size().width && ox < other->size().width; ++tx, ++ox) {
float const alpha = float (op[3]) / 255;
tp[0] = (tp[0] * (1 - alpha)) + op[0] * alpha;
tp[1] = (tp[1] * (1 - alpha)) + op[1] * alpha;
tp[2] = (tp[2] * (1 - alpha)) + op[2] * alpha;
- tp += 3;
- op += 4;
+ tp += this_bpp;
+ op += other_bpp;
}
}
}
* @param p Pixel format.
* @param s Size in pixels.
*/
-Image::Image (AVPixelFormat p, libdcp::Size s, bool aligned)
- : libdcp::Image (s)
+Image::Image (AVPixelFormat p, dcp::Size s, bool aligned)
+ : dcp::Image (s)
, _pixel_format (p)
, _aligned (aligned)
{
}
Image::Image (Image const & other)
- : libdcp::Image (other)
+ : dcp::Image (other)
, _pixel_format (other._pixel_format)
, _aligned (other._aligned)
{
}
Image::Image (AVFrame* frame)
- : libdcp::Image (libdcp::Size (frame->width, frame->height))
+ : dcp::Image (dcp::Size (frame->width, frame->height))
, _pixel_format (static_cast<AVPixelFormat> (frame->format))
, _aligned (true)
{
}
Image::Image (shared_ptr<const Image> other, bool aligned)
- : libdcp::Image (other)
+ : dcp::Image (other)
, _pixel_format (other->_pixel_format)
, _aligned (aligned)
{
void
Image::swap (Image & other)
{
- libdcp::Image::swap (other);
+ dcp::Image::swap (other);
std::swap (_pixel_format, other._pixel_format);
return _stride;
}
-libdcp::Size
+dcp::Size
Image::size () const
{
return _size;
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-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
}
- ImageContent::ImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int)
+ ImageContent::ImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
: Content (f, node)
- , VideoContent (f, node)
+ , VideoContent (f, node, version)
{
}
}
void
-ImageContent::set_video_length (VideoContent::Frame len)
+ImageContent::set_video_length (ContentTime len)
{
{
boost::mutex::scoped_lock lm (_mutex);
signal_changed (ContentProperty::LENGTH);
}
-Time
+DCPTime
ImageContent::full_length () const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- FrameRateConversion frc (video_frame_rate(), film->video_frame_rate ());
- return video_length() * frc.factor() * TIME_HZ / video_frame_rate();
+ return DCPTime (video_length(), FrameRateChange (video_frame_rate(), film->video_frame_rate()));
}
string
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-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
std::string summary () const;
std::string technical_summary () const;
void as_xml (xmlpp::Node *) const;
- Time full_length () const;
+ DCPTime full_length () const;
std::string identifier () const;
- void set_video_length (VideoContent::Frame);
+ void set_video_length (ContentTime);
bool still () const;
void set_video_frame_rate (float);
};
*/
#include <stdint.h>
+#include <algorithm>
#include "player.h"
#include "film.h"
#include "ffmpeg_decoder.h"
+#include "audio_buffers.h"
#include "ffmpeg_content.h"
#include "image_decoder.h"
#include "image_content.h"
#include "sndfile_decoder.h"
#include "sndfile_content.h"
#include "subtitle_content.h"
+#include "subrip_decoder.h"
+#include "subrip_content.h"
#include "playlist.h"
#include "job.h"
#include "image.h"
#include "ratio.h"
-#include "resampler.h"
#include "log.h"
#include "scaler.h"
+#include "render_subtitles.h"
using std::list;
using std::cout;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::dynamic_pointer_cast;
+using boost::optional;
class Piece
{
public:
- Piece (shared_ptr<Content> c)
- : content (c)
- , video_position (c->position ())
- , audio_position (c->position ())
- , repeat_to_do (0)
- , repeat_done (0)
- {}
-
- Piece (shared_ptr<Content> c, shared_ptr<Decoder> d)
+ Piece (shared_ptr<Content> c, shared_ptr<Decoder> d, FrameRateChange f)
: content (c)
, decoder (d)
- , video_position (c->position ())
- , audio_position (c->position ())
- , repeat_to_do (0)
- , repeat_done (0)
+ , frc (f)
{}
- /** Set this piece to repeat a video frame a given number of times */
- void set_repeat (IncomingVideo video, int num)
- {
- repeat_video = video;
- repeat_to_do = num;
- repeat_done = 0;
- }
-
- void reset_repeat ()
- {
- repeat_video.image.reset ();
- repeat_to_do = 0;
- repeat_done = 0;
- }
-
- bool repeating () const
- {
- return repeat_done != repeat_to_do;
- }
-
- void repeat (Player* player)
- {
- player->process_video (
- repeat_video.weak_piece,
- repeat_video.image,
- repeat_video.eyes,
- repeat_done > 0,
- repeat_video.frame,
- (repeat_done + 1) * (TIME_HZ / player->_film->video_frame_rate ())
- );
-
- ++repeat_done;
- }
-
shared_ptr<Content> content;
shared_ptr<Decoder> decoder;
- /** Time of the last video we emitted relative to the start of the DCP */
- Time video_position;
- /** Time of the last audio we emitted relative to the start of the DCP */
- Time audio_position;
-
- IncomingVideo repeat_video;
- int repeat_to_do;
- int repeat_done;
+ FrameRateChange frc;
};
Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
, _have_valid_pieces (false)
, _video_position (0)
, _audio_position (0)
- , _audio_merger (f->audio_channels(), bind (&Film::time_to_audio_frames, f.get(), _1), bind (&Film::audio_frames_to_time, f.get(), _1))
+ , _audio_merger (f->audio_channels(), f->audio_frame_rate ())
, _last_emit_was_black (false)
+ , _just_did_inaccurate_seek (false)
+ , _approximate_size (false)
{
_playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
_playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
setup_pieces ();
}
- Time earliest_t = TIME_MAX;
- shared_ptr<Piece> earliest;
- enum {
- VIDEO,
- AUDIO
- } type = VIDEO;
+ /* Interrogate all our pieces to find the one with the earliest decoded data */
+
+ shared_ptr<Piece> earliest_piece;
+ shared_ptr<Decoded> earliest_decoded;
+ DCPTime earliest_time = DCPTime::max ();
+ DCPTime earliest_audio = DCPTime::max ();
for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
- if ((*i)->decoder->done ()) {
- continue;
- }
- shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> ((*i)->decoder);
- shared_ptr<AudioDecoder> ad = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
+ DCPTime const offset = (*i)->content->position() - (*i)->content->trim_start();
+
+ bool done = false;
+ shared_ptr<Decoded> dec;
+ while (!done) {
+ dec = (*i)->decoder->peek ();
+ if (!dec) {
+ /* Decoder has nothing else to give us */
+ break;
+ }
- if (_video && vd) {
- if ((*i)->video_position < earliest_t) {
- earliest_t = (*i)->video_position;
- earliest = *i;
- type = VIDEO;
+
+ dec->set_dcp_times ((*i)->frc, offset);
+ DCPTime const t = dec->dcp_time - offset;
+ cout << "Peeked " << (*i)->content->paths()[0] << " for " << t << " cf " << ((*i)->content->full_length() - (*i)->content->trim_end ()) << "\n";
+ if (t >= ((*i)->content->full_length() - (*i)->content->trim_end ())) {
+ /* In the end-trimmed part; decoder has nothing else to give us */
+ dec.reset ();
+ done = true;
+ } else if (t >= (*i)->content->trim_start ()) {
+ /* Within the un-trimmed part; everything's ok */
+ done = true;
+ } else {
+ /* Within the start-trimmed part; get something else */
+ (*i)->decoder->consume ();
}
}
- if (_audio && ad && ad->has_audio ()) {
- if ((*i)->audio_position < earliest_t) {
- earliest_t = (*i)->audio_position;
- earliest = *i;
- type = AUDIO;
- }
+ if (!dec) {
+ continue;
}
- }
- if (!earliest) {
+ if (dec->dcp_time < earliest_time) {
+ earliest_piece = *i;
+ earliest_decoded = dec;
+ earliest_time = dec->dcp_time;
+ }
+
+ if (dynamic_pointer_cast<DecodedAudio> (dec) && dec->dcp_time < earliest_audio) {
+ earliest_audio = dec->dcp_time;
+ }
+ }
+
+ if (!earliest_piece) {
flush ();
return true;
}
- switch (type) {
- case VIDEO:
- if (earliest_t > _video_position) {
- emit_black ();
- } else {
- if (earliest->repeating ()) {
- earliest->repeat (this);
- } else {
- earliest->decoder->pass ();
- }
- }
- break;
-
- case AUDIO:
- if (earliest_t > _audio_position) {
- emit_silence (_film->time_to_audio_frames (earliest_t - _audio_position));
- } else {
- earliest->decoder->pass ();
-
- if (earliest->decoder->done()) {
- shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (earliest->content);
- assert (ac);
- shared_ptr<Resampler> re = resampler (ac, false);
- if (re) {
- shared_ptr<const AudioBuffers> b = re->flush ();
- if (b->frames ()) {
- process_audio (earliest, b, ac->audio_length ());
- }
- }
- }
+ if (earliest_audio != DCPTime::max ()) {
+ if (earliest_audio.get() < 0) {
+ earliest_audio = DCPTime ();
}
- break;
+ TimedAudioBuffers<DCPTime> tb = _audio_merger.pull (earliest_audio);
+ Audio (tb.audio, tb.time);
+ /* This assumes that the audio-frames-to-time conversion is exact
+ so that there are no accumulated errors caused by rounding.
+ */
+ _audio_position += DCPTime::from_frames (tb.audio->frames(), _film->audio_frame_rate ());
}
- if (_audio) {
- boost::optional<Time> audio_done_up_to;
- for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
- if ((*i)->decoder->done ()) {
- continue;
+ /* Emit the earliest thing */
+
+ shared_ptr<DecodedVideo> dv = dynamic_pointer_cast<DecodedVideo> (earliest_decoded);
+ shared_ptr<DecodedAudio> da = dynamic_pointer_cast<DecodedAudio> (earliest_decoded);
+ shared_ptr<DecodedImageSubtitle> dis = dynamic_pointer_cast<DecodedImageSubtitle> (earliest_decoded);
+ shared_ptr<DecodedTextSubtitle> dts = dynamic_pointer_cast<DecodedTextSubtitle> (earliest_decoded);
+
+ /* Will be set to false if we shouldn't consume the peeked DecodedThing */
+ bool consume = true;
+
+ if (dv && _video) {
+
+ if (_just_did_inaccurate_seek) {
+
+ /* Just emit; no subtlety */
+ emit_video (earliest_piece, dv);
+ step_video_position (dv);
+
+ } else if (dv->dcp_time > _video_position) {
+
+ /* Too far ahead */
+
+ list<shared_ptr<Piece> >::iterator i = _pieces.begin();
+ while (i != _pieces.end() && ((*i)->content->position() >= _video_position || _video_position >= (*i)->content->end())) {
+ ++i;
}
- shared_ptr<AudioDecoder> ad = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
- if (ad && ad->has_audio ()) {
- audio_done_up_to = min (audio_done_up_to.get_value_or (TIME_MAX), (*i)->audio_position);
+ if (i == _pieces.end() || !_last_incoming_video.video || !_have_valid_pieces) {
+ /* We're outside all video content */
+ emit_black ();
+ _statistics.video.black++;
+ } else {
+ /* We're inside some video; repeat the frame */
+ _last_incoming_video.video->dcp_time = _video_position;
+ emit_video (_last_incoming_video.weak_piece, _last_incoming_video.video);
+ step_video_position (_last_incoming_video.video);
+ _statistics.video.repeat++;
}
+
+ consume = false;
+
+ } else if (dv->dcp_time == _video_position) {
+ /* We're ok */
+ emit_video (earliest_piece, dv);
+ step_video_position (dv);
+ _statistics.video.good++;
+ } else {
+ /* Too far behind: skip */
+ _statistics.video.skip++;
}
- if (audio_done_up_to) {
- TimedAudioBuffers<Time> tb = _audio_merger.pull (audio_done_up_to.get ());
- Audio (tb.audio, tb.time);
- _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
+ _just_did_inaccurate_seek = false;
+
+ } else if (da && _audio) {
+
+ if (da->dcp_time > _audio_position) {
+ /* Too far ahead */
+ emit_silence (da->dcp_time - _audio_position);
+ consume = false;
+ _statistics.audio.silence += (da->dcp_time - _audio_position);
+ } else if (da->dcp_time == _audio_position) {
+ /* We're ok */
+ emit_audio (earliest_piece, da);
+ _statistics.audio.good += da->data->frames();
+ } else {
+ /* Too far behind: skip */
+ _statistics.audio.skip += da->data->frames();
}
- }
+ } else if (dis && _video) {
+ _image_subtitle.piece = earliest_piece;
+ _image_subtitle.subtitle = dis;
+ update_subtitle_from_image ();
+ } else if (dts && _video) {
+ _text_subtitle.piece = earliest_piece;
+ _text_subtitle.subtitle = dts;
+ update_subtitle_from_text ();
+ }
+
+ if (consume) {
+ earliest_piece->decoder->consume ();
+ }
+
return false;
}
-/** @param extra Amount of extra time to add to the content frame's time (for repeat) */
void
-Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, Eyes eyes, bool same, VideoContent::Frame frame, Time extra)
+Player::emit_video (weak_ptr<Piece> weak_piece, shared_ptr<DecodedVideo> video)
{
/* Keep a note of what came in so that we can repeat it if required */
_last_incoming_video.weak_piece = weak_piece;
- _last_incoming_video.image = image;
- _last_incoming_video.eyes = eyes;
- _last_incoming_video.same = same;
- _last_incoming_video.frame = frame;
- _last_incoming_video.extra = extra;
+ _last_incoming_video.video = video;
shared_ptr<Piece> piece = weak_piece.lock ();
if (!piece) {
shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
assert (content);
- FrameRateConversion frc (content->video_frame_rate(), _film->video_frame_rate());
- if (frc.skip && (frame % 2) == 1) {
- return;
- }
+ FrameRateChange frc (content->video_frame_rate(), _film->video_frame_rate());
- float const ratio = content->ratio() ? content->ratio()->ratio() : content->video_size_after_crop().ratio();
- dcp::Size image_size = fit_ratio_within (ratio, _video_container_size);
- Time const relative_time = (frame * frc.factor() * TIME_HZ / _film->video_frame_rate());
- if (content->trimmed (relative_time)) {
- return;
++ dcp::Size const image_size = content->scale().size (content, _video_container_size);
+ if (_approximate_size) {
+ image_size.width &= ~3;
+ image_size.height &= ~3;
}
- Time const time = content->position() + relative_time + extra - content->trim_start ();
- libdcp::Size const image_size = content->scale().size (content, _video_container_size);
-
shared_ptr<PlayerImage> pi (
new PlayerImage (
- image,
+ video->image,
content->crop(),
image_size,
_video_container_size,
)
);
- if (_film->with_subtitles () && _out_subtitle.image && time >= _out_subtitle.from && time <= _out_subtitle.to) {
+ if (
+ _film->with_subtitles () &&
+ _out_subtitle.image &&
+ video->dcp_time >= _out_subtitle.from && video->dcp_time <= _out_subtitle.to
+ ) {
Position<int> const container_offset (
(_video_container_size.width - image_size.width) / 2,
- (_video_container_size.height - image_size.width) / 2
+ (_video_container_size.height - image_size.height) / 2
);
pi->set_subtitle (_out_subtitle.image, _out_subtitle.position + container_offset);
_last_video = piece->content;
#endif
- Video (pi, eyes, content->colour_conversion(), same, time);
-
+ Video (pi, video->eyes, content->colour_conversion(), video->same, video->dcp_time);
+
_last_emit_was_black = false;
- _video_position = piece->video_position = (time + TIME_HZ / _film->video_frame_rate());
+}
- if (frc.repeat > 1 && !piece->repeating ()) {
- piece->set_repeat (_last_incoming_video, frc.repeat - 1);
+void
+Player::step_video_position (shared_ptr<DecodedVideo> video)
+{
+ /* This is a bit of a hack; don't update _video_position if EYES_RIGHT is on its way */
+ if (video->eyes != EYES_LEFT) {
+ /* This assumes that the video-frames-to-time conversion is exact
+ so that there are no accumulated errors caused by rounding.
+ */
+ _video_position += DCPTime::from_frames (1, _film->video_frame_rate ());
}
}
void
-Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers> audio, AudioContent::Frame frame)
+Player::emit_audio (weak_ptr<Piece> weak_piece, shared_ptr<DecodedAudio> audio)
{
shared_ptr<Piece> piece = weak_piece.lock ();
if (!piece) {
/* Gain */
if (content->audio_gain() != 0) {
- shared_ptr<AudioBuffers> gain (new AudioBuffers (audio));
+ shared_ptr<AudioBuffers> gain (new AudioBuffers (audio->data));
gain->apply_gain (content->audio_gain ());
- audio = gain;
- }
-
- /* Resample */
- if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
- shared_ptr<Resampler> r = resampler (content, true);
- pair<shared_ptr<const AudioBuffers>, AudioContent::Frame> ro = r->run (audio, frame);
- audio = ro.first;
- frame = ro.second;
- }
-
- Time const relative_time = _film->audio_frames_to_time (frame);
-
- if (content->trimmed (relative_time)) {
- return;
+ audio->data = gain;
}
- Time time = content->position() + (content->audio_delay() * TIME_HZ / 1000) + relative_time - content->trim_start ();
-
/* Remap channels */
- shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames()));
+ shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->data->frames()));
dcp_mapped->make_silent ();
-
AudioMapping map = content->audio_mapping ();
for (int i = 0; i < map.content_channels(); ++i) {
for (int j = 0; j < _film->audio_channels(); ++j) {
- if (map.get (i, static_cast<libdcp::Channel> (j)) > 0) {
+ if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
dcp_mapped->accumulate_channel (
- audio.get(),
+ audio->data.get(),
i,
- static_cast<libdcp::Channel> (j),
- map.get (i, static_cast<libdcp::Channel> (j))
+ static_cast<dcp::Channel> (j),
+ map.get (i, static_cast<dcp::Channel> (j))
);
}
}
}
- audio = dcp_mapped;
+ audio->data = dcp_mapped;
- /* We must cut off anything that comes before the start of all time */
- if (time < 0) {
- int const frames = - time * _film->audio_frame_rate() / TIME_HZ;
- if (frames >= audio->frames ()) {
+ /* Delay */
+ audio->dcp_time += DCPTime::from_seconds (content->audio_delay() / 1000.0);
+ if (audio->dcp_time < DCPTime (0)) {
+ int const frames = - audio->dcp_time.frames (_film->audio_frame_rate());
+ if (frames >= audio->data->frames ()) {
return;
}
- shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->channels(), audio->frames() - frames));
- trimmed->copy_from (audio.get(), audio->frames() - frames, frames, 0);
+ shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->data->channels(), audio->data->frames() - frames));
+ trimmed->copy_from (audio->data.get(), audio->data->frames() - frames, frames, 0);
- audio = trimmed;
- time = 0;
+ audio->data = trimmed;
+ audio->dcp_time = DCPTime ();
}
- _audio_merger.push (audio, time);
- piece->audio_position += _film->audio_frames_to_time (audio->frames ());
+ _audio_merger.push (audio->data, audio->dcp_time);
}
void
Player::flush ()
{
- TimedAudioBuffers<Time> tb = _audio_merger.flush ();
+ TimedAudioBuffers<DCPTime> tb = _audio_merger.flush ();
if (_audio && tb.audio) {
Audio (tb.audio, tb.time);
- _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
+ _audio_position += DCPTime::from_frames (tb.audio->frames (), _film->audio_frame_rate ());
}
while (_video && _video_position < _audio_position) {
}
while (_audio && _audio_position < _video_position) {
- emit_silence (_film->time_to_audio_frames (_video_position - _audio_position));
+ emit_silence (_video_position - _audio_position);
}
-
}
/** Seek so that the next pass() will yield (approximately) the requested frame.
* @return true on error
*/
void
-Player::seek (Time t, bool accurate)
+Player::seek (DCPTime t, bool accurate)
{
if (!_have_valid_pieces) {
setup_pieces ();
}
for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
- shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*i)->content);
- if (!vc) {
- continue;
- }
-
/* s is the offset of t from the start position of this content */
- Time s = t - vc->position ();
- s = max (static_cast<Time> (0), s);
- s = min (vc->length_after_trim(), s);
+ DCPTime s = t - (*i)->content->position ();
+ s = max (static_cast<DCPTime> (0), s);
+ s = min ((*i)->content->length_after_trim(), s);
- /* Hence set the piece positions to the `global' time */
- (*i)->video_position = (*i)->audio_position = vc->position() + s;
+ /* Convert this to the content time */
+ ContentTime ct (s + (*i)->content->trim_start(), (*i)->frc);
/* And seek the decoder */
- dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (
- vc->time_to_content_video_frames (s + vc->trim_start ()), accurate
- );
-
- (*i)->reset_repeat ();
+ (*i)->decoder->seek (ct, accurate);
}
- _video_position = _audio_position = t;
+ _video_position = t.round_up (_film->video_frame_rate());
+ _audio_position = t.round_up (_film->audio_frame_rate());
+
+ _audio_merger.clear (_audio_position);
- /* XXX: don't seek audio because we don't need to... */
+ if (!accurate) {
+ /* We just did an inaccurate seek, so it's likely that the next thing seen
+ out of pass() will be a fair distance from _{video,audio}_position. Setting
+ this flag stops pass() from trying to fix that: we assume that if it
+ was an inaccurate seek then the caller does not care too much about
+ inserting black/silence to keep the time tidy.
+ */
+ _just_did_inaccurate_seek = true;
+ }
}
void
Player::setup_pieces ()
{
list<shared_ptr<Piece> > old_pieces = _pieces;
-
_pieces.clear ();
ContentList content = _playlist->content ();
- sort (content.begin(), content.end(), ContentSorter ());
for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
if (!(*i)->paths_valid ()) {
continue;
}
+
+ shared_ptr<Decoder> decoder;
+ optional<FrameRateChange> frc;
+
+ /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
+ DCPTime best_overlap_t;
+ shared_ptr<VideoContent> best_overlap;
+ for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
+ shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
+ if (!vc) {
+ continue;
+ }
+
+ DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
+ if (overlap > best_overlap_t) {
+ best_overlap = vc;
+ best_overlap_t = overlap;
+ }
+ }
- shared_ptr<Piece> piece (new Piece (*i));
-
- /* XXX: into content? */
+ optional<FrameRateChange> best_overlap_frc;
+ if (best_overlap) {
+ best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
+ } else {
+ /* No video overlap; e.g. if the DCP is just audio */
+ best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
+ }
+ /* FFmpeg */
shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
if (fc) {
- shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio));
-
- fd->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, 0));
- fd->Audio.connect (bind (&Player::process_audio, this, weak_ptr<Piece> (piece), _1, _2));
- fd->Subtitle.connect (bind (&Player::process_subtitle, this, weak_ptr<Piece> (piece), _1, _2, _3, _4));
-
- fd->seek (fc->time_to_content_video_frames (fc->trim_start ()), true);
- piece->decoder = fd;
+ decoder.reset (new FFmpegDecoder (_film, fc, _video, _audio));
+ frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
}
-
+
+ /* ImageContent */
shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
if (ic) {
- bool reusing = false;
-
/* See if we can re-use an old ImageDecoder */
for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
if (imd && imd->content() == ic) {
- piece = *j;
- reusing = true;
+ decoder = imd;
}
}
- if (!reusing) {
- shared_ptr<ImageDecoder> id (new ImageDecoder (_film, ic));
- id->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, 0));
- piece->decoder = id;
+ if (!decoder) {
+ decoder.reset (new ImageDecoder (_film, ic));
}
+
+ frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
}
+ /* SndfileContent */
shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
if (sc) {
- shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
- sd->Audio.connect (bind (&Player::process_audio, this, weak_ptr<Piece> (piece), _1, _2));
+ decoder.reset (new SndfileDecoder (_film, sc));
+ frc = best_overlap_frc;
+ }
- piece->decoder = sd;
+ /* SubRipContent */
+ shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
+ if (rc) {
+ decoder.reset (new SubRipDecoder (_film, rc));
+ frc = best_overlap_frc;
}
- _pieces.push_back (piece);
+ ContentTime st ((*i)->trim_start(), frc.get ());
+ decoder->seek (st, true);
+
+ _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
}
_have_valid_pieces = true;
+
+ /* The Piece for the _last_incoming_video will no longer be valid */
+ _last_incoming_video.video.reset ();
+
+ _video_position = DCPTime ();
+ _audio_position = DCPTime ();
}
void
property == SubtitleContentProperty::SUBTITLE_SCALE
) {
- update_subtitle ();
+ update_subtitle_from_image ();
+ update_subtitle_from_text ();
Changed (frequent);
} else if (
- property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO ||
+ property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_SCALE ||
property == VideoContentProperty::VIDEO_FRAME_RATE
) {
}
void
-Player::set_video_container_size (libdcp::Size s)
+Player::set_video_container_size (dcp::Size s)
{
_video_container_size = s;
);
}
-shared_ptr<Resampler>
-Player::resampler (shared_ptr<AudioContent> c, bool create)
-{
- map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
- if (i != _resamplers.end ()) {
- return i->second;
- }
-
- if (!create) {
- return shared_ptr<Resampler> ();
- }
-
- _film->log()->log (
- String::compose (
- "Creating new resampler for %1 to %2 with %3 channels", c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()
- )
- );
-
- shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()));
- _resamplers[c] = r;
- return r;
-}
-
void
Player::emit_black ()
{
#endif
Video (_black_frame, EYES_BOTH, ColourConversion(), _last_emit_was_black, _video_position);
- _video_position += _film->video_frames_to_time (1);
+ _video_position += DCPTime::from_frames (1, _film->video_frame_rate ());
_last_emit_was_black = true;
}
void
-Player::emit_silence (OutputAudioFrame most)
+Player::emit_silence (DCPTime most)
{
if (most == 0) {
return;
}
- OutputAudioFrame N = min (most, _film->audio_frame_rate() / 2);
- shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), N));
+ DCPTime t = min (most, DCPTime::from_seconds (0.5));
+ shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), t.frames (_film->audio_frame_rate())));
silence->make_silent ();
Audio (silence, _audio_position);
- _audio_position += _film->audio_frames_to_time (N);
+
+ _audio_position += t;
}
void
}
void
-Player::process_subtitle (weak_ptr<Piece> weak_piece, shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to)
-{
- _in_subtitle.piece = weak_piece;
- _in_subtitle.image = image;
- _in_subtitle.rect = rect;
- _in_subtitle.from = from;
- _in_subtitle.to = to;
-
- update_subtitle ();
-}
-
-void
-Player::update_subtitle ()
+Player::update_subtitle_from_image ()
{
- shared_ptr<Piece> piece = _in_subtitle.piece.lock ();
+ shared_ptr<Piece> piece = _image_subtitle.piece.lock ();
if (!piece) {
return;
}
- if (!_in_subtitle.image) {
+ if (!_image_subtitle.subtitle->image) {
_out_subtitle.image.reset ();
return;
}
shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content);
assert (sc);
- dcpomatic::Rect<double> in_rect = _in_subtitle.rect;
- libdcp::Size scaled_size;
+ dcpomatic::Rect<double> in_rect = _image_subtitle.subtitle->rect;
+ dcp::Size scaled_size;
in_rect.x += sc->subtitle_x_offset ();
in_rect.y += sc->subtitle_y_offset ();
_out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2)));
_out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
- _out_subtitle.image = _in_subtitle.image->scale (
+ _out_subtitle.image = _image_subtitle.subtitle->image->scale (
scaled_size,
Scaler::from_id ("bicubic"),
- _in_subtitle.image->pixel_format (),
+ _image_subtitle.subtitle->image->pixel_format (),
true
);
-
- /* XXX: hack */
- Time from = _in_subtitle.from;
- Time to = _in_subtitle.to;
- shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (piece->content);
- if (vc) {
- from = rint (from * vc->video_frame_rate() / _film->video_frame_rate());
- to = rint (to * vc->video_frame_rate() / _film->video_frame_rate());
- }
- _out_subtitle.from = from + piece->content->position ();
- _out_subtitle.to = to + piece->content->position ();
+ _out_subtitle.from = _image_subtitle.subtitle->dcp_time + piece->content->position ();
+ _out_subtitle.to = _image_subtitle.subtitle->dcp_time_to + piece->content->position ();
}
/** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
bool
Player::repeat_last_video ()
{
- if (!_last_incoming_video.image || !_have_valid_pieces) {
+ if (!_last_incoming_video.video || !_have_valid_pieces) {
return false;
}
- process_video (
+ emit_video (
_last_incoming_video.weak_piece,
- _last_incoming_video.image,
- _last_incoming_video.eyes,
- _last_incoming_video.same,
- _last_incoming_video.frame,
- _last_incoming_video.extra
+ _last_incoming_video.video
);
return true;
}
+void
+Player::update_subtitle_from_text ()
+{
+ if (_text_subtitle.subtitle->subs.empty ()) {
+ _out_subtitle.image.reset ();
+ return;
+ }
+
+ render_subtitles (_text_subtitle.subtitle->subs, _video_container_size, _out_subtitle.image, _out_subtitle.position);
+}
+
+void
+Player::set_approximate_size ()
+{
+ _approximate_size = true;
+}
+
PlayerImage::PlayerImage (
shared_ptr<const Image> in,
Crop crop,
- libdcp::Size inter_size,
- libdcp::Size out_size,
+ dcp::Size inter_size,
+ dcp::Size out_size,
Scaler const * scaler
)
: _in (in)
}
shared_ptr<Image>
-PlayerImage::image ()
+PlayerImage::image (AVPixelFormat format, bool aligned)
{
- shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, PIX_FMT_RGB24, false);
-
+ shared_ptr<Image> out = _in->crop_scale_window (_crop, _inter_size, _out_size, _scaler, format, aligned);
+
Position<int> const container_offset ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.width) / 2);
if (_subtitle_image) {
return out;
}
+
+void
+PlayerStatistics::dump (shared_ptr<Log> log) const
+{
+ log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat));
+ log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence));
+}
+
+PlayerStatistics const &
+Player::statistics () const
+{
+ return _statistics;
+}
#include <openssl/md5.h>
#include <magick/MagickCore.h>
#include <magick/version.h>
-#include <libdcp/version.h>
-#include <libdcp/util.h>
-#include <libdcp/signer_chain.h>
-#include <libdcp/signer.h>
+#include <pangomm/init.h>
+#include <dcp/version.h>
+#include <dcp/util.h>
+#include <dcp/signer_chain.h>
+#include <dcp/signer.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include "ratio.h"
#include "job.h"
#include "cross.h"
+ #include "video_content.h"
#ifdef DCPOMATIC_WINDOWS
#include "stack.hpp"
#endif
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 ();
+ VideoContentScale::setup_scales ();
DCPContentType::setup_dcp_content_types ();
Scaler::setup_scalers ();
Filter::setup_filters ();
return s.str ();
}
-static bool
-about_equal (float a, float b)
-{
- /* A film of F seconds at f FPS will be Ff frames;
- Consider some delta FPS d, so if we run the same
- film at (f + d) FPS it will last F(f + d) seconds.
-
- Hence the difference in length over the length of the film will
- be F(f + d) - Ff frames
- = Ff + Fd - Ff frames
- = Fd frames
- = Fd/f seconds
-
- So if we accept a difference of 1 frame, ie 1/f seconds, we can
- say that
-
- 1/f = Fd/f
- ie 1 = Fd
- ie d = 1/F
-
- So for a 3hr film, ie F = 3 * 60 * 60 = 10800, the acceptable
- FPS error is 1/F ~= 0.0001 ~= 10-e4
- */
-
- return (fabs (a - b) < 1e-4);
-}
-
/** @param An arbitrary audio frame rate.
* @return The appropriate DCP-approved frame rate (48kHz or 96kHz).
*/
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)
{
return channels[c];
}
-FrameRateConversion::FrameRateConversion (float source, int dcp)
- : skip (false)
- , repeat (1)
- , change_speed (false)
-{
- if (fabs (source / 2.0 - dcp) < fabs (source - dcp)) {
- /* The difference between source and DCP frame rate will be lower
- (i.e. better) if we skip.
- */
- skip = true;
- } else if (fabs (source * 2 - dcp) < fabs (source - dcp)) {
- /* The difference between source and DCP frame rate would be better
- if we repeated each frame once; it may be better still if we
- repeated more than once. Work out the required repeat.
- */
- repeat = round (dcp / source);
- }
-
- change_speed = !about_equal (source * factor(), dcp);
-
- if (!skip && repeat == 1 && !change_speed) {
- description = _("Content and DCP have the same rate.\n");
- } else {
- if (skip) {
- description = _("DCP will use every other frame of the content.\n");
- } else if (repeat == 2) {
- description = _("Each content frame will be doubled in the DCP.\n");
- } else if (repeat > 2) {
- description = String::compose (_("Each content frame will be repeated %1 more times in the DCP.\n"), repeat - 1);
- }
-
- if (change_speed) {
- float const pc = dcp * 100 / (source * factor());
- description += String::compose (_("DCP will run at %1%% of the content speed.\n"), pc);
- }
- }
-}
-
LocaleGuard::LocaleGuard ()
: _old (0)
{
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));
}
map<string, string>
return r;
}
-libdcp::Size
-fit_ratio_within (float ratio, libdcp::Size full_frame)
+dcp::Size
+fit_ratio_within (float ratio, dcp::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));
}
void *
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-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
#include <iomanip>
#include <libcxml/cxml.h>
-#include <libdcp/colour_matrix.h>
+#include <dcp/colour_matrix.h>
#include "video_content.h"
#include "video_examiner.h"
- #include "ratio.h"
#include "compose.hpp"
+ #include "ratio.h"
#include "config.h"
#include "colour_conversion.h"
#include "util.h"
int const VideoContentProperty::VIDEO_FRAME_RATE = 1;
int const VideoContentProperty::VIDEO_FRAME_TYPE = 2;
int const VideoContentProperty::VIDEO_CROP = 3;
- int const VideoContentProperty::VIDEO_RATIO = 4;
+ int const VideoContentProperty::VIDEO_SCALE = 4;
int const VideoContentProperty::COLOUR_CONVERSION = 5;
using std::string;
using boost::optional;
using boost::dynamic_pointer_cast;
+ vector<VideoContentScale> VideoContentScale::_scales;
+
VideoContent::VideoContent (shared_ptr<const Film> f)
: Content (f)
, _video_length (0)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
- , _ratio (Ratio::from_id ("185"))
+ , _scale (Ratio::from_id ("185"))
{
setup_default_colour_conversion ();
}
-VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Frame len)
+VideoContent::VideoContent (shared_ptr<const Film> f, DCPTime s, ContentTime len)
: Content (f, s)
, _video_length (len)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
- , _ratio (Ratio::from_id ("185"))
+ , _scale (Ratio::from_id ("185"))
{
setup_default_colour_conversion ();
}
, _video_length (0)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
- , _ratio (Ratio::from_id ("185"))
+ , _scale (Ratio::from_id ("185"))
{
setup_default_colour_conversion ();
}
- VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
+ VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
: Content (f, node)
- , _ratio (0)
{
- _video_length = node->number_child<VideoContent::Frame> ("VideoLength");
+ _video_length = ContentTime (node->number_child<int64_t> ("VideoLength"));
_video_size.width = node->number_child<int> ("VideoWidth");
_video_size.height = node->number_child<int> ("VideoHeight");
_video_frame_rate = node->number_child<float> ("VideoFrameRate");
_crop.right = node->number_child<int> ("RightCrop");
_crop.top = node->number_child<int> ("TopCrop");
_crop.bottom = node->number_child<int> ("BottomCrop");
- optional<string> r = node->optional_string_child ("Ratio");
- if (r) {
- _ratio = Ratio::from_id (r.get ());
+
+ if (version <= 7) {
+ optional<string> r = node->optional_string_child ("Ratio");
+ if (r) {
+ _scale = VideoContentScale (Ratio::from_id (r.get ()));
+ }
+ } else {
+ _scale = VideoContentScale (node->node_child ("Scale"));
}
+
_colour_conversion = ColourConversion (node->node_child ("ColourConversion"));
}
throw JoinError (_("Content to be joined must have the same crop."));
}
- if (vc->ratio() != ref->ratio()) {
- throw JoinError (_("Content to be joined must have the same ratio."));
+ if (vc->scale() != ref->scale()) {
+ throw JoinError (_("Content to be joined must have the same scale setting."));
}
if (vc->colour_conversion() != ref->colour_conversion()) {
_video_frame_rate = ref->video_frame_rate ();
_video_frame_type = ref->video_frame_type ();
_crop = ref->crop ();
- _ratio = ref->ratio ();
+ _scale = ref->scale ();
_colour_conversion = ref->colour_conversion ();
}
node->add_child("RightCrop")->add_child_text (boost::lexical_cast<string> (_crop.right));
node->add_child("TopCrop")->add_child_text (boost::lexical_cast<string> (_crop.top));
node->add_child("BottomCrop")->add_child_text (boost::lexical_cast<string> (_crop.bottom));
- if (_ratio) {
- node->add_child("Ratio")->add_child_text (_ratio->id ());
- }
+ _scale.as_xml (node->add_child("Scale"));
_colour_conversion.as_xml (node->add_child("ColourConversion"));
}
void
VideoContent::setup_default_colour_conversion ()
{
- _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, libdcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
+ _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, dcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
}
void
VideoContent::take_from_video_examiner (shared_ptr<VideoExaminer> d)
{
/* These examiner calls could call other content methods which take a lock on the mutex */
- libdcp::Size const vs = d->video_size ();
+ dcp::Size const vs = d->video_size ();
float const vfr = d->video_frame_rate ();
{
}
void
- VideoContent::set_ratio (Ratio const * r)
+ VideoContent::set_scale (VideoContentScale s)
{
{
boost::mutex::scoped_lock lm (_mutex);
- if (_ratio == r) {
+ if (_scale == s) {
return;
}
- _ratio = r;
+ _scale = s;
}
- signal_changed (VideoContentProperty::VIDEO_RATIO);
+ signal_changed (VideoContentProperty::VIDEO_SCALE);
}
/** @return string which includes everything about how this content looks */
<< "_" << crop().right
<< "_" << crop().top
<< "_" << crop().bottom
+ << "_" << scale().id()
<< "_" << colour_conversion().identifier ();
- if (ratio()) {
- s << "_" << ratio()->id ();
- }
-
return s.str ();
}
return String::compose ("video: length %1, size %2x%3, rate %4", video_length(), video_size().width, video_size().height, video_frame_rate());
}
-libdcp::Size
+dcp::Size
VideoContent::video_size_after_3d_split () const
{
- libdcp::Size const s = video_size ();
+ dcp::Size const s = video_size ();
switch (video_frame_type ()) {
case VIDEO_FRAME_TYPE_2D:
return s;
case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
- return libdcp::Size (s.width / 2, s.height);
+ return dcp::Size (s.width / 2, s.height);
case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
- return libdcp::Size (s.width, s.height / 2);
+ return dcp::Size (s.width, s.height / 2);
}
assert (false);
}
/** @return Video size after 3D split and crop */
-libdcp::Size
+dcp::Size
VideoContent::video_size_after_crop () const
{
return crop().apply (video_size_after_3d_split ());
}
/** @param t A time offset from the start of this piece of content.
- * @return Corresponding frame index.
+ * @return Corresponding time with respect to the content.
*/
-VideoContent::Frame
-VideoContent::time_to_content_video_frames (Time t) const
+ContentTime
+VideoContent::dcp_time_to_content_time (DCPTime t) const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- FrameRateConversion frc (video_frame_rate(), film->video_frame_rate());
-
- /* Here we are converting from time (in the DCP) to a frame number in the content.
- Hence we need to use the DCP's frame rate and the double/skip correction, not
- the source's rate.
- */
- return t * film->video_frame_rate() / (frc.factor() * TIME_HZ);
+ return ContentTime (t, FrameRateChange (video_frame_rate(), film->video_frame_rate()));
}
+
+ VideoContentScale::VideoContentScale (Ratio const * r)
+ : _ratio (r)
+ , _scale (true)
+ {
+
+ }
+
+ VideoContentScale::VideoContentScale ()
+ : _ratio (0)
+ , _scale (false)
+ {
+
+ }
+
+ VideoContentScale::VideoContentScale (bool scale)
+ : _ratio (0)
+ , _scale (scale)
+ {
+
+ }
+
+ VideoContentScale::VideoContentScale (shared_ptr<cxml::Node> node)
+ : _ratio (0)
+ , _scale (true)
+ {
+ optional<string> r = node->optional_string_child ("Ratio");
+ if (r) {
+ _ratio = Ratio::from_id (r.get ());
+ } else {
+ _scale = node->bool_child ("Scale");
+ }
+ }
+
+ void
+ VideoContentScale::as_xml (xmlpp::Node* node) const
+ {
+ if (_ratio) {
+ node->add_child("Ratio")->add_child_text (_ratio->id ());
+ } else {
+ node->add_child("Scale")->add_child_text (_scale ? "1" : "0");
+ }
+ }
+
+ string
+ VideoContentScale::id () const
+ {
+ stringstream s;
+
+ if (_ratio) {
+ s << _ratio->id () << "_";
+ } else {
+ s << (_scale ? "S1" : "S0");
+ }
+
+ return s.str ();
+ }
+
+ string
+ VideoContentScale::name () const
+ {
+ if (_ratio) {
+ return _ratio->nickname ();
+ }
+
+ if (_scale) {
+ return _("No stretch");
+ }
+
+ return _("No scale");
+ }
+
+ libdcp::Size
+ VideoContentScale::size (shared_ptr<const VideoContent> c, libdcp::Size container) const
+ {
+ if (_ratio) {
+ return fit_ratio_within (_ratio->ratio (), container);
+ }
+
+ /* Force scale if the container is smaller than the content's image */
+ if (_scale || container.width < c->video_size().width || container.height < c->video_size().height) {
+ return fit_ratio_within (c->video_size().ratio (), container);
+ }
+
+ return c->video_size ();
+ }
+
+ void
+ VideoContentScale::setup_scales ()
+ {
+ vector<Ratio const *> ratios = Ratio::all ();
+ for (vector<Ratio const *>::const_iterator i = ratios.begin(); i != ratios.end(); ++i) {
+ _scales.push_back (VideoContentScale (*i));
+ }
+
+ _scales.push_back (VideoContentScale (true));
+ _scales.push_back (VideoContentScale (false));
+ }
+
+ bool
+ operator== (VideoContentScale const & a, VideoContentScale const & b)
+ {
+ return (a.ratio() == b.ratio() && a.scale() == b.scale());
+ }
+
+ bool
+ operator!= (VideoContentScale const & a, VideoContentScale const & b)
+ {
+ return (a.ratio() != b.ratio() || a.scale() != b.scale());
+ }
static int const VIDEO_FRAME_RATE;
static int const VIDEO_FRAME_TYPE;
static int const VIDEO_CROP;
- static int const VIDEO_RATIO;
+ static int const VIDEO_SCALE;
static int const COLOUR_CONVERSION;
};
+ class VideoContentScale
+ {
+ public:
+ VideoContentScale ();
+ VideoContentScale (Ratio const *);
+ VideoContentScale (bool);
+ VideoContentScale (boost::shared_ptr<cxml::Node>);
+
+ libdcp::Size size (boost::shared_ptr<const VideoContent>, libdcp::Size) const;
+ std::string id () const;
+ std::string name () const;
+ void as_xml (xmlpp::Node *) const;
+
+ Ratio const * ratio () const {
+ return _ratio;
+ }
+
+ bool scale () const {
+ return _scale;
+ }
+
+ static void setup_scales ();
+ static std::vector<VideoContentScale> all () {
+ return _scales;
+ }
+
+ private:
+ Ratio const * _ratio;
+ bool _scale;
+
+ static std::vector<VideoContentScale> _scales;
+ };
+
+ bool operator== (VideoContentScale const & a, VideoContentScale const & b);
+ bool operator!= (VideoContentScale const & a, VideoContentScale const & b);
+
class VideoContent : public virtual Content
{
public:
typedef int Frame;
VideoContent (boost::shared_ptr<const Film>);
- VideoContent (boost::shared_ptr<const Film>, Time, VideoContent::Frame);
+ VideoContent (boost::shared_ptr<const Film>, DCPTime, ContentTime);
VideoContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- VideoContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
+ VideoContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int);
VideoContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
void as_xml (xmlpp::Node *) const;
virtual std::string information () const;
virtual std::string identifier () const;
- VideoContent::Frame video_length () const {
+ ContentTime video_length () const {
boost::mutex::scoped_lock lm (_mutex);
return _video_length;
}
- libdcp::Size video_size () const {
+ dcp::Size video_size () const {
boost::mutex::scoped_lock lm (_mutex);
return _video_size;
}
void set_top_crop (int);
void set_bottom_crop (int);
+ void set_scale (VideoContentScale);
void set_colour_conversion (ColourConversion);
-
+
VideoFrameType video_frame_type () const {
boost::mutex::scoped_lock lm (_mutex);
return _video_frame_type;
boost::mutex::scoped_lock lm (_mutex);
return _crop.bottom;
}
-
- void set_ratio (Ratio const *);
- /** @return ratio to scale to, or 0 if the content's own ratio should be preserved. */
- Ratio const * ratio () const {
+ /** @return Description of how to scale this content (if indeed it should be scaled) */
+ VideoContentScale scale () const {
boost::mutex::scoped_lock lm (_mutex);
- return _ratio;
+ return _scale;
}
ColourConversion colour_conversion () const {
return _colour_conversion;
}
- libdcp::Size video_size_after_3d_split () const;
- libdcp::Size video_size_after_crop () const;
+ dcp::Size video_size_after_3d_split () const;
+ dcp::Size video_size_after_crop () const;
- VideoContent::Frame time_to_content_video_frames (Time) const;
+ ContentTime dcp_time_to_content_time (DCPTime) const;
protected:
void take_from_video_examiner (boost::shared_ptr<VideoExaminer>);
- VideoContent::Frame _video_length;
+ ContentTime _video_length;
float _video_frame_rate;
private:
void setup_default_colour_conversion ();
- libdcp::Size _video_size;
+ dcp::Size _video_size;
VideoFrameType _video_frame_type;
Crop _crop;
- Ratio const * _ratio;
+ VideoContentScale _scale;
ColourConversion _colour_conversion;
};
/*
- Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-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
*/
#include <wx/spinctrl.h>
- #include "lib/ratio.h"
#include "lib/filter.h"
#include "lib/ffmpeg_content.h"
#include "lib/colour_conversion.h"
#include "lib/config.h"
#include "lib/util.h"
+ #include "lib/ratio.h"
#include "filter_dialog.h"
#include "video_panel.h"
#include "wx_util.h"
using boost::bind;
using boost::optional;
- static Ratio const *
- index_to_ratio (int n)
+ static VideoContentScale
+ index_to_scale (int n)
{
+ vector<VideoContentScale> scales = VideoContentScale::all ();
assert (n >= 0);
-
- vector<Ratio const *> ratios = Ratio::all ();
- if (n >= int (ratios.size ())) {
- return 0;
- }
-
- return ratios[n];
+ assert (n < int (scales.size ()));
+ return scales[n];
}
static int
- ratio_to_index (Ratio const * r)
+ scale_to_index (VideoContentScale scale)
{
- vector<Ratio const *> ratios = Ratio::all ();
- size_t i = 0;
- while (i < ratios.size() && ratios[i] != r) {
- ++i;
+ vector<VideoContentScale> scales = VideoContentScale::all ();
+ for (size_t i = 0; i < scales.size(); ++i) {
+ if (scales[i] == scale) {
+ return i;
+ }
}
- return i;
+ assert (false);
}
VideoPanel::VideoPanel (FilmEditor* e)
++r;
add_label_to_grid_bag_sizer (grid, this, _("Scale to"), true, wxGBPosition (r, 0));
- _ratio = new ContentChoice<VideoContent, Ratio const *> (
+ _scale = new ContentChoice<VideoContent, VideoContentScale> (
this,
new wxChoice (this, wxID_ANY),
- VideoContentProperty::VIDEO_RATIO,
- boost::mem_fn (&VideoContent::ratio),
- boost::mem_fn (&VideoContent::set_ratio),
- &index_to_ratio,
- &ratio_to_index
+ VideoContentProperty::VIDEO_SCALE,
+ boost::mem_fn (&VideoContent::scale),
+ boost::mem_fn (&VideoContent::set_scale),
+ &index_to_scale,
+ &scale_to_index
);
- _ratio->add (grid, wxGBPosition (r, 1));
+ _scale->add (grid, wxGBPosition (r, 1));
++r;
{
_right_crop->wrapped()->SetRange (0, 1024);
_bottom_crop->wrapped()->SetRange (0, 1024);
- vector<Ratio const *> ratios = Ratio::all ();
- _ratio->wrapped()->Clear ();
- for (vector<Ratio const *>::iterator i = ratios.begin(); i != ratios.end(); ++i) {
- _ratio->wrapped()->Append (std_to_wx ((*i)->nickname ()));
+ vector<VideoContentScale> scales = VideoContentScale::all ();
+ _scale->wrapped()->Clear ();
+ for (vector<VideoContentScale>::iterator i = scales.begin(); i != scales.end(); ++i) {
+ _scale->wrapped()->Append (std_to_wx (i->name ()));
}
- _ratio->wrapped()->Append (_("No stretch"));
_frame_type->wrapped()->Append (_("2D"));
_frame_type->wrapped()->Append (_("3D left/right"));
setup_description ();
} else if (property == VideoContentProperty::VIDEO_CROP) {
setup_description ();
- } else if (property == VideoContentProperty::VIDEO_RATIO) {
+ } else if (property == VideoContentProperty::VIDEO_SCALE) {
setup_description ();
} else if (property == VideoContentProperty::VIDEO_FRAME_RATE) {
setup_description ();
}
Crop const crop = vcs->crop ();
- if ((crop.left || crop.right || crop.top || crop.bottom) && vcs->video_size() != libdcp::Size (0, 0)) {
- libdcp::Size cropped = vcs->video_size_after_crop ();
+ if ((crop.left || crop.right || crop.top || crop.bottom) && vcs->video_size() != dcp::Size (0, 0)) {
+ dcp::Size cropped = vcs->video_size_after_crop ();
d << wxString::Format (
_("Cropped to %dx%d (%.2f:1)\n"),
cropped.width, cropped.height,
++lines;
}
- Ratio const * ratio = vcs->ratio ();
- dcp::Size container_size = fit_ratio_within (_editor->film()->container()->ratio (), _editor->film()->full_frame ());
- float const ratio_value = ratio ? ratio->ratio() : vcs->video_size_after_crop().ratio ();
- libdcp::Size const container_size = fit_ratio_within (_editor->film()->container()->ratio (), _editor->film()->full_frame ());
- libdcp::Size const scaled = vcs->scale().size (vcs, container_size);
++ dcp::Size const container_size = fit_ratio_within (_editor->film()->container()->ratio (), _editor->film()->full_frame ());
++ dcp::Size const scaled = vcs->scale().size (vcs, container_size);
- /* We have a specified ratio to scale to */
- dcp::Size const scaled = fit_ratio_within (ratio_value, container_size);
-
- d << wxString::Format (
- _("Scaled to %dx%d (%.2f:1)\n"),
- scaled.width, scaled.height,
- scaled.ratio ()
- );
- ++lines;
+ if (scaled != vcs->video_size_after_crop ()) {
+ d << wxString::Format (
+ _("Scaled to %dx%d (%.2f:1)\n"),
+ scaled.width, scaled.height,
+ scaled.ratio ()
+ );
+ ++lines;
+ }
if (scaled != container_size) {
d << wxString::Format (
d << wxString::Format (_("Content frame rate %.4f\n"), vcs->video_frame_rate ());
++lines;
- FrameRateConversion frc (vcs->video_frame_rate(), _editor->film()->video_frame_rate ());
+ FrameRateChange frc (vcs->video_frame_rate(), _editor->film()->video_frame_rate ());
d << std_to_wx (frc.description) << "\n";
++lines;
_top_crop->set_content (sel);
_bottom_crop->set_content (sel);
_frame_type->set_content (sel);
- _ratio->set_content (sel);
+ _scale->set_content (sel);
/* Things that are only allowed with single selections */
_filters_button->Enable (single);
film->set_container (Ratio::from_id ("185"));
film->set_sequence_video (false);
shared_ptr<ImageContent> contentA (new ImageContent (film, "test/data/simple_testcard_640x480.png"));
- contentA->set_ratio (Ratio::from_id ("185"));
+ contentA->set_scale (VideoContentScale (Ratio::from_id ("185")));
shared_ptr<ImageContent> contentB (new ImageContent (film, "test/data/simple_testcard_640x480.png"));
- contentB->set_ratio (Ratio::from_id ("185"));
+ contentB->set_scale (VideoContentScale (Ratio::from_id ("185")));
film->examine_and_add_content (contentA);
film->examine_and_add_content (contentB);
wait_for_jobs ();
- contentA->set_video_length (3);
- contentA->set_position (film->video_frames_to_time (2));
- contentB->set_video_length (1);
- contentB->set_position (film->video_frames_to_time (7));
+ contentA->set_video_length (ContentTime::from_frames (3, 24));
+ contentA->set_position (DCPTime::from_frames (2, film->video_frame_rate ()));
+ contentB->set_video_length (ContentTime::from_frames (1, 24));
+ contentB->set_position (DCPTime::from_frames (7, film->video_frame_rate ()));
film->make_dcp ();
*/
+/** @file test/ffmpeg_audio_test.cc
+ * @brief A simple test of reading audio from an FFmpeg file.
+ */
+
#include <boost/test/unit_test.hpp>
-#include <libdcp/cpl.h>
-#include <libdcp/dcp.h>
-#include <libdcp/sound_asset.h>
-#include <libdcp/sound_frame.h>
-#include <libdcp/reel.h>
+#include <dcp/cpl.h>
+#include <dcp/dcp.h>
+#include <dcp/sound_mxf.h>
+#include <dcp/sound_frame.h>
+#include <dcp/reel_sound_asset.h>
+#include <dcp/reel.h>
#include "lib/sndfile_content.h"
#include "lib/film.h"
#include "lib/dcp_content_type.h"
shared_ptr<Film> film = new_test_film ("ffmpeg_audio_test");
film->set_name ("ffmpeg_audio_test");
shared_ptr<FFmpegContent> c (new FFmpegContent (film, "test/data/staircase.mov"));
- c->set_ratio (Ratio::from_id ("185"));
+ c->set_scale (VideoContentScale (Ratio::from_id ("185")));
film->examine_and_add_content (c);
wait_for_jobs ();
boost::filesystem::path path = "build/test";
path /= "ffmpeg_audio_test";
path /= film->dcp_name ();
- libdcp::DCP check (path.string ());
+ dcp::DCP check (path.string ());
check.read ();
- shared_ptr<const libdcp::SoundAsset> sound_asset = check.cpls().front()->reels().front()->main_sound ();
+ shared_ptr<const dcp::ReelSoundAsset> sound_asset = check.cpls().front()->reels().front()->main_sound ();
BOOST_CHECK (sound_asset);
- BOOST_CHECK (sound_asset->channels () == 6);
+ BOOST_CHECK (sound_asset->mxf()->channels () == 6);
/* Sample index in the DCP */
int n = 0;
/* DCP sound asset frame */
int frame = 0;
- while (n < sound_asset->intrinsic_duration()) {
- shared_ptr<const libdcp::SoundFrame> sound_frame = sound_asset->get_frame (frame++);
+ while (n < sound_asset->mxf()->intrinsic_duration()) {
+ shared_ptr<const dcp::SoundFrame> sound_frame = sound_asset->mxf()->get_frame (frame++);
uint8_t const * d = sound_frame->data ();
- for (int i = 0; i < sound_frame->size(); i += (3 * sound_asset->channels())) {
+ for (int i = 0; i < sound_frame->size(); i += (3 * sound_asset->mxf()->channels())) {
- if (sound_asset->channels() > 0) {
+ if (sound_asset->mxf()->channels() > 0) {
/* L should be silent */
int const sample = d[i + 0] | (d[i + 1] << 8);
BOOST_CHECK_EQUAL (sample, 0);
}
- if (sound_asset->channels() > 1) {
+ if (sound_asset->mxf()->channels() > 1) {
/* R should be silent */
int const sample = d[i + 2] | (d[i + 3] << 8);
BOOST_CHECK_EQUAL (sample, 0);
}
- if (sound_asset->channels() > 2) {
+ if (sound_asset->mxf()->channels() > 2) {
/* Mono input so it will appear on centre */
int const sample = d[i + 7] | (d[i + 8] << 8);
BOOST_CHECK_EQUAL (sample, n);
}
- if (sound_asset->channels() > 3) {
+ if (sound_asset->mxf()->channels() > 3) {
/* Lfe should be silent */
int const sample = d[i + 9] | (d[i + 10] << 8);
BOOST_CHECK_EQUAL (sample, 0);
}
- if (sound_asset->channels() > 4) {
+ if (sound_asset->mxf()->channels() > 4) {
/* Ls should be silent */
int const sample = d[i + 11] | (d[i + 12] << 8);
BOOST_CHECK_EQUAL (sample, 0);
}
- if (sound_asset->channels() > 5) {
+ if (sound_asset->mxf()->channels() > 5) {
/* Rs should be silent */
int const sample = d[i + 13] | (d[i + 14] << 8);
BOOST_CHECK_EQUAL (sample, 0);
static void scaling_test_for (shared_ptr<Film> film, shared_ptr<VideoContent> content, string image, string container)
{
- content->set_ratio (Ratio::from_id (image));
+ content->set_scale (VideoContentScale (Ratio::from_id (image)));
film->set_container (Ratio::from_id (container));
film->make_dcp ();
wait_for_jobs ();
- imc->set_video_length (1);
+ imc->set_video_length (ContentTime::from_frames (1, 24));
scaling_test_for (film, imc, "133", "185");
scaling_test_for (film, imc, "185", "185");
import sys
APPNAME = 'dcpomatic'
++<<<<<<< HEAD
+VERSION = '2.0.0devel'
++=======
+ VERSION = '1.64.18devel'
++>>>>>>> master
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>