Basic support for selection of audio / subtitle streams.
authorCarl Hetherington <cth@carlh.net>
Wed, 17 Oct 2012 12:47:50 +0000 (13:47 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 17 Oct 2012 12:47:50 +0000 (13:47 +0100)
13 files changed:
ChangeLog
src/lib/decoder.h
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_decoder.h
src/lib/film.cc
src/lib/film.h
src/lib/film_state.cc
src/lib/film_state.h
src/lib/stream.cc [new file with mode: 0644]
src/lib/stream.h [new file with mode: 0644]
src/lib/wscript
src/wx/film_editor.cc
src/wx/film_editor.h

index 41bfd88b49de4217c1d281d3fd7b1e6e90db7763..fa2e67db173abb2db86afbb817a73fe590979581 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -6,6 +6,9 @@
 
        * 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.
index 8a04ec9f2d77d4a553c80cf8f830d391bd383ec5..312cbbe8e06b8e791d06ea999cc2555a2422f4f3 100644 (file)
@@ -30,6 +30,7 @@
 #include <boost/shared_ptr.hpp>
 #include <sigc++/sigc++.h>
 #include "util.h"
+#include "stream.h"
 
 class Job;
 class FilmState;
@@ -79,6 +80,17 @@ public:
        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.
index e85439f6ed1adf190eea24435b5a749c39e7715b..4cec7ef353858879ebeb4f4e10b044ffad6a6b74 100644 (file)
@@ -110,9 +110,15 @@ FFmpegDecoder::setup_general ()
                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));
                }
        }
 
@@ -356,3 +362,54 @@ FFmpegDecoder::has_subtitles () const
 {
        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 ();
+}
index ec822bf799fd78fa8f88fd61b9a4a67add9df770..9abc7f49ad743760ef9843b19a9b918a31e8f6d3 100644 (file)
@@ -39,6 +39,7 @@ struct AVFormatContext;
 struct AVFrame;
 struct AVBufferContext;
 struct AVCodec;
+struct AVStream;
 class Job;
 class FilmState;
 class Options;
@@ -65,6 +66,12 @@ public:
        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 ();
@@ -81,11 +88,16 @@ private:
 
        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;
index ce83b132433fb87bcee662d55c0e0ee82b5abc10..9e1640d58661828d2c0d51134beb2c287ec66b8f 100644 (file)
@@ -201,6 +201,10 @@ Film::set_content (string c)
        _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;
@@ -211,6 +215,8 @@ Film::set_content (string 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 */
index ae0ad7ad1379745ce7388941903fa2b261b60fd3..deff4b8a839c39084b15505ca626e546bcc18e0a 100644 (file)
@@ -162,6 +162,22 @@ public:
                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);
@@ -287,6 +303,7 @@ public:
                DCP_FRAMES,
                DCP_TRIM_ACTION,
                DCP_AB,
+               AUDIO_STREAM,
                AUDIO_GAIN,
                AUDIO_DELAY,
                THUMBS,
@@ -296,6 +313,7 @@ public:
                AUDIO_CHANNELS,
                AUDIO_SAMPLE_RATE,
                STILL_DURATION,
+               SUBTITLE_STREAM,
                WITH_SUBTITLES,
                SUBTITLE_OFFSET,
                SUBTITLE_SCALE,
index aa8bb55636e3b7f56eb417e735bb9f7326ab5edb..0d0bd6313667ca5e14c5d80322f600ea32868582 100644 (file)
@@ -79,12 +79,20 @@ FilmState::write_metadata (ofstream& f) const
        }
        
        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.
@@ -99,14 +107,16 @@ FilmState::write_metadata (ofstream& f) const
        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.
@@ -151,6 +161,8 @@ FilmState::read_metadata (string k, string v)
                }
        } 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") {
@@ -163,6 +175,22 @@ FilmState::read_metadata (string k, string v)
                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 */
@@ -188,20 +216,10 @@ FilmState::read_metadata (string k, string v)
                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));
        }
 }
 
index ea287fb3b7eb3290efbe32139c3fb28d9d04b68e..38176f22405456f328e62b89f8ba884a025119d8 100644 (file)
@@ -34,6 +34,7 @@ extern "C" {
 #include "scaler.h"
 #include "util.h"
 #include "trim_action.h"
+#include "stream.h"
 
 class Format;
 class DCPContentType;
@@ -60,9 +61,11 @@ public:
                , 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)
@@ -130,12 +133,14 @@ public:
            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
@@ -172,6 +177,8 @@ public:
        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;
diff --git a/src/lib/stream.cc b/src/lib/stream.cc
new file mode 100644 (file)
index 0000000..e407389
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+    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);
+}
diff --git a/src/lib/stream.h b/src/lib/stream.h
new file mode 100644 (file)
index 0000000..764c03d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+    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
index 63847224c43042c6110e1b4acd2a1164d593e809..6ce216d81f799151b6430872cc907cd5befc472f 100644 (file)
@@ -43,6 +43,7 @@ def build(bld):
                  screen.cc
                 server.cc
                  sound_processor.cc
+                 stream.cc
                  subtitle.cc
                 thumbs_job.cc
                  tiff_decoder.cc
index 471ca215f2fd377f030c88ea892c5f973caca59b..21cd0acfa0946c930b53435a4d7d29198175a254 100644 (file)
@@ -119,6 +119,10 @@ FilmEditor::FilmEditor (Film* f, wxWindow* parent)
        _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);
@@ -143,7 +147,9 @@ FilmEditor::FilmEditor (Film* f, wxWindow* parent)
        _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);
@@ -328,6 +334,7 @@ FilmEditor::content_changed (wxCommandEvent &)
        setup_visibility ();
        setup_formats ();
        setup_subtitle_button ();
+       setup_streams ();
 }
 
 /** Called when the DCP A/B switch has been toggled */
@@ -404,6 +411,7 @@ FilmEditor::film_changed (Film::Property p)
                setup_visibility ();
                setup_formats ();
                setup_subtitle_button ();
+               setup_streams ();
                break;
        case Film::FORMAT:
        {
@@ -524,6 +532,12 @@ FilmEditor::film_changed (Film::Property p)
        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;
        }
 }
 
@@ -853,3 +867,36 @@ FilmEditor::edit_dci_button_clicked (wxCommandEvent &)
        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);
+}
index 6fb8973bfeb5c33c8b6926045a099b66a329449e..9d159d307875bdf93de12b787394dc58e1fe54a8 100644 (file)
@@ -76,6 +76,8 @@ private:
        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 *);
@@ -107,6 +109,7 @@ private:
        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 */
@@ -114,6 +117,7 @@ private:
        /** 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 */