* Fix a few bugs.
+ * Basic support for selection of audio
+ and subtitle streams.
+
2012-10-09 Carl Hetherington <cth@carlh.net>
* Version 0.55 released.
#include <boost/shared_ptr.hpp>
#include <sigc++/sigc++.h>
#include "util.h"
+#include "stream.h"
class Job;
class FilmState;
int last_video_frame () const {
return _video_frame;
}
+
+ virtual std::vector<Stream> audio_streams () const {
+ return std::vector<Stream> ();
+ }
+
+ virtual std::vector<Stream> subtitle_streams () const {
+ return std::vector<Stream> ();
+ }
+
+ virtual void set_audio_stream (Stream s) {}
+ virtual void set_subtitle_stream (Stream s) {}
/** Emitted when a video frame is ready.
* First parameter is the frame.
if (_format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
_video_stream = i;
} else if (_format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
- _audio_stream = i;
+ if (_audio_stream == -1) {
+ _audio_stream = i;
+ }
+ _audio_streams.push_back (Stream (stream_name (_format_context->streams[i]), i));
} else if (_format_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
- _subtitle_stream = i;
+ if (_subtitle_stream == -1) {
+ _subtitle_stream = i;
+ }
+ _subtitle_streams.push_back (Stream (stream_name (_format_context->streams[i]), i));
}
}
{
return (_subtitle_stream != -1);
}
+
+vector<Stream>
+FFmpegDecoder::audio_streams () const
+{
+ return _audio_streams;
+}
+
+vector<Stream>
+FFmpegDecoder::subtitle_streams () const
+{
+ return _subtitle_streams;
+}
+
+void
+FFmpegDecoder::set_audio_stream (int s)
+{
+ _audio_stream = s;
+ setup_audio ();
+}
+
+void
+FFmpegDecoder::set_subtitle_stream (int s)
+{
+ _subtitle_stream = s;
+ setup_subtitle ();
+}
+
+string
+FFmpegDecoder::stream_name (AVStream* s) const
+{
+ stringstream n;
+
+ AVDictionaryEntry const * lang = av_dict_get (s->metadata, "language", 0, 0);
+ if (lang) {
+ n << lang->value;
+ }
+
+ AVDictionaryEntry const * title = av_dict_get (s->metadata, "title", 0, 0);
+ if (title) {
+ if (!n.str().empty()) {
+ n << " ";
+ }
+ n << title->value;
+ }
+
+ if (n.str().empty()) {
+ n << "unknown";
+ }
+
+ return n.str ();
+}
struct AVFrame;
struct AVBufferContext;
struct AVCodec;
+struct AVStream;
class Job;
class FilmState;
class Options;
int64_t audio_channel_layout () const;
bool has_subtitles () const;
+ std::vector<Stream> audio_streams () const;
+ std::vector<Stream> subtitle_streams () const;
+
+ void set_audio_stream (int id);
+ void set_subtitle_stream (int id);
+
private:
bool do_pass ();
void maybe_add_subtitle ();
+ std::string stream_name (AVStream* s) const;
+
AVFormatContext* _format_context;
int _video_stream;
int _audio_stream; ///< may be < 0 if there is no audio
int _subtitle_stream; ///< may be < 0 if there is no subtitle
AVFrame* _frame;
+
+ std::vector<Stream> _audio_streams;
+ std::vector<Stream> _subtitle_streams;
AVCodecContext* _video_codec_context;
AVCodec* _video_codec;
_state.audio_sample_rate = d->audio_sample_rate ();
_state.audio_sample_format = d->audio_sample_format ();
_state.has_subtitles = d->has_subtitles ();
+ _state.audio_streams = d->audio_streams ();
+ _state.subtitle_streams = d->subtitle_streams ();
+ _state.audio_stream = _state.audio_streams.empty() ? -1 : _state.audio_streams.front().id;
+ _state.subtitle_stream = _state.subtitle_streams.empty() ? -1 : _state.subtitle_streams.front().id;
_state.content_digest = md5_digest (s->content_path ());
_state.content = c;
signal_changed (AUDIO_CHANNELS);
signal_changed (AUDIO_SAMPLE_RATE);
signal_changed (CONTENT);
+ signal_changed (AUDIO_STREAM);
+ signal_changed (SUBTITLE_STREAM);
}
/** Set the format that this Film should be shown in */
return _state.dcp_content_type;
}
+ std::vector<Stream> audio_streams () const {
+ return _state.audio_streams;
+ }
+
+ int audio_stream () const {
+ return _state.audio_stream;
+ }
+
+ std::vector<Stream> subtitle_streams () const {
+ return _state.subtitle_streams;
+ }
+
+ int subtitle_stream () const {
+ return _state.subtitle_stream;
+ }
+
void set_dcp_frames (int);
void set_dcp_trim_action (TrimAction);
void set_dcp_ab (bool);
DCP_FRAMES,
DCP_TRIM_ACTION,
DCP_AB,
+ AUDIO_STREAM,
AUDIO_GAIN,
AUDIO_DELAY,
THUMBS,
AUDIO_CHANNELS,
AUDIO_SAMPLE_RATE,
STILL_DURATION,
+ SUBTITLE_STREAM,
WITH_SUBTITLES,
SUBTITLE_OFFSET,
SUBTITLE_SCALE,
}
f << "dcp_ab " << (dcp_ab ? "1" : "0") << "\n";
+ f << "selected_audio_stream " << audio_stream << "\n";
f << "audio_gain " << audio_gain << "\n";
f << "audio_delay " << audio_delay << "\n";
f << "still_duration " << still_duration << "\n";
f << "with_subtitles " << with_subtitles << "\n";
f << "subtitle_offset " << subtitle_offset << "\n";
f << "subtitle_scale " << subtitle_scale << "\n";
+ f << "audio_language " << audio_language << "\n";
+ f << "subtitle_language " << subtitle_language << "\n";
+ f << "territory " << territory << "\n";
+ f << "rating " << rating << "\n";
+ f << "studio " << studio << "\n";
+ f << "facility " << facility << "\n";
+ f << "package_type " << package_type << "\n";
/* Cached stuff; this is information about our content; we could
look it up each time, but that's slow.
f << "audio_sample_rate " << audio_sample_rate << "\n";
f << "audio_sample_format " << audio_sample_format_to_string (audio_sample_format) << "\n";
f << "content_digest " << content_digest << "\n";
+ f << "selected_subtitle_stream " << subtitle_stream << "\n";
f << "has_subtitles " << has_subtitles << "\n";
- f << "audio_language " << audio_language << "\n";
- f << "subtitle_language " << subtitle_language << "\n";
- f << "territory " << territory << "\n";
- f << "rating " << rating << "\n";
- f << "studio " << studio << "\n";
- f << "facility " << facility << "\n";
- f << "package_type " << package_type << "\n";
+
+ for (vector<Stream>::const_iterator i = audio_streams.begin(); i != audio_streams.end(); ++i) {
+ f << "audio_stream " << i->to_string () << "\n";
+ }
+
+ for (vector<Stream>::const_iterator i = subtitle_streams.begin(); i != subtitle_streams.end(); ++i) {
+ f << "subtitle_stream " << i->to_string () << "\n";
+ }
}
/** Read state from a key / value pair.
}
} else if (k == "dcp_ab") {
dcp_ab = (v == "1");
+ } else if (k == "selected_audio_stream") {
+ audio_stream = atoi (v.c_str ());
} else if (k == "audio_gain") {
audio_gain = atof (v.c_str ());
} else if (k == "audio_delay") {
subtitle_offset = atoi (v.c_str ());
} else if (k == "subtitle_scale") {
subtitle_scale = atof (v.c_str ());
+ } else if (k == "selected_subtitle_stream") {
+ subtitle_stream = atoi (v.c_str ());
+ } else if (k == "audio_language") {
+ audio_language = v;
+ } else if (k == "subtitle_language") {
+ subtitle_language = v;
+ } else if (k == "territory") {
+ territory = v;
+ } else if (k == "rating") {
+ rating = v;
+ } else if (k == "studio") {
+ studio = v;
+ } else if (k == "facility") {
+ facility = v;
+ } else if (k == "package_type") {
+ package_type = v;
}
/* Cached stuff */
content_digest = v;
} else if (k == "has_subtitles") {
has_subtitles = (v == "1");
- } else if (k == "audio_language") {
- audio_language = v;
- } else if (k == "subtitle_language") {
- subtitle_language = v;
- } else if (k == "territory") {
- territory = v;
- } else if (k == "rating") {
- rating = v;
- } else if (k == "studio") {
- studio = v;
- } else if (k == "facility") {
- facility = v;
- } else if (k == "package_type") {
- package_type = v;
+ } else if (k == "audio_stream") {
+ audio_streams.push_back (Stream (v));
+ } else if (k == "subtitle_stream") {
+ subtitle_streams.push_back (Stream (v));
}
}
#include "scaler.h"
#include "util.h"
#include "trim_action.h"
+#include "stream.h"
class Format;
class DCPContentType;
, dcp_frames (0)
, dcp_trim_action (CUT)
, dcp_ab (false)
+ , audio_stream (-1)
, audio_gain (0)
, audio_delay (0)
, still_duration (10)
+ , subtitle_stream (-1)
, with_subtitles (false)
, subtitle_offset (0)
, subtitle_scale (1)
has the specified filters and post-processing.
*/
bool dcp_ab;
+ int audio_stream;
/** Gain to apply to audio in dB */
float audio_gain;
/** Delay to apply to audio (positive moves audio later) in milliseconds */
int audio_delay;
/** Duration to make still-sourced films (in seconds) */
int still_duration;
+ int subtitle_stream;
/** True if subtitles should be shown for this film */
bool with_subtitles;
/** y offset for placing subtitles, in source pixels; +ve is further down
std::string content_digest;
/** true if the source has subtitles */
bool has_subtitles;
+ std::vector<Stream> audio_streams;
+ std::vector<Stream> subtitle_streams;
private:
std::string thumb_file_for_frame (int) const;
--- /dev/null
+/*
+ Copyright (C) 2012 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 <sstream>
+#include "compose.hpp"
+#include "stream.h"
+
+using namespace std;
+
+Stream::Stream (string t)
+{
+ stringstream n (t);
+ n >> id;
+
+ size_t const s = t.find (' ');
+ if (s != string::npos) {
+ name = t.substr (s + 1);
+ }
+}
+
+string
+Stream::to_string () const
+{
+ return String::compose ("%1 %2", id, name);
+}
--- /dev/null
+/*
+ Copyright (C) 2012 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.
+
+*/
+
+#ifndef DVDOMATIC_STREAM_H
+#define DVDOMATIC_STREAM_H
+
+struct Stream
+{
+public:
+ Stream (std::string t);
+
+ Stream (std::string n, int i)
+ : name (n)
+ , id (i)
+ {}
+
+ std::string to_string () const;
+
+ std::string name;
+ int id;
+};
+
+#endif
screen.cc
server.cc
sound_processor.cc
+ stream.cc
subtitle.cc
thumbs_job.cc
tiff_decoder.cc
_scaler = new wxComboBox (this, wxID_ANY);
_sizer->Add (video_control (_scaler), 1);
+ video_control (add_label_to_sizer (_sizer, this, "Audio Stream"));
+ _audio_stream = new wxComboBox (this, wxID_ANY);
+ _sizer->Add (_audio_stream);
+
{
video_control (add_label_to_sizer (_sizer, this, "Audio Gain"));
wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
_with_subtitles = new wxCheckBox (this, wxID_ANY, wxT("With Subtitles"));
video_control (_with_subtitles);
_sizer->Add (_with_subtitles, 1);
- _sizer->AddSpacer (0);
+
+ _subtitle_stream = new wxComboBox (this, wxID_ANY);
+ _sizer->Add (_subtitle_stream);
video_control (add_label_to_sizer (_sizer, this, "Subtitle Offset"));
_subtitle_offset = new wxSpinCtrl (this);
setup_visibility ();
setup_formats ();
setup_subtitle_button ();
+ setup_streams ();
}
/** Called when the DCP A/B switch has been toggled */
setup_visibility ();
setup_formats ();
setup_subtitle_button ();
+ setup_streams ();
break;
case Film::FORMAT:
{
case Film::DCI_METADATA:
_dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
break;
+ case Film::AUDIO_STREAM:
+ set_selected_stream (_film->audio_streams(), _film->audio_stream(), _audio_stream);
+ break;
+ case Film::SUBTITLE_STREAM:
+ set_selected_stream (_film->subtitle_streams(), _film->subtitle_stream(), _subtitle_stream);
+ break;
}
}
d->ShowModal ();
d->Destroy ();
}
+
+void
+FilmEditor::setup_streams ()
+{
+ _audio_stream->Clear ();
+ vector<Stream> s = _film->audio_streams ();
+ for (vector<Stream>::iterator i = s.begin(); i != s.end(); ++i) {
+ _audio_stream->Append (std_to_wx (i->name));
+ }
+ set_selected_stream (_film->audio_streams(), _film->audio_stream(), _audio_stream);
+
+ _subtitle_stream->Clear ();
+ s = _film->subtitle_streams ();
+ for (vector<Stream>::iterator i = s.begin(); i != s.end(); ++i) {
+ _subtitle_stream->Append (std_to_wx (i->name));
+ }
+ set_selected_stream (_film->subtitle_streams(), _film->subtitle_stream(), _subtitle_stream);
+}
+
+void
+FilmEditor::set_selected_stream (vector<Stream> const & streams, int id, wxComboBox* combo) const
+{
+ if (id == -1) {
+ return;
+ }
+
+ size_t n = 0;
+ while (n < streams.size() && streams[n].id != id) {
+ ++n;
+ }
+ assert (n < streams.size());
+ combo->SetSelection (n);
+}
void set_things_sensitive (bool);
void setup_formats ();
void setup_subtitle_button ();
+ void setup_streams ();
+ void set_selected_stream (std::vector<Stream> const & streams, int id, wxComboBox* combo) const;
wxControl* video_control (wxControl *);
wxControl* still_control (wxControl *);
wxButton* _filters_button;
/** The Film's scaler */
wxComboBox* _scaler;
+ wxComboBox* _audio_stream;
/** The Film's audio gain */
wxSpinCtrl* _audio_gain;
/** A button to open the gain calculation dialogue */
/** The Film's audio delay */
wxSpinCtrl* _audio_delay;
wxCheckBox* _with_subtitles;
+ wxComboBox* _subtitle_stream;
wxSpinCtrl* _subtitle_offset;
wxSpinCtrl* _subtitle_scale;
/** The Film's DCP content type */