+2014-03-07 Carl Hetherington <cth@carlh.net>
+
+ * Add subtitle view.
+
2014-03-06 Carl Hetherington <cth@carlh.net>
* Version 1.65.0 released.
#include "log.h"
#include "resampler.h"
#include "util.h"
-#include "film.h"
#include "i18n.h"
using boost::optional;
using boost::shared_ptr;
-AudioDecoder::AudioDecoder (shared_ptr<const Film> film, shared_ptr<const AudioContent> content)
- : Decoder (film)
- , _audio_content (content)
+AudioDecoder::AudioDecoder (shared_ptr<const AudioContent> content)
+ : _audio_content (content)
{
if (content->output_audio_frame_rate() != content->content_audio_frame_rate() && content->audio_channels ()) {
_resampler.reset (new Resampler (content->content_audio_frame_rate(), content->output_audio_frame_rate(), content->audio_channels ()));
}
if (!_audio_position) {
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
_audio_position = time;
}
class AudioDecoder : public virtual Decoder
{
public:
- AudioDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const AudioContent>);
+ AudioDecoder (boost::shared_ptr<const AudioContent>);
boost::shared_ptr<const AudioContent> audio_content () const {
return _audio_content;
* @brief Parent class for decoders of content.
*/
-#include "film.h"
#include "decoder.h"
#include "decoded.h"
using std::cout;
using boost::shared_ptr;
-/** @param f Film.
- * @param o Decode options.
+/** @param o Decode options.
*/
-Decoder::Decoder (shared_ptr<const Film> f)
- : _film (f)
- , _done (false)
+Decoder::Decoder ()
+ : _done (false)
{
}
#include "types.h"
#include "dcpomatic_time.h"
-class Film;
class Decoded;
/** @class Decoder.
class Decoder : public boost::noncopyable
{
public:
- Decoder (boost::shared_ptr<const Film>);
+ Decoder ();
virtual ~Decoder () {}
/** Seek so that the next peek() will yield the next thing
virtual bool pass () = 0;
virtual void flush () {};
- /** The Film that we are decoding in */
- boost::weak_ptr<const Film> _film;
-
std::list<boost::shared_ptr<Decoded> > _pending;
bool _done;
};
Content::examine (job);
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
shared_ptr<FFmpegExaminer> examiner (new FFmpegExaminer (shared_from_this ()));
take_from_video_examiner (examiner);
ContentTime video_length = examiner->video_length ();
+
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
film->log()->log (String::compose ("Video length obtained from header as %1 frames", video_length.frames (video_frame_rate ())));
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
-#include "film.h"
#include "filter.h"
#include "exceptions.h"
#include "image.h"
using boost::dynamic_pointer_cast;
using dcp::Size;
-FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegContent> c, bool video, bool audio)
- : Decoder (f)
- , VideoDecoder (f, c)
- , AudioDecoder (f, c)
- , SubtitleDecoder (f)
+FFmpegDecoder::FFmpegDecoder (shared_ptr<const FFmpegContent> c, shared_ptr<Log> log, bool video, bool audio, bool subtitles)
+ : VideoDecoder (c)
+ , AudioDecoder (c)
, FFmpeg (c)
+ , _log (log)
, _subtitle_codec_context (0)
, _subtitle_codec (0)
, _decode_video (video)
, _decode_audio (audio)
+ , _decode_subtitles (subtitles)
, _pts_offset (0)
{
setup_subtitle ();
/* Maybe we should fail here, but for now we'll just finish off instead */
char buf[256];
av_strerror (r, buf, sizeof(buf));
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
- film->log()->log (String::compose (N_("error on av_read_frame (%1) (%2)"), buf, r));
+ _log->log (String::compose (N_("error on av_read_frame (%1) (%2)"), buf, r));
}
flush ();
return true;
}
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
int const si = _packet.stream_index;
if (si == _video_stream && _decode_video) {
decode_video_packet ();
} else if (_ffmpeg_content->audio_stream() && _ffmpeg_content->audio_stream()->uses_index (_format_context, si) && _decode_audio) {
decode_audio_packet ();
- } else if (_ffmpeg_content->subtitle_stream() && _ffmpeg_content->subtitle_stream()->uses_index (_format_context, si) && film->with_subtitles ()) {
+ } else if (_ffmpeg_content->subtitle_stream() && _ffmpeg_content->subtitle_stream()->uses_index (_format_context, si) && _decode_subtitles) {
decode_subtitle_packet ();
}
int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, ©_packet);
if (decode_result < 0) {
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
- film->log()->log (String::compose ("avcodec_decode_audio4 failed (%1)", decode_result));
+ _log->log (String::compose ("avcodec_decode_audio4 failed (%1)", decode_result));
return;
}
}
if (i == _filter_graphs.end ()) {
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
graph.reset (new FilterGraph (_ffmpeg_content, dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
_filter_graphs.push_back (graph);
-
- film->log()->log (String::compose (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format));
+ _log->log (String::compose (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format));
} else {
graph = *i;
}
if (i->second != AV_NOPTS_VALUE) {
video (image, false, ContentTime::from_seconds (i->second * av_q2d (_format_context->streams[_video_stream]->time_base)) + _pts_offset);
} else {
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
- film->log()->log ("Dropping frame without PTS");
+ _log->log ("Dropping frame without PTS");
}
}
#include "subtitle_decoder.h"
#include "ffmpeg.h"
-class Film;
+class Log;
class FilterGraph;
class ffmpeg_pts_offset_test;
class FFmpegDecoder : public VideoDecoder, public AudioDecoder, public SubtitleDecoder, public FFmpeg
{
public:
- FFmpegDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const FFmpegContent>, bool video, bool audio);
+ FFmpegDecoder (boost::shared_ptr<const FFmpegContent>, boost::shared_ptr<Log>, bool video, bool audio, bool subtitles);
~FFmpegDecoder ();
void seek (ContentTime time, bool);
int minimal_run (boost::function<bool (boost::optional<ContentTime>, boost::optional<ContentTime>, int)>);
void seek_and_flush (ContentTime);
+ boost::shared_ptr<Log> _log;
AVCodecContext* _subtitle_codec_context; ///< may be 0 if there is no subtitle
AVCodec* _subtitle_codec; ///< may be 0 if there is no subtitle
bool _decode_video;
bool _decode_audio;
+ bool _decode_subtitles;
ContentTime _pts_offset;
};
using boost::shared_ptr;
using dcp::Size;
-ImageDecoder::ImageDecoder (shared_ptr<const Film> f, shared_ptr<const ImageContent> c)
- : Decoder (f)
- , VideoDecoder (f, c)
+ImageDecoder::ImageDecoder (shared_ptr<const ImageContent> c)
+ : VideoDecoder (c)
, _image_content (c)
{
class ImageDecoder : public VideoDecoder
{
public:
- ImageDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const ImageContent>);
+ ImageDecoder (boost::shared_ptr<const ImageContent>);
boost::shared_ptr<const ImageContent> content () {
return _image_content;
/* FFmpeg */
shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
if (fc) {
- decoder.reset (new FFmpegDecoder (_film, fc, _video, _audio));
+ decoder.reset (new FFmpegDecoder (fc, _film->log(), _video, _audio, _film->with_subtitles ()));
frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
}
}
if (!decoder) {
- decoder.reset (new ImageDecoder (_film, ic));
+ decoder.reset (new ImageDecoder (ic));
}
frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
/* SndfileContent */
shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
if (sc) {
- decoder.reset (new SndfileDecoder (_film, sc));
+ decoder.reset (new SndfileDecoder (sc));
frc = best_overlap_frc;
}
/* SubRipContent */
shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
if (rc) {
- decoder.reset (new SubRipDecoder (_film, rc));
+ decoder.reset (new SubRipDecoder (rc));
frc = best_overlap_frc;
}
job->set_progress_unknown ();
Content::examine (job);
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- SndfileDecoder dec (film, shared_from_this());
+ SndfileDecoder dec (shared_from_this());
{
boost::mutex::scoped_lock lm (_mutex);
#include <sndfile.h>
#include "sndfile_content.h"
#include "sndfile_decoder.h"
-#include "film.h"
#include "exceptions.h"
#include "audio_buffers.h"
using std::cout;
using boost::shared_ptr;
-SndfileDecoder::SndfileDecoder (shared_ptr<const Film> f, shared_ptr<const SndfileContent> c)
- : Decoder (f)
- , AudioDecoder (f, c)
+SndfileDecoder::SndfileDecoder (shared_ptr<const SndfileContent> c)
+ : AudioDecoder (c)
, _sndfile_content (c)
, _deinterleave_buffer (0)
{
class SndfileDecoder : public AudioDecoder
{
public:
- SndfileDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const SndfileContent>);
+ SndfileDecoder (boost::shared_ptr<const SndfileContent>);
~SndfileDecoder ();
void seek (ContentTime, bool);
using std::list;
using boost::shared_ptr;
-SubRipDecoder::SubRipDecoder (shared_ptr<const Film> film, shared_ptr<const SubRipContent> content)
- : Decoder (film)
- , SubtitleDecoder (film)
- , SubRip (content)
+SubRipDecoder::SubRipDecoder (shared_ptr<const SubRipContent> content)
+ : SubRip (content)
, _next (0)
{
class SubRipDecoder : public SubtitleDecoder, public SubRip
{
public:
- SubRipDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const SubRipContent>);
-
+ SubRipDecoder (boost::shared_ptr<const SubRipContent>);
+
+protected:
bool pass ();
private:
using boost::shared_ptr;
using boost::optional;
-SubtitleDecoder::SubtitleDecoder (shared_ptr<const Film> f)
- : Decoder (f)
+SubtitleDecoder::SubtitleDecoder ()
{
}
class SubtitleDecoder : public virtual Decoder
{
public:
- SubtitleDecoder (boost::shared_ptr<const Film>);
+ SubtitleDecoder ();
protected:
void image_subtitle (boost::shared_ptr<Image>, dcpomatic::Rect<double>, ContentTime, ContentTime);
using boost::shared_ptr;
using boost::optional;
-VideoDecoder::VideoDecoder (shared_ptr<const Film> f, shared_ptr<const VideoContent> c)
- : Decoder (f)
- , _video_content (c)
+VideoDecoder::VideoDecoder (shared_ptr<const VideoContent> c)
+ : _video_content (c)
{
}
class VideoDecoder : public virtual Decoder
{
public:
- VideoDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const VideoContent>);
+ VideoDecoder (boost::shared_ptr<const VideoContent>);
boost::shared_ptr<const VideoContent> video_content () const {
return _video_content;
/*
- Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 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
#include <boost/lexical_cast.hpp>
#include <wx/spinctrl.h>
#include "lib/ffmpeg_content.h"
+#include "lib/subrip_content.h"
#include "subtitle_panel.h"
#include "film_editor.h"
#include "wx_util.h"
+#include "subtitle_view.h"
using std::vector;
using std::string;
SubtitlePanel::SubtitlePanel (FilmEditor* e)
: FilmEditorPanel (e, _("Subtitles"))
+ , _view (0)
{
wxFlexGridSizer* grid = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
_sizer->Add (grid, 0, wxALL, 8);
add_label_to_sizer (grid, this, _("Subtitle Stream"), true);
_stream = new wxChoice (this, wxID_ANY);
grid->Add (_stream, 1, wxEXPAND);
+
+ _view_button = new wxButton (this, wxID_ANY, _("View..."));
+ grid->Add (_view_button);
_x_offset->SetRange (-100, 100);
_y_offset->SetRange (-100, 100);
_y_offset->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::y_offset_changed, this));
_scale->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::scale_changed, this));
_stream->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&SubtitlePanel::stream_changed, this));
+ _view_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&SubtitlePanel::view_clicked, this));
}
void
_y_offset->Enable (j);
_scale->Enable (j);
_stream->Enable (j);
+
+ SubtitleContentList c = _editor->selected_subtitle_content ();
+ _view_button->Enable (c.size() == 1);
}
void
film_content_changed (SubtitleContentProperty::SUBTITLE_Y_OFFSET);
film_content_changed (SubtitleContentProperty::SUBTITLE_SCALE);
}
+
+void
+SubtitlePanel::view_clicked ()
+{
+ if (_view) {
+ _view->Destroy ();
+ _view = 0;
+ }
+
+ SubtitleContentList c = _editor->selected_subtitle_content ();
+ assert (c.size() == 1);
+ shared_ptr<SubRipContent> sr = dynamic_pointer_cast<SubRipContent> (c.front ());
+ if (sr) {
+ _view = new SubtitleView (this, sr);
+ }
+
+ _view->Show ();
+}
/*
- Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 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
class wxCheckBox;
class wxSpinCtrl;
+class SubtitleView;
class SubtitlePanel : public FilmEditorPanel
{
void y_offset_changed ();
void scale_changed ();
void stream_changed ();
+ void view_clicked ();
void setup_sensitivity ();
wxSpinCtrl* _y_offset;
wxSpinCtrl* _scale;
wxChoice* _stream;
+ wxButton* _view_button;
+ SubtitleView* _view;
};
--- /dev/null
+/*
+ Copyright (C) 2014 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 "lib/subrip_decoder.h"
+#include "lib/decoded.h"
+#include "subtitle_view.h"
+
+using std::list;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+
+SubtitleView::SubtitleView (wxWindow* parent, shared_ptr<SubRipContent> content)
+ : wxDialog (parent, wxID_ANY, _("Subtitles"))
+{
+ _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
+
+ {
+ wxListItem ip;
+ ip.SetId (0);
+ ip.SetText (_("Start"));
+ ip.SetWidth (100);
+ _list->InsertColumn (0, ip);
+ }
+
+ {
+ wxListItem ip;
+ ip.SetId (1);
+ ip.SetText (_("End"));
+ ip.SetWidth (100);
+ _list->InsertColumn (1, ip);
+ }
+
+ {
+ wxListItem ip;
+ ip.SetId (2);
+ ip.SetText (_("Subtitle"));
+ ip.SetWidth (640);
+ _list->InsertColumn (2, ip);
+ }
+
+ wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
+ sizer->Add (_list, 1, wxEXPAND);
+
+ wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
+ if (buttons) {
+ sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+ }
+
+ shared_ptr<SubRipDecoder> decoder (new SubRipDecoder (content));
+ int n = 0;
+ while (1) {
+ shared_ptr<Decoded> dec = decoder->peek ();
+ if (!dec) {
+ break;
+ }
+
+ shared_ptr<DecodedTextSubtitle> sub = dynamic_pointer_cast<DecodedTextSubtitle> (dec);
+ assert (sub);
+
+ for (list<dcp::SubtitleString>::const_iterator i = sub->subs.begin(); i != sub->subs.end(); ++i) {
+ wxListItem list_item;
+ list_item.SetId (n);
+ _list->InsertItem (list_item);
+ _list->SetItem (n, 2, i->text ());
+ ++n;
+ }
+
+ decoder->consume ();
+ }
+
+ SetSizerAndFit (sizer);
+}
+
--- /dev/null
+/*
+ Copyright (C) 2014 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 <wx/wx.h>
+#include <wx/listctrl.h>
+
+class SubRipContent;
+
+class SubtitleView : public wxDialog
+{
+public:
+ SubtitleView (wxWindow *, boost::shared_ptr<SubRipContent>);
+
+private:
+ wxListCtrl* _list;
+};
server_dialog.cc
servers_list_dialog.cc
subtitle_panel.cc
+ subtitle_view.cc
timecode.cc
timeline.cc
timeline_dialog.cc
/* Sound == video so no offset required */
content->_first_video = ContentTime ();
content->_audio_stream->first_audio = ContentTime ();
- FFmpegDecoder decoder (film, content, true, true);
+ FFmpegDecoder decoder (content, film->log(), true, true, true);
BOOST_CHECK_EQUAL (decoder._pts_offset, ContentTime ());
}
/* Common offset should be removed */
content->_first_video = ContentTime::from_seconds (600);
content->_audio_stream->first_audio = ContentTime::from_seconds (600);
- FFmpegDecoder decoder (film, content, true, true);
+ FFmpegDecoder decoder (content, film->log(), true, true, true);
BOOST_CHECK_EQUAL (decoder._pts_offset, ContentTime::from_seconds (-600));
}
/* Video is on a frame boundary */
content->_first_video = ContentTime::from_frames (1, 24);
content->_audio_stream->first_audio = ContentTime ();
- FFmpegDecoder decoder (film, content, true, true);
+ FFmpegDecoder decoder (content, film->log(),true, true, true);
BOOST_CHECK_EQUAL (decoder._pts_offset, ContentTime ());
}
double const frame = 1.0 / 24.0;
content->_first_video = ContentTime::from_seconds (frame + 0.0215);
content->_audio_stream->first_audio = ContentTime ();
- FFmpegDecoder decoder (film, content, true, true);
+ FFmpegDecoder decoder (content, film->log(), true, true, true);
BOOST_CHECK_CLOSE (decoder._pts_offset.seconds(), (frame - 0.0215), 0.00001);
}
double const frame = 1.0 / 24.0;
content->_first_video = ContentTime::from_seconds (frame + 0.0215 + 4.1);
content->_audio_stream->first_audio = ContentTime::from_seconds (4.1);
- FFmpegDecoder decoder (film, content, true, true);
+ FFmpegDecoder decoder (content, film->log(), true, true, true);
BOOST_CHECK_EQUAL (decoder._pts_offset.seconds(), (frame - 0.0215) - 4.1);
}
}
film->examine_and_add_content (content);
wait_for_jobs ();
- FFmpegDecoder decoder (film, content, true, false);
+ FFmpegDecoder decoder (content, film->log(), true, false, false);
shared_ptr<DecodedVideo> a = dynamic_pointer_cast<DecodedVideo> (decoder.peek ());
decoder.seek (ContentTime(), true);
shared_ptr<DecodedVideo> b = dynamic_pointer_cast<DecodedVideo> (decoder.peek ());
shared_ptr<Film> film = new_test_film ("subrip_render_test");
- shared_ptr<SubRipDecoder> decoder (new SubRipDecoder (film, content));
+ shared_ptr<SubRipDecoder> decoder (new SubRipDecoder (content));
shared_ptr<DecodedTextSubtitle> dts = dynamic_pointer_cast<DecodedTextSubtitle> (decoder->peek ());
shared_ptr<Image> image;