* Default to using a DCI name.
+ * Support for using external sound files instead
+ of the ones in the video source.
+
2012-11-14 Carl Hetherington <cth@carlh.net>
* Rearrange the GUI a bit to tidy things up.
#include <boost/shared_ptr.hpp>
#include "ab_transcoder.h"
#include "film.h"
-#include "decoder.h"
+#include "video_decoder.h"
+#include "audio_decoder.h"
#include "encoder.h"
#include "job.h"
#include "options.h"
_da = decoder_factory (_film_a, o, j);
_db = decoder_factory (_film_b, o, j);
- _da->Video.connect (bind (&ABTranscoder::process_video, this, _1, _2, 0));
- _db->Video.connect (bind (&ABTranscoder::process_video, this, _1, _2, 1));
- _da->Audio.connect (bind (&Encoder::process_audio, e, _1));
+ /* XXX */
+
+// _da->Video.connect (bind (&ABTranscoder::process_video, this, _1, _2, 0));
+// _db->Video.connect (bind (&ABTranscoder::process_video, this, _1, _2, 1));
+// _da->Audio.connect (bind (&Encoder::process_audio, e, _1));
}
ABTranscoder::~ABTranscoder ()
_encoder->process_begin ();
while (1) {
- bool const a = _da->pass ();
- bool const b = _db->pass ();
+ bool const va = _da.first->pass ();
+ bool const vb = _db.first->pass ();
+ bool const a = _da.first->pass ();
- if (a && b) {
+ if (va && vb && a) {
break;
}
}
class Job;
class Encoder;
-class Decoder;
+class VideoDecoder;
+class AudioDecoder;
class Options;
class Image;
class Log;
boost::shared_ptr<const Options> _opt;
Job* _job;
boost::shared_ptr<Encoder> _encoder;
- boost::shared_ptr<Decoder> _da;
- boost::shared_ptr<Decoder> _db;
+ std::pair<boost::shared_ptr<VideoDecoder>, boost::shared_ptr<AudioDecoder> > _da;
+ std::pair<boost::shared_ptr<VideoDecoder>, boost::shared_ptr<AudioDecoder> > _db;
boost::shared_ptr<Image> _image;
};
--- /dev/null
+#include "audio_decoder.h"
+#include "stream.h"
+
+using boost::optional;
+using boost::shared_ptr;
+
+AudioDecoder::AudioDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j)
+ : Decoder (f, o, j)
+{
+
+}
+
+void
+AudioDecoder::set_audio_stream (optional<AudioStream> s)
+{
+ _audio_stream = s;
+}
--- /dev/null
+#ifndef DVDOMATIC_AUDIO_DECODER_H
+#define DVDOMATIC_AUDIO_DECODER_H
+
+#include "audio_source.h"
+#include "stream.h"
+#include "decoder.h"
+
+class AudioDecoder : public AudioSource, public virtual Decoder
+{
+public:
+ AudioDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *);
+
+ virtual void set_audio_stream (boost::optional<AudioStream>);
+
+ boost::optional<AudioStream> audio_stream () const {
+ return _audio_stream;
+ }
+
+ std::vector<AudioStream> audio_streams () const {
+ return _audio_streams;
+ }
+
+protected:
+ boost::optional<AudioStream> _audio_stream;
+ std::vector<AudioStream> _audio_streams;
+};
+
+#endif
: _film (f)
, _opt (o)
, _job (j)
- , _video_frame (0)
{
}
-
-/** Start decoding */
-void
-Decoder::go ()
-{
- if (_job && !_film->dcp_length()) {
- _job->set_progress_unknown ();
- }
-
- while (pass () == false) {
- if (_job && _film->dcp_length()) {
- _job->set_progress (float (_video_frame) / _film->length().get());
- }
- }
-}
-
-/** Called by subclasses to tell the world that some video data is ready.
- * We find a subtitle then emit it for listeners.
- * @param frame to decode; caller manages memory.
- */
-void
-Decoder::emit_video (shared_ptr<Image> image)
-{
- shared_ptr<Subtitle> sub;
- if (_timed_subtitle && _timed_subtitle->displayed_at (double (video_frame()) / _film->frames_per_second())) {
- sub = _timed_subtitle->subtitle ();
- }
-
- signal_video (image, sub);
-}
-
-void
-Decoder::repeat_last_video ()
-{
- if (!_last_image) {
- _last_image.reset (new CompactImage (pixel_format(), native_size()));
- _last_image->make_black ();
- }
-
- signal_video (_last_image, _last_subtitle);
-}
-
-void
-Decoder::signal_video (shared_ptr<Image> image, shared_ptr<Subtitle> sub)
-{
- TIMING ("Decoder emits %1", _video_frame);
- Video (image, sub);
- ++_video_frame;
-
- _last_image = image;
- _last_subtitle = sub;
-}
-
-void
-Decoder::emit_subtitle (shared_ptr<TimedSubtitle> s)
-{
- _timed_subtitle = s;
-
- if (_timed_subtitle && _opt->apply_crop) {
- Position const p = _timed_subtitle->subtitle()->position ();
- _timed_subtitle->subtitle()->set_position (Position (p.x - _film->crop().left, p.y - _film->crop().top));
- }
-}
-
-void
-Decoder::set_audio_stream (optional<AudioStream> s)
-{
- _audio_stream = s;
-}
-
-void
-Decoder::set_subtitle_stream (optional<SubtitleStream> s)
-{
- _subtitle_stream = s;
-}
/** @class Decoder.
* @brief Parent class for decoders of content.
*
- * These classes can be instructed run through their content
- * (by calling ::go), and they emit signals when video or audio data is ready for something else
- * to process.
+ * These classes can be instructed run through their content (by
+ * calling ::go), and they emit signals when video or audio data is
+ * ready for something else to process.
*/
-class Decoder : public VideoSource, public AudioSource
+class Decoder
{
public:
Decoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *);
virtual ~Decoder () {}
- /* Methods to query our input video */
-
- /** @return video frames per second, or 0 if unknown */
- virtual float frames_per_second () const = 0;
- /** @return native size in pixels */
- virtual Size native_size () const = 0;
-
- virtual int time_base_numerator () const = 0;
- virtual int time_base_denominator () const = 0;
- virtual int sample_aspect_ratio_numerator () const = 0;
- virtual int sample_aspect_ratio_denominator () const = 0;
-
virtual bool pass () = 0;
- void go ();
-
- SourceFrame video_frame () const {
- return _video_frame;
- }
-
- virtual void set_audio_stream (boost::optional<AudioStream>);
- virtual void set_subtitle_stream (boost::optional<SubtitleStream>);
-
- boost::optional<AudioStream> audio_stream () const {
- return _audio_stream;
- }
-
- boost::optional<SubtitleStream> subtitle_stream () const {
- return _subtitle_stream;
- }
-
- std::vector<AudioStream> audio_streams () const {
- return _audio_streams;
- }
-
- std::vector<SubtitleStream> subtitle_streams () const {
- return _subtitle_streams;
- }
protected:
+ virtual void set_progress () const {}
- virtual PixelFormat pixel_format () const = 0;
-
- void emit_video (boost::shared_ptr<Image>);
- void emit_subtitle (boost::shared_ptr<TimedSubtitle>);
- void repeat_last_video ();
-
/** our Film */
boost::shared_ptr<Film> _film;
/** our options */
boost::shared_ptr<const Options> _opt;
/** associated Job, or 0 */
Job* _job;
-
- boost::optional<AudioStream> _audio_stream;
- boost::optional<SubtitleStream> _subtitle_stream;
-
- std::vector<AudioStream> _audio_streams;
- std::vector<SubtitleStream> _subtitle_streams;
-
-private:
- void signal_video (boost::shared_ptr<Image>, boost::shared_ptr<Subtitle>);
-
- SourceFrame _video_frame;
-
- boost::shared_ptr<TimedSubtitle> _timed_subtitle;
-
- boost::shared_ptr<Image> _last_image;
- boost::shared_ptr<Subtitle> _last_subtitle;
};
#endif
#include "ffmpeg_decoder.h"
#include "imagemagick_decoder.h"
#include "film.h"
+#include "external_audio_decoder.h"
using std::string;
+using std::pair;
+using std::make_pair;
using boost::shared_ptr;
-shared_ptr<Decoder>
+pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> >
decoder_factory (
shared_ptr<Film> f, shared_ptr<const Options> o, Job* j
)
{
if (boost::filesystem::is_directory (f->content_path()) || f->content_type() == STILL) {
/* A single image file, or a directory of them */
- return shared_ptr<Decoder> (new ImageMagickDecoder (f, o, j));
+ return make_pair (
+ shared_ptr<VideoDecoder> (new ImageMagickDecoder (f, o, j)),
+ shared_ptr<AudioDecoder> ()
+ );
+ }
+
+ shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (f, o, j));
+ if (f->use_content_audio()) {
+ return make_pair (fd, fd);
}
- return shared_ptr<Decoder> (new FFmpegDecoder (f, o, j));
+ return make_pair (fd, shared_ptr<AudioDecoder> (new ExternalAudioDecoder (f, o, j)));
}
*/
/** @file src/decoder_factory.h
- * @brief A method to create an appropriate decoder for some content.
+ * @brief A method to create appropriate decoders for some content.
*/
-class Decoder;
class Film;
class Options;
class Job;
-class Log;
+class VideoDecoder;
+class AudioDecoder;
-extern boost::shared_ptr<Decoder> decoder_factory (
+extern std::pair<boost::shared_ptr<VideoDecoder>, boost::shared_ptr<AudioDecoder> > decoder_factory (
boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *
);
#include "transcoder.h"
#include "log.h"
#include "film.h"
+#include "video_decoder.h"
using std::string;
using std::vector;
+using std::pair;
using boost::shared_ptr;
ExamineContentJob::ExamineContentJob (shared_ptr<Film> f, shared_ptr<Job> req)
descend (0.5);
- _decoder = decoder_factory (_film, o, this);
- _decoder->go ();
+ pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> > decoders = decoder_factory (_film, o, this);
- _film->set_length (_decoder->video_frame());
+ set_progress_unknown ();
+ while (!decoders.first->pass()) {
+ /* keep going */
+ }
+
+ _film->set_length (decoders.first->video_frame());
_film->log()->log (String::compose ("Video length is %1 frames", _film->length()));
#include "job.h"
-class Decoder;
-
/** @class ExamineContentJob
* @brief A class to run through content at high speed to find its length.
*/
std::string name () const;
void run ();
-
-private:
- boost::shared_ptr<Decoder> _decoder;
};
--- /dev/null
+#include <sndfile.h>
+#include "external_audio_decoder.h"
+#include "film.h"
+#include "exceptions.h"
+
+using std::vector;
+using std::string;
+using std::min;
+using boost::shared_ptr;
+
+ExternalAudioDecoder::ExternalAudioDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j)
+ : Decoder (f, o, j)
+ , AudioDecoder (f, o, j)
+{
+
+}
+
+bool
+ExternalAudioDecoder::pass ()
+{
+ vector<string> const files = _film->external_audio ();
+
+ int N = 0;
+ for (size_t i = 0; i < files.size(); ++i) {
+ if (!files[i].empty()) {
+ N = i + 1;
+ }
+ }
+
+ if (N == 0) {
+ return true;
+ }
+
+ bool first = true;
+ sf_count_t frames = 0;
+
+ vector<SNDFILE*> sndfiles;
+ for (vector<string>::const_iterator i = files.begin(); i != files.end(); ++i) {
+ if (i->empty ()) {
+ sndfiles.push_back (0);
+ } else {
+ SF_INFO info;
+ SNDFILE* s = sf_open (i->c_str(), SFM_READ, &info);
+ if (!s) {
+ throw DecodeError ("could not open external audio file for reading");
+ }
+
+ if (info.channels != 1) {
+ throw DecodeError ("external audio files must be mono");
+ }
+
+ sndfiles.push_back (s);
+
+ if (first) {
+ /* XXX: nasty magic value */
+ AudioStream st ("DVDOMATIC-EXTERNAL", -1, info.samplerate, av_get_default_channel_layout (info.channels));
+ _audio_streams.push_back (st);
+ _audio_stream = st;
+ frames = info.frames;
+ first = false;
+ } else {
+ if (info.frames != frames) {
+ throw DecodeError ("external audio files have differing lengths");
+ }
+ }
+ }
+ }
+
+ sf_count_t const block = 65536;
+
+ shared_ptr<AudioBuffers> audio (new AudioBuffers (_audio_stream.get().channels(), block));
+ while (frames > 0) {
+ sf_count_t const this_time = min (block, frames);
+ for (size_t i = 0; i < sndfiles.size(); ++i) {
+ if (!sndfiles[i]) {
+ audio->make_silent (i);
+ } else {
+ sf_read_float (sndfiles[i], audio->data(i), block);
+ }
+ }
+
+ Audio (audio);
+ frames -= this_time;
+ }
+
+ return true;
+}
--- /dev/null
+#include "decoder.h"
+#include "audio_decoder.h"
+
+class ExternalAudioDecoder : public AudioDecoder
+{
+public:
+ ExternalAudioDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *);
+
+ bool pass ();
+};
FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j)
: Decoder (f, o, j)
+ , VideoDecoder (f, o, j)
+ , AudioDecoder (f, o, j)
, _format_context (0)
, _video_stream (-1)
, _frame (0)
void
FFmpegDecoder::set_audio_stream (optional<AudioStream> s)
{
- Decoder::set_audio_stream (s);
+ AudioDecoder::set_audio_stream (s);
setup_audio ();
}
void
FFmpegDecoder::set_subtitle_stream (optional<SubtitleStream> s)
{
- Decoder::set_subtitle_stream (s);
+ VideoDecoder::set_subtitle_stream (s);
setup_subtitle ();
}
}
#include "util.h"
#include "decoder.h"
+#include "video_decoder.h"
+#include "audio_decoder.h"
struct AVFilterGraph;
struct AVCodecContext;
/** @class FFmpegDecoder
* @brief A decoder using FFmpeg to decode content.
*/
-class FFmpegDecoder : public Decoder
+class FFmpegDecoder : public VideoDecoder, public AudioDecoder
{
public:
FFmpegDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *);
~FFmpegDecoder ();
- /* Methods to query our input video */
float frames_per_second () const;
Size native_size () const;
int time_base_numerator () const;
#include "check_hashes_job.h"
#include "version.h"
#include "ui_signaller.h"
+#include "video_decoder.h"
+#include "audio_decoder.h"
using std::string;
using std::stringstream;
shared_ptr<Options> o (new Options ("", "", ""));
o->out_size = Size (1024, 1024);
- shared_ptr<Decoder> d = decoder_factory (shared_from_this(), o, 0);
+ pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> > d = decoder_factory (shared_from_this(), o, 0);
- set_size (d->native_size ());
- set_frames_per_second (d->frames_per_second ());
- set_audio_streams (d->audio_streams ());
- set_subtitle_streams (d->subtitle_streams ());
+ set_size (d.first->native_size ());
+ set_frames_per_second (d.first->frames_per_second ());
+ set_subtitle_streams (d.first->subtitle_streams ());
+ set_audio_streams (d.second->audio_streams ());
/* Start off with the first audio and subtitle streams */
- if (!d->audio_streams().empty()) {
- set_audio_stream (d->audio_streams().front());
+ if (!d.second->audio_streams().empty()) {
+ set_audio_stream (d.second->audio_streams().front());
}
- if (!d->subtitle_streams().empty()) {
- set_subtitle_stream (d->subtitle_streams().front());
+ if (!d.first->subtitle_streams().empty()) {
+ set_subtitle_stream (d.first->subtitle_streams().front());
}
{
ImageMagickDecoder::ImageMagickDecoder (
boost::shared_ptr<Film> f, boost::shared_ptr<const Options> o, Job* j)
: Decoder (f, o, j)
+ , VideoDecoder (f, o, j)
{
if (boost::filesystem::is_directory (_film->content_path())) {
for (
*/
-#include "decoder.h"
+#include "video_decoder.h"
namespace Magick {
class Image;
}
-class ImageMagickDecoder : public Decoder
+class ImageMagickDecoder : public VideoDecoder
{
public:
ImageMagickDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *);
#include "delay_line.h"
#include "options.h"
#include "gain.h"
+#include "video_decoder.h"
+#include "audio_decoder.h"
using std::string;
using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
/** Construct a transcoder using a Decoder that we create and a supplied Encoder.
* @param f Film that we are transcoding.
Transcoder::Transcoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j, shared_ptr<Encoder> e)
: _job (j)
, _encoder (e)
- , _decoder (decoder_factory (f, o, j))
+ , _decoders (decoder_factory (f, o, j))
{
assert (_encoder);
}
/* Set up the decoder to use the film's set streams */
- _decoder->set_audio_stream (f->audio_stream ());
- _decoder->set_subtitle_stream (f->subtitle_stream ());
+ _decoders.first->set_subtitle_stream (f->subtitle_stream ());
+ _decoders.second->set_audio_stream (f->audio_stream ());
if (_matcher) {
- _decoder->connect_video (_matcher);
+ _decoders.first->connect_video (_matcher);
_matcher->connect_video (_encoder);
} else {
- _decoder->connect_video (_encoder);
+ _decoders.first->connect_video (_encoder);
}
if (_matcher && _delay_line) {
- _decoder->connect_audio (_delay_line);
+ _decoders.second->connect_audio (_delay_line);
_delay_line->connect_audio (_matcher);
_matcher->connect_audio (_gain);
_gain->connect_audio (_encoder);
{
_encoder->process_begin ();
try {
- _decoder->go ();
+ while (1) {
+ bool const v = _decoders.first->pass ();
+
+ bool a = false;
+ if (dynamic_pointer_cast<Decoder> (_decoders.second) != dynamic_pointer_cast<Decoder> (_decoders.first)) {
+ a = _decoders.second->pass ();
+ }
+
+ if (v && a) {
+ break;
+ }
+ }
+
} catch (...) {
/* process_end() is important as the decoder may have worker
threads that need to be cleaned up.
*/
-#include "decoder.h"
-
/** @file src/transcoder.h
* @brief A class which takes a FilmState and some Options, then uses those to transcode a Film.
*
class Matcher;
class VideoFilter;
class Gain;
+class VideoDecoder;
+class AudioDecoder;
+class DelayLine;
+class Options;
/** @class Transcoder
* @brief A class which takes a FilmState and some Options, then uses those to transcode a Film.
void go ();
- /** @return Our decoder */
- boost::shared_ptr<Decoder> decoder () {
- return _decoder;
- }
-
protected:
/** A Job that is running this Transcoder, or 0 */
Job* _job;
/** The encoder that we will use */
boost::shared_ptr<Encoder> _encoder;
- /** The decoder that we will use */
- boost::shared_ptr<Decoder> _decoder;
+ /** The decoders that we will use */
+ std::pair<boost::shared_ptr<VideoDecoder>, boost::shared_ptr<AudioDecoder> > _decoders;
boost::shared_ptr<Matcher> _matcher;
boost::shared_ptr<DelayLine> _delay_line;
boost::shared_ptr<Gain> _gain;
AudioBuffers::make_silent ()
{
for (int i = 0; i < _channels; ++i) {
- for (int j = 0; j < _frames; ++j) {
- _data[i][j] = 0;
- }
+ make_silent (i);
+ }
+}
+
+void
+AudioBuffers::make_silent (int c)
+{
+ for (int i = 0; i < _frames; ++i) {
+ _data[c][i] = 0;
}
}
void set_frames (int f);
void make_silent ();
+ void make_silent (int c);
void copy_from (AudioBuffers* from, int frames_to_copy, int read_offset, int write_offset);
void move (int from, int to, int frames);
--- /dev/null
+#include "video_decoder.h"
+#include "subtitle.h"
+#include "film.h"
+#include "image.h"
+#include "log.h"
+#include "options.h"
+#include "job.h"
+
+using boost::shared_ptr;
+using boost::optional;
+
+VideoDecoder::VideoDecoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j)
+ : Decoder (f, o, j)
+ , _video_frame (0)
+{
+
+}
+
+/** Called by subclasses to tell the world that some video data is ready.
+ * We find a subtitle then emit it for listeners.
+ * @param frame to decode; caller manages memory.
+ */
+void
+VideoDecoder::emit_video (shared_ptr<Image> image)
+{
+ shared_ptr<Subtitle> sub;
+ if (_timed_subtitle && _timed_subtitle->displayed_at (double (video_frame()) / _film->frames_per_second())) {
+ sub = _timed_subtitle->subtitle ();
+ }
+
+ signal_video (image, sub);
+}
+
+void
+VideoDecoder::repeat_last_video ()
+{
+ if (!_last_image) {
+ _last_image.reset (new CompactImage (pixel_format(), native_size()));
+ _last_image->make_black ();
+ }
+
+ signal_video (_last_image, _last_subtitle);
+}
+
+void
+VideoDecoder::signal_video (shared_ptr<Image> image, shared_ptr<Subtitle> sub)
+{
+ TIMING ("Decoder emits %1", _video_frame);
+ Video (image, sub);
+ ++_video_frame;
+
+ _last_image = image;
+ _last_subtitle = sub;
+}
+
+void
+VideoDecoder::emit_subtitle (shared_ptr<TimedSubtitle> s)
+{
+ _timed_subtitle = s;
+
+ if (_timed_subtitle && _opt->apply_crop) {
+ Position const p = _timed_subtitle->subtitle()->position ();
+ _timed_subtitle->subtitle()->set_position (Position (p.x - _film->crop().left, p.y - _film->crop().top));
+ }
+}
+
+void
+VideoDecoder::set_subtitle_stream (optional<SubtitleStream> s)
+{
+ _subtitle_stream = s;
+}
+
+void
+VideoDecoder::set_progress () const
+{
+ if (_job && _film->dcp_length()) {
+ _job->set_progress (float (_video_frame) / _film->length().get());
+ }
+}
--- /dev/null
+#ifndef DVDOMATIC_VIDEO_DECODER_H
+#define DVDOMATIC_VIDEO_DECODER_H
+
+#include "video_source.h"
+#include "stream.h"
+#include "decoder.h"
+
+class VideoDecoder : public VideoSource, public virtual Decoder
+{
+public:
+ VideoDecoder (boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *);
+
+ /** @return video frames per second, or 0 if unknown */
+ virtual float frames_per_second () const = 0;
+ /** @return native size in pixels */
+ virtual Size native_size () const = 0;
+
+ virtual int time_base_numerator () const = 0;
+ virtual int time_base_denominator () const = 0;
+ virtual int sample_aspect_ratio_numerator () const = 0;
+ virtual int sample_aspect_ratio_denominator () const = 0;
+
+ virtual void set_subtitle_stream (boost::optional<SubtitleStream>);
+
+ SourceFrame video_frame () const {
+ return _video_frame;
+ }
+
+ boost::optional<SubtitleStream> subtitle_stream () const {
+ return _subtitle_stream;
+ }
+
+ std::vector<SubtitleStream> subtitle_streams () const {
+ return _subtitle_streams;
+ }
+
+protected:
+
+ virtual PixelFormat pixel_format () const = 0;
+ void set_progress () const;
+
+ void emit_video (boost::shared_ptr<Image>);
+ void emit_subtitle (boost::shared_ptr<TimedSubtitle>);
+ void repeat_last_video ();
+
+ boost::optional<SubtitleStream> _subtitle_stream;
+ std::vector<SubtitleStream> _subtitle_streams;
+
+private:
+ void signal_video (boost::shared_ptr<Image>, boost::shared_ptr<Subtitle>);
+
+ SourceFrame _video_frame;
+
+ boost::shared_ptr<TimedSubtitle> _timed_subtitle;
+
+ boost::shared_ptr<Image> _last_image;
+ boost::shared_ptr<Subtitle> _last_subtitle;
+};
+
+#endif
obj.source = """
ab_transcode_job.cc
ab_transcoder.cc
+ audio_decoder.cc
audio_source.cc
check_hashes_job.cc
config.cc
encoder.cc
encoder_factory.cc
examine_content_job.cc
+ external_audio_decoder.cc
filter_graph.cc
ffmpeg_compatibility.cc
ffmpeg_decoder.cc
ui_signaller.cc
util.cc
version.cc
+ video_decoder.cc
video_source.cc
"""