diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-04-28 01:20:55 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-04-28 01:20:55 +0100 |
| commit | d487ee606113a2b4bbab1810758e9777792f1cac (patch) | |
| tree | 4e9f29b582e9f0ab097419f27a7fb3b49da25667 /src/lib | |
| parent | f85101ff72bf93ab3546ea0c4ec8e3f1242f256f (diff) | |
Add total-playlist loop option; move state of content list into the Playlist.
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/audio_mapping.cc | 2 | ||||
| -rw-r--r-- | src/lib/film.cc | 159 | ||||
| -rw-r--r-- | src/lib/film.h | 18 | ||||
| -rw-r--r-- | src/lib/player.cc | 114 | ||||
| -rw-r--r-- | src/lib/playlist.cc | 132 | ||||
| -rw-r--r-- | src/lib/playlist.h | 26 |
6 files changed, 268 insertions, 183 deletions
diff --git a/src/lib/audio_mapping.cc b/src/lib/audio_mapping.cc index 83c748f1a..7e28aa5c4 100644 --- a/src/lib/audio_mapping.cc +++ b/src/lib/audio_mapping.cc @@ -115,7 +115,7 @@ AudioMapping::set_from_xml (ContentList const & content, shared_ptr<const cxml:: continue; } - shared_ptr<const AudioContent> ac = dynamic_pointer_cast<AudioContent> (*j); + shared_ptr<const AudioContent> ac = dynamic_pointer_cast<const AudioContent> (*j); assert (ac); add (AudioMapping::Channel (ac, (*i)->number_child<int> ("ContentIndex")), static_cast<libdcp::Channel> ((*i)->number_child<int> ("DCP"))); diff --git a/src/lib/film.cc b/src/lib/film.cc index 98c6c0610..5481bd012 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -113,7 +113,8 @@ Film::Film (string d, bool must_exist) { set_dci_date_today (); - _playlist->ContentChanged.connect (bind (&Film::content_changed, this, _1, _2)); + _playlist->Changed.connect (bind (&Film::playlist_changed, this)); + _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2)); /* Make state.directory a complete path without ..s (where possible) (Code swiped from Adam Bowen on stackoverflow) @@ -156,7 +157,7 @@ Film::Film (Film const & o) : boost::enable_shared_from_this<Film> (o) /* note: the copied film shares the original's log */ , _log (o._log) - , _playlist (new Playlist) + , _playlist (new Playlist (o._playlist)) , _directory (o._directory) , _name (o._name) , _use_dci_name (o._use_dci_name) @@ -182,13 +183,7 @@ Film::Film (Film const & o) , _dci_date (o._dci_date) , _dirty (o._dirty) { - for (ContentList::const_iterator i = o._content.begin(); i != o._content.end(); ++i) { - _content.push_back ((*i)->clone ()); - } - - _playlist->ContentChanged.connect (bind (&Film::content_changed, this, _1, _2)); - - _playlist->setup (_content); + _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2)); } string @@ -320,7 +315,7 @@ Film::make_dcp () throw MissingSettingError (_("format")); } - if (content().empty ()) { + if (_playlist->content().empty ()) { throw MissingSettingError (_("content")); } @@ -405,8 +400,6 @@ Film::encoded_frames () const void Film::write_metadata () const { - ContentList the_content = content (); - boost::mutex::scoped_lock lm (_state_mutex); LocaleGuard lg; @@ -460,10 +453,7 @@ Film::write_metadata () const root->add_child("DCPFrameRate")->add_child_text (boost::lexical_cast<string> (_dcp_frame_rate)); root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date)); _audio_mapping.as_xml (root->add_child("AudioMapping")); - - for (ContentList::iterator i = the_content.begin(); i != the_content.end(); ++i) { - (*i)->as_xml (root->add_child ("Content")); - } + _playlist->as_xml (root->add_child ("Playlist")); doc.write_to_file_formatted (file ("metadata.xml")); @@ -537,29 +527,10 @@ Film::read_metadata () _dcp_frame_rate = f.number_child<int> ("DCPFrameRate"); _dci_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate")); - list<shared_ptr<cxml::Node> > c = f.node_children ("Content"); - for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) { - - string const type = (*i)->string_child ("Type"); - boost::shared_ptr<Content> c; - - if (type == "FFmpeg") { - c.reset (new FFmpegContent (*i)); - } else if (type == "ImageMagick") { - c.reset (new ImageMagickContent (*i)); - } else if (type == "Sndfile") { - c.reset (new SndfileContent (*i)); - } - - _content.push_back (c); - } - - /* This must come after we've loaded the content, as we're looking things up in _content */ - _audio_mapping.set_from_xml (_content, f.node_child ("AudioMapping")); + _playlist->set_from_xml (f.node_child ("Playlist")); + _audio_mapping.set_from_xml (_playlist->content(), f.node_child ("AudioMapping")); _dirty = false; - - _playlist->setup (_content); } libdcp::Size @@ -766,10 +737,11 @@ Film::set_trust_content_headers (bool t) signal_changed (TRUST_CONTENT_HEADERS); - if (!_trust_content_headers && !content().empty()) { + + ContentList content = _playlist->content (); + if (!_trust_content_headers && !content.empty()) { /* We just said that we don't trust the content's header */ - ContentList c = content (); - for (ContentList::iterator i = c.begin(); i != c.end(); ++i) { + for (ContentList::iterator i = content.begin(); i != content.end(); ++i) { examine_content (*i); } } @@ -1023,7 +995,6 @@ Film::signal_changed (Property p) switch (p) { case Film::CONTENT: - _playlist->setup (content ()); set_dcp_frame_rate (best_dcp_frame_rate (video_frame_rate ())); set_audio_mapping (_playlist->default_audio_mapping ()); break; @@ -1104,73 +1075,35 @@ Film::player () const return shared_ptr<Player> (new Player (shared_from_this (), _playlist)); } +ContentList +Film::content () const +{ + return _playlist->content (); +} + void Film::add_content (shared_ptr<Content> c) { - { - boost::mutex::scoped_lock lm (_state_mutex); - _content.push_back (c); - } - - signal_changed (CONTENT); - + _playlist->add (c); examine_content (c); } void Film::remove_content (shared_ptr<Content> c) { - { - boost::mutex::scoped_lock lm (_state_mutex); - ContentList::iterator i = find (_content.begin(), _content.end(), c); - if (i != _content.end ()) { - _content.erase (i); - } - } - - signal_changed (CONTENT); + _playlist->remove (c); } void Film::move_content_earlier (shared_ptr<Content> c) { - { - boost::mutex::scoped_lock lm (_state_mutex); - ContentList::iterator i = find (_content.begin(), _content.end(), c); - if (i == _content.begin () || i == _content.end()) { - return; - } - - ContentList::iterator j = i; - --j; - - swap (*i, *j); - } - - signal_changed (CONTENT); + _playlist->move_earlier (c); } void Film::move_content_later (shared_ptr<Content> c) { - { - boost::mutex::scoped_lock lm (_state_mutex); - ContentList::iterator i = find (_content.begin(), _content.end(), c); - if (i == _content.end()) { - return; - } - - ContentList::iterator j = i; - ++j; - if (j == _content.end ()) { - return; - } - - swap (*i, *j); - } - - signal_changed (CONTENT); - + _playlist->move_later (c); } ContentAudioFrame @@ -1221,26 +1154,10 @@ Film::content_length () const return _playlist->content_length (); } -/** Unfortunately this is needed as the GUI has FFmpeg-specific controls */ -shared_ptr<FFmpegContent> -Film::ffmpeg () const -{ - boost::mutex::scoped_lock lm (_state_mutex); - - for (ContentList::const_iterator i = _content.begin (); i != _content.end(); ++i) { - shared_ptr<FFmpegContent> f = boost::dynamic_pointer_cast<FFmpegContent> (*i); - if (f) { - return f; - } - } - - return shared_ptr<FFmpegContent> (); -} - vector<FFmpegSubtitleStream> Film::ffmpeg_subtitle_streams () const { - shared_ptr<FFmpegContent> f = ffmpeg (); + shared_ptr<FFmpegContent> f = _playlist->ffmpeg (); if (f) { return f->subtitle_streams (); } @@ -1251,7 +1168,7 @@ Film::ffmpeg_subtitle_streams () const boost::optional<FFmpegSubtitleStream> Film::ffmpeg_subtitle_stream () const { - shared_ptr<FFmpegContent> f = ffmpeg (); + shared_ptr<FFmpegContent> f = _playlist->ffmpeg (); if (f) { return f->subtitle_stream (); } @@ -1262,7 +1179,7 @@ Film::ffmpeg_subtitle_stream () const vector<FFmpegAudioStream> Film::ffmpeg_audio_streams () const { - shared_ptr<FFmpegContent> f = ffmpeg (); + shared_ptr<FFmpegContent> f = _playlist->ffmpeg (); if (f) { return f->audio_streams (); } @@ -1273,7 +1190,7 @@ Film::ffmpeg_audio_streams () const boost::optional<FFmpegAudioStream> Film::ffmpeg_audio_stream () const { - shared_ptr<FFmpegContent> f = ffmpeg (); + shared_ptr<FFmpegContent> f = _playlist->ffmpeg (); if (f) { return f->audio_stream (); } @@ -1284,7 +1201,7 @@ Film::ffmpeg_audio_stream () const void Film::set_ffmpeg_subtitle_stream (FFmpegSubtitleStream s) { - shared_ptr<FFmpegContent> f = ffmpeg (); + shared_ptr<FFmpegContent> f = _playlist->ffmpeg (); if (f) { f->set_subtitle_stream (s); } @@ -1293,7 +1210,7 @@ Film::set_ffmpeg_subtitle_stream (FFmpegSubtitleStream s) void Film::set_ffmpeg_audio_stream (FFmpegAudioStream s) { - shared_ptr<FFmpegContent> f = ffmpeg (); + shared_ptr<FFmpegContent> f = _playlist->ffmpeg (); if (f) { f->set_audio_stream (s); } @@ -1311,7 +1228,7 @@ Film::set_audio_mapping (AudioMapping m) } void -Film::content_changed (boost::weak_ptr<Content> c, int p) +Film::playlist_content_changed (boost::weak_ptr<Content> c, int p) { if (p == VideoContentProperty::VIDEO_FRAME_RATE) { set_dcp_frame_rate (best_dcp_frame_rate (video_frame_rate ())); @@ -1323,3 +1240,21 @@ Film::content_changed (boost::weak_ptr<Content> c, int p) ui_signaller->emit (boost::bind (boost::ref (ContentChanged), c, p)); } } + +void +Film::playlist_changed () +{ + signal_changed (CONTENT); +} + +int +Film::loop () const +{ + return _playlist->loop (); +} + +void +Film::set_loop (int c) +{ + _playlist->set_loop (c); +} diff --git a/src/lib/film.h b/src/lib/film.h index 43b5d1a17..f4d7cde67 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -106,6 +106,8 @@ public: /* Proxies for some Playlist methods */ + ContentList content () const; + ContentAudioFrame audio_length () const; int audio_channels () const; int audio_frame_rate () const; @@ -125,6 +127,9 @@ public: void set_ffmpeg_subtitle_stream (FFmpegSubtitleStream); void set_ffmpeg_audio_stream (FFmpegAudioStream); + void set_loop (int); + int loop () const; + enum TrimType { CPL, ENCODE @@ -138,8 +143,9 @@ public: NAME, USE_DCI_NAME, TRUST_CONTENT_HEADERS, - /** The content list has changed (i.e. content has been added, moved around or removed) */ + /** The playlist's content list has changed (i.e. content has been added, moved around or removed) */ CONTENT, + LOOP, DCP_CONTENT_TYPE, FORMAT, CROP, @@ -184,11 +190,6 @@ public: return _trust_content_headers; } - ContentList content () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _content; - } - DCPContentType const * dcp_content_type () const { boost::mutex::scoped_lock lm (_state_mutex); return _dcp_content_type; @@ -336,8 +337,8 @@ private: void analyse_audio_finished (); std::string video_state_identifier () const; void read_metadata (); - void content_changed (boost::weak_ptr<Content>, int); - boost::shared_ptr<FFmpegContent> ffmpeg () const; + void playlist_changed (); + void playlist_content_changed (boost::weak_ptr<Content>, int); void setup_default_audio_mapping (); std::string filename_safe_name () const; @@ -359,7 +360,6 @@ private: /** True if a auto-generated DCI-compliant name should be used for our DCP */ bool _use_dci_name; bool _trust_content_headers; - ContentList _content; /** The type of content that this Film represents (feature, trailer etc.) */ DCPContentType const * _dcp_content_type; /** The format to present this Film in (flat, scope, etc.) */ diff --git a/src/lib/player.cc b/src/lib/player.cc index b21224587..d62d8f8b6 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -229,67 +229,69 @@ Player::setup_decoders () double video_so_far = 0; double audio_so_far = 0; - - list<shared_ptr<const VideoContent> > vc = _playlist->video (); - for (list<shared_ptr<const VideoContent> >::iterator i = vc.begin(); i != vc.end(); ++i) { - - shared_ptr<const VideoContent> video_content; - shared_ptr<const AudioContent> audio_content; - shared_ptr<VideoDecoder> video_decoder; - shared_ptr<AudioDecoder> audio_decoder; - - /* XXX: into content? */ - - shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i); - if (fc) { - shared_ptr<FFmpegDecoder> fd ( - new FFmpegDecoder ( - _film, fc, _video, - _audio && _playlist->audio_from() == Playlist::AUDIO_FFMPEG, - _subtitles - ) - ); + + for (int l = 0; l < _playlist->loop(); ++l) { + list<shared_ptr<const VideoContent> > vc = _playlist->video (); + for (list<shared_ptr<const VideoContent> >::iterator i = vc.begin(); i != vc.end(); ++i) { - video_content = fc; - audio_content = fc; - video_decoder = fd; - audio_decoder = fd; - } - - shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i); - if (ic) { - video_content = ic; - video_decoder.reset (new ImageMagickDecoder (_film, ic)); + shared_ptr<const VideoContent> video_content; + shared_ptr<const AudioContent> audio_content; + shared_ptr<VideoDecoder> video_decoder; + shared_ptr<AudioDecoder> audio_decoder; + + /* XXX: into content? */ + + shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i); + if (fc) { + shared_ptr<FFmpegDecoder> fd ( + new FFmpegDecoder ( + _film, fc, _video, + _audio && _playlist->audio_from() == Playlist::AUDIO_FFMPEG, + _subtitles + ) + ); + + video_content = fc; + audio_content = fc; + video_decoder = fd; + audio_decoder = fd; + } + + shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i); + if (ic) { + video_content = ic; + video_decoder.reset (new ImageMagickDecoder (_film, ic)); + } + + video_decoder->connect_video (shared_from_this ()); + _video_decoders.push_back (video_decoder); + _video_start.push_back (video_so_far); + video_so_far += video_content->video_length() / video_content->video_frame_rate(); + + if (audio_decoder && _playlist->audio_from() == Playlist::AUDIO_FFMPEG) { + audio_decoder->Audio.connect (bind (&Player::process_audio, this, audio_content, _1, _2)); + _audio_decoders.push_back (audio_decoder); + _audio_start.push_back (audio_so_far); + audio_so_far += double(audio_content->audio_length()) / audio_content->audio_frame_rate(); + } } - video_decoder->connect_video (shared_from_this ()); - _video_decoders.push_back (video_decoder); - _video_start.push_back (video_so_far); - video_so_far += video_content->video_length() / video_content->video_frame_rate(); - - if (audio_decoder && _playlist->audio_from() == Playlist::AUDIO_FFMPEG) { - audio_decoder->Audio.connect (bind (&Player::process_audio, this, audio_content, _1, _2)); - _audio_decoders.push_back (audio_decoder); - _audio_start.push_back (audio_so_far); - audio_so_far += double(audio_content->audio_length()) / audio_content->audio_frame_rate(); - } - } - - _video_decoder = 0; - _sequential_audio_decoder = 0; - - if (_playlist->audio_from() == Playlist::AUDIO_SNDFILE) { + _video_decoder = 0; + _sequential_audio_decoder = 0; - list<shared_ptr<const AudioContent> > ac = _playlist->audio (); - for (list<shared_ptr<const AudioContent> >::iterator i = ac.begin(); i != ac.end(); ++i) { + if (_playlist->audio_from() == Playlist::AUDIO_SNDFILE) { - shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i); - assert (sc); - - shared_ptr<AudioDecoder> d (new SndfileDecoder (_film, sc)); - d->Audio.connect (bind (&Player::process_audio, this, sc, _1, _2)); - _audio_decoders.push_back (d); - _audio_start.push_back (audio_so_far); + list<shared_ptr<const AudioContent> > ac = _playlist->audio (); + for (list<shared_ptr<const AudioContent> >::iterator i = ac.begin(); i != ac.end(); ++i) { + + shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i); + assert (sc); + + shared_ptr<AudioDecoder> d (new SndfileDecoder (_film, sc)); + d->Audio.connect (bind (&Player::process_audio, this, sc, _1, _2)); + _audio_decoders.push_back (d); + _audio_start.push_back (audio_so_far); + } } } } diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc index 3c69ae15f..216244bd0 100644 --- a/src/lib/playlist.cc +++ b/src/lib/playlist.cc @@ -17,6 +17,7 @@ */ +#include <libcxml/cxml.h> #include <boost/shared_ptr.hpp> #include <boost/lexical_cast.hpp> #include "playlist.h" @@ -26,6 +27,7 @@ #include "ffmpeg_decoder.h" #include "ffmpeg_content.h" #include "imagemagick_decoder.h" +#include "imagemagick_content.h" #include "job.h" using std::list; @@ -41,12 +43,24 @@ using boost::lexical_cast; Playlist::Playlist () : _audio_from (AUDIO_FFMPEG) + , _loop (1) { } +Playlist::Playlist (shared_ptr<const Playlist> other) + : _audio_from (other->_audio_from) + , _loop (other->_loop) +{ + for (ContentList::const_iterator i = other->_content.begin(); i != other->_content.end(); ++i) { + _content.push_back ((*i)->clone ()); + } + + setup (); +} + void -Playlist::setup (ContentList content) +Playlist::setup () { _audio_from = AUDIO_FFMPEG; @@ -59,7 +73,7 @@ Playlist::setup (ContentList content) _content_connections.clear (); - for (ContentList::const_iterator i = content.begin(); i != content.end(); ++i) { + for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) { /* Video is video */ shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*i); @@ -114,7 +128,7 @@ Playlist::audio_length () const break; } - return len; + return len * _loop; } /** @return number of audio channels */ @@ -182,7 +196,7 @@ Playlist::video_length () const len += (*i)->video_length (); } - return len; + return len * _loop; } bool @@ -258,6 +272,8 @@ Playlist::audio_digest () const } } + t += lexical_cast<string> (_loop); + return md5_digest (t.c_str(), t.length()); } @@ -274,6 +290,8 @@ Playlist::video_digest () const } } + t += lexical_cast<string> (_loop); + return md5_digest (t.c_str(), t.length()); } @@ -288,3 +306,109 @@ Playlist::content_length () const ContentVideoFrame (audio_length() * vfr / afr) ); } + +void +Playlist::set_from_xml (shared_ptr<const cxml::Node> node) +{ + list<shared_ptr<cxml::Node> > c = node->node_children ("Content"); + for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) { + + string const type = (*i)->string_child ("Type"); + boost::shared_ptr<Content> c; + + if (type == "FFmpeg") { + c.reset (new FFmpegContent (*i)); + } else if (type == "ImageMagick") { + c.reset (new ImageMagickContent (*i)); + } else if (type == "Sndfile") { + c.reset (new SndfileContent (*i)); + } + + _content.push_back (c); + } + + _loop = node->number_child<int> ("Loop"); +} + +void +Playlist::as_xml (xmlpp::Node* node) +{ + for (ContentList::iterator i = _content.begin(); i != _content.end(); ++i) { + (*i)->as_xml (node->add_child ("Content")); + } + + node->add_child("Loop")->add_child_text(lexical_cast<string> (_loop)); +} + +void +Playlist::add (shared_ptr<Content> c) +{ + _content.push_back (c); + setup (); +} + +void +Playlist::remove (shared_ptr<Content> c) +{ + ContentList::iterator i = find (_content.begin(), _content.end(), c); + if (i != _content.end ()) { + _content.erase (i); + } + + setup (); +} + +void +Playlist::move_earlier (shared_ptr<Content> c) +{ + ContentList::iterator i = find (_content.begin(), _content.end(), c); + if (i == _content.begin () || i == _content.end()) { + return; + } + + ContentList::iterator j = i; + --j; + + swap (*i, *j); + + setup (); +} + +void +Playlist::move_later (shared_ptr<Content> c) +{ + ContentList::iterator i = find (_content.begin(), _content.end(), c); + if (i == _content.end()) { + return; + } + + ContentList::iterator j = i; + ++j; + if (j == _content.end ()) { + return; + } + + swap (*i, *j); + + setup (); +} + +void +Playlist::set_loop (int l) +{ + _loop = l; + Changed (); +} + +shared_ptr<FFmpegContent> +Playlist::ffmpeg () const +{ + for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) { + shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (*i); + if (fc) { + return fc; + } + } + + return shared_ptr<FFmpegContent> (); +} diff --git a/src/lib/playlist.h b/src/lib/playlist.h index 85bde64ff..935bbb2bd 100644 --- a/src/lib/playlist.h +++ b/src/lib/playlist.h @@ -51,8 +51,15 @@ class Playlist { public: Playlist (); + Playlist (boost::shared_ptr<const Playlist>); - void setup (ContentList); + void as_xml (xmlpp::Node *); + void set_from_xml (boost::shared_ptr<const cxml::Node>); + + void add (boost::shared_ptr<Content>); + void remove (boost::shared_ptr<Content>); + void move_earlier (boost::shared_ptr<Content>); + void move_later (boost::shared_ptr<Content>); ContentAudioFrame audio_length () const; int audio_channels () const; @@ -75,6 +82,12 @@ public: return _audio_from; } + ContentList content () const { + return _content; + } + + boost::shared_ptr<FFmpegContent> ffmpeg () const; + std::list<boost::shared_ptr<const VideoContent> > video () const { return _video; } @@ -86,15 +99,24 @@ public: std::string audio_digest () const; std::string video_digest () const; + int loop () const { + return _loop; + } + + void set_loop (int l); + mutable boost::signals2::signal<void ()> Changed; mutable boost::signals2::signal<void (boost::weak_ptr<Content>, int)> ContentChanged; private: + void setup (); void content_changed (boost::weak_ptr<Content>, int); /** where we should get our audio from */ AudioFrom _audio_from; + /** all our content */ + ContentList _content; /** all our content which contains video */ std::list<boost::shared_ptr<const VideoContent> > _video; /** all our content which contains audio. This may contain the same objects @@ -102,5 +124,7 @@ private: */ std::list<boost::shared_ptr<const AudioContent> > _audio; + int _loop; + std::list<boost::signals2::connection> _content_connections; }; |
