#include "audio_sink.h"
using boost::shared_ptr;
+using boost::weak_ptr;
using boost::bind;
+static void
+process_audio_proxy (weak_ptr<AudioSink> sink, shared_ptr<AudioBuffers> audio)
+{
+ shared_ptr<AudioSink> p = sink.lock ();
+ if (p) {
+ p->process_audio (audio);
+ }
+}
+
void
AudioSource::connect_audio (shared_ptr<AudioSink> s)
{
- Audio.connect (bind (&AudioSink::process_audio, s, _1));
+ Audio.connect (bind (process_audio_proxy, weak_ptr<AudioSink> (s), _1));
}
* or 0 if not known.
*/
float
-Encoder::current_frames_per_second () const
+Encoder::current_encoding_rate () const
{
boost::mutex::scoped_lock lock (_history_mutex);
if (int (_time_history.size()) < _history_size) {
/** Called when a processing run has finished */
virtual void process_end ();
- float current_frames_per_second () const;
+ float current_encoding_rate () const;
int video_frames_out () const;
private:
void
FFmpegContent::examine (shared_ptr<Film> film, shared_ptr<Job> job, bool quick)
{
- job->descend (0.5);
- Content::examine (film, job, quick);
- job->ascend ();
-
job->set_progress_unknown ();
+ Content::examine (film, job, quick);
+
shared_ptr<FFmpegDecoder> decoder (new FFmpegDecoder (film, shared_from_this (), true, false, false, true));
ContentVideoFrame video_length = 0;
string
FFmpegContent::information () const
{
+ if (video_length() == 0 || video_frame_rate() == 0) {
+ return "";
+ }
+
stringstream s;
s << String::compose (_("%1 frames; %2 frames per second"), video_length(), video_frame_rate()) << "\n";
using boost::dynamic_pointer_cast;
using libdcp::Size;
+boost::mutex FFmpegDecoder::_mutex;
+
FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegContent> c, bool video, bool audio, bool subtitles, bool video_sync)
: Decoder (f)
, VideoDecoder (f)
FFmpegDecoder::~FFmpegDecoder ()
{
+ boost::mutex::scoped_lock lm (_mutex);
+
if (_audio_codec_context) {
avcodec_close (_audio_codec_context);
}
-
+
if (_video_codec_context) {
avcodec_close (_video_codec_context);
}
void
FFmpegDecoder::setup_video ()
{
+ boost::mutex::scoped_lock lm (_mutex);
+
_video_codec_context = _format_context->streams[_video_stream]->codec;
_video_codec = avcodec_find_decoder (_video_codec_context->codec_id);
void
FFmpegDecoder::setup_audio ()
{
+ boost::mutex::scoped_lock lm (_mutex);
+
if (!_ffmpeg_content->audio_stream ()) {
return;
}
void
FFmpegDecoder::setup_subtitle ()
{
+ boost::mutex::scoped_lock lm (_mutex);
+
if (!_ffmpeg_content->subtitle_stream() || _ffmpeg_content->subtitle_stream()->id >= int (_format_context->nb_streams)) {
return;
}
private:
+ /* No copy construction */
+ FFmpegDecoder (FFmpegDecoder const &);
+ FFmpegDecoder& operator= (FFmpegDecoder const &);
+
bool do_seek (double p, bool);
PixelFormat pixel_format () const;
AVSampleFormat audio_sample_format () const;
bool _decode_audio;
bool _decode_subtitles;
bool _video_sync;
+
+ /* It would appear (though not completely verified) that one must have
+ a mutex around calls to avcodec_open* and avcodec_close... and here
+ it is.
+ */
+ static boost::mutex _mutex;
};
int
Film::target_audio_sample_rate () const
{
- if (has_audio ()) {
+ if (!has_audio ()) {
return 0;
}
#include "ffmpeg_decoder.h"
#include "imagemagick_content.h"
#include "imagemagick_decoder.h"
+#include "job.h"
using std::list;
using std::cout;
{
}
-
+
void
Player::disable_video ()
{
void
Player::set_progress (shared_ptr<Job> job)
{
- /* XXX */
+ /* Assume progress can be divined from how far through the video we are */
+ switch (_playlist->video_from ()) {
+ case Playlist::VIDEO_NONE:
+ break;
+ case Playlist::VIDEO_FFMPEG:
+ if (_playlist->video_length ()) {
+ job->set_progress (float(_ffmpeg_decoder->video_frame()) / _playlist->video_length ());
+ }
+ break;
+ case Playlist::VIDEO_IMAGEMAGICK:
+ {
+ int n = 0;
+ for (std::list<boost::shared_ptr<ImageMagickDecoder> >::iterator i = _imagemagick_decoders.begin(); i != _imagemagick_decoders.end(); ++i) {
+ if (_imagemagick_decoder == i) {
+ job->set_progress (float (n) / _imagemagick_decoders.size ());
+ }
+ ++n;
+ }
+ break;
+ }
+ }
}
void
_film->log()->log (N_("Transcode job starting"));
_film->log()->log (String::compose (N_("Audio delay is %1ms"), _film->audio_delay()));
- Transcoder w (_film, shared_from_this ());
- w.go ();
+ _transcoder.reset (new Transcoder (_film, shared_from_this ()));
+ _transcoder->go ();
set_progress (1);
set_state (FINISHED_OK);
string
TranscodeJob::status () const
{
-// if (!_encoder) {
-// return _("0%");
-// }
+ if (!_transcoder) {
+ return _("0%");
+ }
- /* XXX */
-// float const fps = _encoder->current_frames_per_second ();
- float const fps = 0;
+ float const fps = _transcoder->current_encoding_rate ();
if (fps == 0) {
return Job::status ();
}
int
TranscodeJob::remaining_time () const
{
- return 0;
-#if 0
- XXX
- float fps = _encoder->current_frames_per_second ();
+ if (!_transcoder) {
+ return 0;
+ }
+
+ float fps = _transcoder->current_encoding_rate ();
+
if (fps == 0) {
return 0;
}
- if (!_video->length()) {
+ if (!_film->video_length()) {
return 0;
}
/* Compute approximate proposed length here, as it's only here that we need it */
- int length = _film->length().get();
- FrameRateConversion const frc (_film->source_frame_rate(), _film->dcp_frame_rate());
+ int length = _film->video_length();
+ FrameRateConversion const frc (_film->video_frame_rate(), _film->dcp_frame_rate());
if (frc.skip) {
length /= 2;
}
/* If we are repeating it shouldn't affect transcode time, so don't take it into account */
- /* We assume that dcp_length() is valid, if it is set */
- int const left = length - _encoder->video_frames_out();
+ int const left = length - _transcoder->video_frames_out();
return left / fps;
-#endif
}
#include <boost/shared_ptr.hpp>
#include "job.h"
-class Encoder;
+class Transcoder;
/** @class TranscodeJob
* @brief A job which transcodes from one format to another.
void run ();
std::string status () const;
-protected:
+private:
int remaining_time () const;
+
+ boost::shared_ptr<Transcoder> _transcoder;
};
}
_encoder->process_end ();
}
+
+float
+Transcoder::current_encoding_rate () const
+{
+ return _encoder->current_encoding_rate ();
+}
+
+int
+Transcoder::video_frames_out () const
+{
+ return _encoder->video_frames_out ();
+}
void go ();
+ float current_encoding_rate () const;
+ int video_frames_out () const;
+
protected:
/** A Job that is running this Transcoder, or 0 */
boost::shared_ptr<Job> _job;
#include "video_sink.h"
using boost::shared_ptr;
+using boost::weak_ptr;
using boost::bind;
+static void
+process_video_proxy (weak_ptr<VideoSink> sink, shared_ptr<Image> i, bool same, shared_ptr<Subtitle> s)
+{
+ boost::shared_ptr<VideoSink> p = sink.lock ();
+ if (p) {
+ p->process_video (i, same, s);
+ }
+}
+
void
VideoSource::connect_video (shared_ptr<VideoSink> s)
{
- Video.connect (bind (&VideoSink::process_video, s, _1, _2, _3));
+ /* If we bind, say, a Playlist (as the VideoSink) to a Decoder (which is owned
+ by the Playlist) we create a cycle. Use a weak_ptr to break it.
+ */
+ Video.connect (bind (process_video_proxy, boost::weak_ptr<VideoSink> (s), _1, _2, _3));
}
setup_show_audio_sensitivity ();
} else if (p == VideoContentProperty::VIDEO_LENGTH) {
setup_length ();
+ setup_content_information ();
} else if (p == FFmpegContentProperty::AUDIO_STREAM) {
if (_film->ffmpeg_audio_stream()) {
- checked_set (_ffmpeg_audio_stream, _film->ffmpeg_audio_stream()->id);
+ checked_set (_ffmpeg_audio_stream, boost::lexical_cast<string> (_film->ffmpeg_audio_stream()->id));
}
setup_dcp_name ();
setup_audio_details ();
setup_show_audio_sensitivity ();
} else if (p == FFmpegContentProperty::SUBTITLE_STREAM) {
if (_film->ffmpeg_subtitle_stream()) {
- checked_set (_ffmpeg_subtitle_stream, _film->ffmpeg_subtitle_stream()->id);
+ checked_set (_ffmpeg_subtitle_stream, boost::lexical_cast<string> (_film->ffmpeg_subtitle_stream()->id));
}
}
}
} else {
s << _film->audio_channels() << " " << wx_to_std (_("channels"));
}
- s << ", " << _film->audio_channels() << wx_to_std (_("Hz"));
+ s << ", " << _film->audio_frame_rate() << wx_to_std (_("Hz"));
_audio->SetLabel (std_to_wx (s.str ()));
}
}
void
FilmEditor::setup_content ()
{
+ string selected_summary;
+ int const s = _content->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ if (s != -1) {
+ selected_summary = wx_to_std (_content->GetItemText (s));
+ }
+
_content->DeleteAllItems ();
ContentList content = _film->content ();
for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
- _content->InsertItem (_content->GetItemCount(), std_to_wx ((*i)->summary ()));
+ int const t = _content->GetItemCount ();
+ _content->InsertItem (t, std_to_wx ((*i)->summary ()));
+ if ((*i)->summary() == selected_summary) {
+ _content->SetItemState (t, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+ }
+ }
+
+ if (selected_summary.empty () && !content.empty ()) {
+ /* Select the first item of content if non was selected before */
+ _content->SetItemState (0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}
}
FilmEditor::content_item_selected (wxListEvent &)
{
setup_content_button_sensitivity ();
+ setup_content_information ();
+}
+void
+FilmEditor::setup_content_information ()
+{
int const s = _content->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (s == -1) {
_content_information->SetValue ("");
void setup_content_button_sensitivity ();
void setup_length ();
void setup_format ();
+ void setup_content_information ();
void active_jobs_changed (bool);