+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "black_decoder.h"
-#include "image.h"
-#include "null_content.h"
-
-using boost::shared_ptr;
-
-BlackDecoder::BlackDecoder (shared_ptr<const Film> f, shared_ptr<NullContent> c)
- : Decoder (f)
- , VideoDecoder (f)
- , _null_content (c)
-{
-
-}
-
-void
-BlackDecoder::pass ()
-{
- if (!_image) {
- _image.reset (new SimpleImage (AV_PIX_FMT_RGB24, video_size(), true));
- _image->make_black ();
- video (_image, false, _next_video_frame);
- } else {
- video (_image, true, _next_video_frame);
- }
-}
-
-float
-BlackDecoder::video_frame_rate () const
-{
- boost::shared_ptr<const Film> f = _film.lock ();
- if (!f) {
- return 24;
- }
-
- return f->dcp_video_frame_rate ();
-}
-
-VideoContent::Frame
-BlackDecoder::video_length () const
-{
- return _null_content->length() * video_frame_rate() / TIME_HZ;
-}
-
-void
-BlackDecoder::seek (VideoContent::Frame frame)
-{
- _next_video_frame = frame;
-}
-
-void
-BlackDecoder::seek_back ()
-{
- if (_next_video_frame > 0) {
- --_next_video_frame;
- }
-}
-
-bool
-BlackDecoder::done () const
-{
- return _next_video_frame >= _null_content->video_length ();
-}
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "video_decoder.h"
-
-class NullContent;
-
-class BlackDecoder : public VideoDecoder
-{
-public:
- BlackDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<NullContent>);
-
- /* Decoder */
-
- void pass ();
- bool done () const;
-
- /* VideoDecoder */
-
- void seek (VideoContent::Frame);
- void seek_back ();
- float video_frame_rate () const;
- libdcp::Size video_size () const {
- return libdcp::Size (256, 256);
- }
- VideoContent::Frame video_length () const;
-
-private:
- boost::shared_ptr<NullContent> _null_content;
- boost::shared_ptr<Image> _image;
-};
bool
ImageMagickDecoder::done () const
{
- return _next_video_frame > _imagemagick_content->video_length ();
+ return _next_video_frame >= _imagemagick_content->video_length ();
}
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "null_content.h"
-#include "film.h"
-
-using boost::shared_ptr;
-
-NullContent::NullContent (shared_ptr<const Film> f, Time s, Time len)
- : Content (f, s)
- , VideoContent (f, s, f->time_to_video_frames (len))
- , AudioContent (f, s)
- , _audio_length (f->time_to_audio_frames (len))
- , _length (len)
-{
-
-}
-
-int
-NullContent::content_audio_frame_rate () const
-{
- return output_audio_frame_rate ();
-}
-
-
-int
-NullContent::output_audio_frame_rate () const
-{
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
- return film->dcp_audio_frame_rate ();
-}
-
-int
-NullContent::audio_channels () const
-{
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
- return film->dcp_audio_channels ();
-}
-
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <string>
-#include <boost/shared_ptr.hpp>
-#include "video_content.h"
-#include "audio_content.h"
-
-class NullContent : public VideoContent, public AudioContent
-{
-public:
- NullContent (boost::shared_ptr<const Film>, Time, Time);
-
- std::string summary () const {
- return "";
- }
-
- std::string information () const {
- return "";
- }
-
- void as_xml (xmlpp::Node *) const {}
-
- boost::shared_ptr<Content> clone () const {
- return boost::shared_ptr<Content> ();
- }
-
- int audio_channels () const;
-
- AudioContent::Frame audio_length () const {
- return _audio_length;
- }
-
- int content_audio_frame_rate () const;
-
- int output_audio_frame_rate () const;
-
- AudioMapping audio_mapping () const {
- return AudioMapping ();
- }
-
- void set_audio_mapping (AudioMapping) {}
-
- Time length () const {
- return _length;
- }
-
-private:
- AudioContent::Frame _audio_length;
- Time _length;
-};
#include "playlist.h"
#include "job.h"
#include "image.h"
-#include "null_content.h"
-#include "black_decoder.h"
-#include "silence_decoder.h"
#include "ratio.h"
#include "resampler.h"
#define DEBUG_PLAYER 1
-struct Piece
+class Piece
{
+public:
Piece (shared_ptr<Content> c)
: content (c)
- , last_emission (0)
+ , video_position (c->start ())
+ , audio_position (c->start ())
{}
Piece (shared_ptr<Content> c, shared_ptr<Decoder> d)
: content (c)
, decoder (d)
- , last_emission (0)
+ , video_position (c->start ())
+ , audio_position (c->start ())
{}
shared_ptr<Content> content;
shared_ptr<Decoder> decoder;
- Time last_emission;
+ Time video_position;
+ Time audio_position;
};
-
#ifdef DEBUG_PLAYER
std::ostream& operator<<(std::ostream& s, Piece const & p)
{
- if (dynamic_pointer_cast<NullContent> (p.content)) {
- if (dynamic_pointer_cast<SilenceDecoder> (p.decoder)) {
- s << "\tsilence ";
- } else {
- s << "\tblack ";
- }
- } else if (dynamic_pointer_cast<FFmpegContent> (p.content)) {
+ if (dynamic_pointer_cast<FFmpegContent> (p.content)) {
s << "\tffmpeg ";
} else if (dynamic_pointer_cast<ImageMagickContent> (p.content)) {
s << "\timagemagick";
, _video (true)
, _audio (true)
, _have_valid_pieces (false)
- , _position (0)
+ , _video_position (0)
+ , _audio_position (0)
, _audio_buffers (f->dcp_audio_channels(), 0)
- , _next_audio (0)
{
_playlist->Changed.connect (bind (&Player::playlist_changed, this));
_playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2));
+ set_video_container_size (_film->container()->size (_film->full_frame ()));
}
void
_have_valid_pieces = true;
}
- /* Here we are just finding the active decoder with the earliest last emission time, then
- calling pass on it.
- */
+#ifdef DEBUG_PLAYER
+ cout << "= PASS\n";
+#endif
Time earliest_t = TIME_MAX;
shared_ptr<Piece> earliest;
+ enum {
+ VIDEO,
+ AUDIO
+ } type = VIDEO;
for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
if ((*i)->decoder->done ()) {
continue;
}
- if ((*i)->last_emission < earliest_t) {
- earliest_t = (*i)->last_emission;
- earliest = *i;
+ if (dynamic_pointer_cast<VideoDecoder> ((*i)->decoder)) {
+ if ((*i)->video_position < earliest_t) {
+ earliest_t = (*i)->video_position;
+ earliest = *i;
+ type = VIDEO;
+ }
+ }
+
+ if (dynamic_pointer_cast<AudioDecoder> ((*i)->decoder)) {
+ if ((*i)->audio_position < earliest_t) {
+ earliest_t = (*i)->audio_position;
+ earliest = *i;
+ type = AUDIO;
+ }
}
}
if (!earliest) {
+#ifdef DEBUG_PLAYER
+ cout << "no earliest piece.\n";
+#endif
+
flush ();
return true;
}
- earliest->decoder->pass ();
- _position = earliest->last_emission;
+ switch (type) {
+ case VIDEO:
+ if (earliest_t > _video_position) {
+#ifdef DEBUG_PLAYER
+ cout << "no video here; emitting black frame.\n";
+#endif
+ emit_black ();
+ } else {
+#ifdef DEBUG_PLAYER
+ cout << "Pass " << *earliest << "\n";
+#endif
+ earliest->decoder->pass ();
+ }
+ break;
+
+ case AUDIO:
+ if (earliest_t > _audio_position) {
+#ifdef DEBUG_PLAYER
+ cout << "no audio here; emitting silence.\n";
+#endif
+ emit_silence (_film->time_to_audio_frames (earliest_t - _audio_position));
+ } else {
+#ifdef DEBUG_PLAYER
+ cout << "Pass " << *earliest << "\n";
+#endif
+ earliest->decoder->pass ();
+ }
+ break;
+ }
+
+#ifdef DEBUG_PLAYER
+ cout << "\tpost pass " << _video_position << " " << _audio_position << "\n";
+#endif
return false;
}
image = image->crop (content->crop(), true);
- libdcp::Size const container_size = _video_container_size.get_value_or (_film->container()->size (_film->full_frame ()));
- libdcp::Size const image_size = content->ratio()->size (container_size);
+ libdcp::Size const image_size = content->ratio()->size (_video_container_size);
image = image->scale_and_convert_to_rgb (image_size, _film->scaler(), true);
}
#endif
- if (image_size != container_size) {
- assert (image_size.width <= container_size.width);
- assert (image_size.height <= container_size.height);
- shared_ptr<Image> im (new SimpleImage (PIX_FMT_RGB24, container_size, true));
+ if (image_size != _video_container_size) {
+ assert (image_size.width <= _video_container_size.width);
+ assert (image_size.height <= _video_container_size.height);
+ shared_ptr<Image> im (new SimpleImage (PIX_FMT_RGB24, _video_container_size, true));
im->make_black ();
- im->copy (image, Position ((container_size.width - image_size.width) / 2, (container_size.height - image_size.height) / 2));
+ im->copy (image, Position ((_video_container_size.width - image_size.width) / 2, (_video_container_size.height - image_size.height) / 2));
image = im;
}
Time time = content->start() + (frame * frc.factor() * TIME_HZ / _film->dcp_video_frame_rate());
Video (image, same, time);
+ time += TIME_HZ / _film->dcp_video_frame_rate();
if (frc.repeat) {
- time += TIME_HZ / _film->dcp_video_frame_rate();
Video (image, true, time);
+ time += TIME_HZ / _film->dcp_video_frame_rate();
}
- piece->last_emission = min (piece->last_emission, time);
+ _video_position = piece->video_position = time;
}
void
*/
Time const time = content->start() + (frame * TIME_HZ / _film->dcp_audio_frame_rate());
- piece->last_emission = min (piece->last_emission, time);
- cout << "Player gets " << dcp_mapped->frames() << " @ " << time << " cf " << _next_audio << "\n";
+ cout << "Player gets " << dcp_mapped->frames() << " @ " << time << " cf " << _audio_position << "\n";
- if (time > _next_audio) {
+ if (time > _audio_position) {
/* We can emit some audio from our buffers */
- OutputAudioFrame const N = _film->time_to_audio_frames (time - _next_audio);
+ OutputAudioFrame const N = _film->time_to_audio_frames (time - _audio_position);
assert (N <= _audio_buffers.frames());
shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), N));
emit->copy_from (&_audio_buffers, N, 0, 0);
- Audio (emit, _next_audio);
- _next_audio += _film->audio_frames_to_time (N);
+ Audio (emit, _audio_position);
+ _audio_position = piece->audio_position = time + _film->audio_frames_to_time (N);
/* And remove it from our buffers */
if (_audio_buffers.frames() > N) {
if (_audio_buffers.frames() > 0) {
shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), _audio_buffers.frames()));
emit->copy_from (&_audio_buffers, _audio_buffers.frames(), 0, 0);
- Audio (emit, _next_audio);
- _next_audio += _film->audio_frames_to_time (_audio_buffers.frames ());
+ Audio (emit, _audio_position);
+ _audio_position += _film->audio_frames_to_time (_audio_buffers.frames ());
_audio_buffers.set_frames (0);
}
+
+ while (_video_position < _audio_position) {
+ emit_black ();
+ }
+
+ while (_audio_position < _video_position) {
+ emit_silence (_film->time_to_audio_frames (_video_position - _audio_position));
+ }
+
}
/** @return true on error */
}
-void
-Player::add_black_piece (Time s, Time len)
-{
- shared_ptr<NullContent> nc (new NullContent (_film, s, len));
- nc->set_ratio (_film->container ());
- shared_ptr<BlackDecoder> bd (new BlackDecoder (_film, nc));
- shared_ptr<Piece> p (new Piece (nc, bd));
- _pieces.push_back (p);
- bd->Video.connect (bind (&Player::process_video, this, p, _1, _2, _3));
-}
-
-void
-Player::add_silent_piece (Time s, Time len)
-{
- shared_ptr<NullContent> nc (new NullContent (_film, s, len));
- shared_ptr<SilenceDecoder> sd (new SilenceDecoder (_film, nc));
- shared_ptr<Piece> p (new Piece (nc, sd));
- _pieces.push_back (p);
- sd->Audio.connect (bind (&Player::process_audio, this, p, _1, _2));
-}
-
-
void
Player::setup_pieces ()
{
_pieces.push_back (piece);
}
- /* Fill in visual gaps with black and audio gaps with silence */
-
- Time video_pos = 0;
- Time audio_pos = 0;
- list<shared_ptr<Piece> > pieces_copy = _pieces;
- for (list<shared_ptr<Piece> >::iterator i = pieces_copy.begin(); i != pieces_copy.end(); ++i) {
- if (dynamic_pointer_cast<VideoContent> ((*i)->content)) {
- Time const diff = (*i)->content->start() - video_pos;
- if (diff > 0) {
- add_black_piece (video_pos, diff);
- }
- video_pos = (*i)->content->end();
- }
-
- shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> ((*i)->content);
- if (ac && ac->audio_channels()) {
- Time const diff = (*i)->content->start() - audio_pos;
- if (diff > 0) {
- add_silent_piece (video_pos, diff);
- }
- audio_pos = (*i)->content->end();
- }
- }
-
- if (video_pos < audio_pos) {
- add_black_piece (video_pos, audio_pos - video_pos);
- } else if (audio_pos < video_pos) {
- add_silent_piece (audio_pos, video_pos - audio_pos);
- }
-
#ifdef DEBUG_PLAYER
cout << "=== Player setup:\n";
for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
Player::set_video_container_size (libdcp::Size s)
{
_video_container_size = s;
+ _black_frame.reset (new SimpleImage (PIX_FMT_RGB24, _video_container_size, true));
+ _black_frame->make_black ();
}
shared_ptr<Resampler>
_resamplers[c] = r;
return r;
}
+
+void
+Player::emit_black ()
+{
+ /* XXX: use same here */
+ Video (_black_frame, false, _video_position);
+ _video_position += _film->video_frames_to_time (1);
+}
+
+void
+Player::emit_silence (OutputAudioFrame most)
+{
+ OutputAudioFrame N = min (most, _film->dcp_audio_frame_rate() / 2);
+ shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->dcp_audio_channels(), N));
+ silence->make_silent ();
+ Audio (silence, _audio_position);
+ _audio_position += _film->audio_frames_to_time (N);
+}
+
+
+
void seek (Time);
void seek_back ();
- /** @return position that we are at; ie the time of the next thing we will emit on pass() */
- Time position () const {
- return _position;
+ Time video_position () const {
+ return _video_position;
}
void set_video_container_size (libdcp::Size);
void playlist_changed ();
void content_changed (boost::weak_ptr<Content>, int);
void do_seek (Time, bool);
- void add_black_piece (Time, Time);
- void add_silent_piece (Time, Time);
void flush ();
+ void emit_black ();
+ void emit_silence (OutputAudioFrame);
boost::shared_ptr<Resampler> resampler (boost::shared_ptr<AudioContent>);
boost::shared_ptr<const Film> _film;
/** Our pieces are ready to go; if this is false the pieces must be (re-)created before they are used */
bool _have_valid_pieces;
std::list<boost::shared_ptr<Piece> > _pieces;
- Time _position;
+
+ /** The time after the last video that we emitted */
+ Time _video_position;
+ /** The time after the last audio that we emitted */
+ Time _audio_position;
+
AudioBuffers _audio_buffers;
- Time _next_audio;
- boost::optional<libdcp::Size> _video_container_size;
+
+ libdcp::Size _video_container_size;
+ boost::shared_ptr<Image> _black_frame;
std::map<boost::shared_ptr<AudioContent>, boost::shared_ptr<Resampler> > _resamplers;
};
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "silence_decoder.h"
-#include "null_content.h"
-#include "audio_buffers.h"
-
-using std::min;
-using std::cout;
-using boost::shared_ptr;
-
-SilenceDecoder::SilenceDecoder (shared_ptr<const Film> f, shared_ptr<NullContent> c)
- : Decoder (f)
- , AudioDecoder (f)
- , _null_content (c)
-{
-
-}
-
-void
-SilenceDecoder::pass ()
-{
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- AudioContent::Frame const this_time = min (_null_content->audio_length() - _next_audio_frame, int64_t (_null_content->output_audio_frame_rate() / 2));
- shared_ptr<AudioBuffers> data (new AudioBuffers (film->dcp_audio_channels(), this_time));
- data->make_silent ();
- audio (data, _next_audio_frame);
-}
-
-bool
-SilenceDecoder::done () const
-{
- return _next_audio_frame > _null_content->audio_length ();
-}
+++ /dev/null
-/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <boost/shared_ptr.hpp>
-#include "audio_decoder.h"
-
-class Film;
-class NullContent;
-
-class SilenceDecoder : public AudioDecoder
-{
-public:
- SilenceDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<NullContent>);
-
- void pass ();
- bool done () const;
-
-private:
- boost::shared_ptr<NullContent> _null_content;
-};
audio_content.cc
audio_decoder.cc
audio_mapping.cc
- black_decoder.cc
config.cc
content.cc
cross.cc
job_manager.cc
log.cc
lut.cc
- null_content.cc
player.cc
playlist.cc
ratio.cc
scp_dcp_job.cc
scaler.cc
server.cc
- silence_decoder.cc
sndfile_content.cc
sndfile_decoder.cc
sound_processor.cc
return;
}
- _player->seek (_player->position() - _film->video_frames_to_time (1));
+ _player->seek (_player->video_position() - _film->video_frames_to_time (1));
get_frame ();
_panel->Refresh ();
_panel->Update ();
get_frame ();
if (_film->length()) {
- int const new_slider_position = 4096 * _player->position() / _film->length();
+ int const new_slider_position = 4096 * _player->video_position() / _film->length();
if (new_slider_position != _slider->GetValue()) {
_slider->SetValue (new_slider_position);
}