* Fix specification of architecture in .debs.
+2012-12-10 Carl Hetherington <cth@carlh.net>
+
+ * Add a check-box (which defaults to on) which tells DVD-o-matic
+ not to scan new content files to work out their length, but instead
+ to trust the length from the header. This length only matters for
+ working out what thumbnails to generate, so it isn't critical.
+ Trusting the header will speed up the "Examine Content" job by
+ a factor of about 2, which is handy for large films.
+
2012-12-10 Carl Hetherington <cth@carlh.net>
* Version 0.59 released.
void
ExamineContentJob::run ()
{
- /* Decode the content to get an accurate length */
+ float progress_remaining = 1;
- /* We don't want to use any existing length here, as progress
- will be messed up.
+ /* Set the film's length to either
+ a) a length judged by running through the content or
+ b) the length from a decoder's header.
*/
- _film->unset_length ();
-
- shared_ptr<Options> o (new Options ("", "", ""));
- o->out_size = Size (512, 512);
- o->apply_crop = false;
- o->decode_audio = false;
-
- descend (0.5);
- pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> > decoders = decoder_factory (_film, o, this);
+ if (!_film->trust_content_header()) {
+ /* Decode the content to get an accurate length */
+
+ /* We don't want to use any existing length here, as progress
+ will be messed up.
+ */
+ _film->unset_length ();
+
+ shared_ptr<Options> o (new Options ("", "", ""));
+ o->out_size = Size (512, 512);
+ o->apply_crop = false;
+ o->decode_audio = false;
+
+ descend (0.5);
+
+ pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> > decoders = decoder_factory (_film, o, this);
+
+ set_progress_unknown ();
+ while (!decoders.first->pass()) {
+ /* keep going */
+ }
+
+ _film->set_length (decoders.first->video_frame());
+
+ _film->log()->log (String::compose ("Video length examined as %1 frames", _film->length().get()));
+
+ ascend ();
+
+ progress_remaining -= 0.5;
+
+ } else {
- set_progress_unknown ();
- while (!decoders.first->pass()) {
- /* keep going */
+ /* Get a quick decoder to get the content's length from its header.
+ It would have been nice to just use the thumbnail transcoder's decoder,
+ but that's a bit fiddly, and this isn't too expensive.
+ */
+
+ shared_ptr<Options> o (new Options ("", "", ""));
+ o->out_size = Size (1024, 1024);
+ pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> > d = decoder_factory (_film, o, 0);
+ _film->set_length (d.first->length());
+
+ _film->log()->log (String::compose ("Video length obtained from header as %1 frames", _film->length().get()));
}
- _film->set_length (decoders.first->video_frame());
-
- _film->log()->log (String::compose ("Video length is %1 frames", _film->length()));
-
- ascend ();
-
/* Now make thumbnails for it */
- descend (0.5);
+ descend (progress_remaining);
try {
- o.reset (new Options (_film->dir ("thumbs"), ".png", ""));
+ shared_ptr<Options> o (new Options (_film->dir ("thumbs"), ".png", ""));
o->out_size = _film->size ();
o->apply_crop = false;
o->decode_audio = false;
- if (_film->length() > 0) {
- o->decode_video_skip = _film->length().get() / 128;
- } else {
- o->decode_video_skip = 0;
- }
+ o->decode_video_skip = _film->length().get() / 128;
o->decode_subtitles = true;
shared_ptr<ImageMagickEncoder> e (new ImageMagickEncoder (_film, o));
Transcoder w (_film, o, this, e);
w.go ();
+
+ /* Now set the film's length from the transcoder's decoder, since we
+ went to all the trouble of going through the content.
+ */
+
+ _film->set_length (w.video_decoder()->video_frame());
} catch (std::exception& e) {
throw DecodeError ("could not find video decoder");
}
- /* I think this prevents problems with green hash on decodes and
- "changing frame properties on the fly is not supported by all filters"
- messages with some content. Although I'm not sure; needs checking.
- */
- AVDictionary* opts = 0;
- av_dict_set (&opts, "threads", "1", 0);
-
- if (avcodec_open2 (_video_codec_context, _video_codec, &opts) < 0) {
+ if (avcodec_open2 (_video_codec_context, _video_codec, 0) < 0) {
throw DecodeError ("could not open video decoder");
}
}
return String::compose ("ffmpeg %1 %2 %3 %4", _id, _sample_rate, _channel_layout, _name);
}
+/** @return Length (in video frames) according to our content's header */
+SourceFrame
+FFmpegDecoder::length () const
+{
+ return (double(_format_context->duration) / AV_TIME_BASE) * frames_per_second();
+}
, _name (n)
, _id (i)
{}
-
+
std::string to_string () const;
std::string name () const {
float frames_per_second () const;
Size native_size () const;
+ SourceFrame length () const;
int time_base_numerator () const;
int time_base_denominator () const;
int sample_aspect_ratio_numerator () const;
Film::Film (string d, bool must_exist)
: _use_dci_name (true)
+ , _trust_content_header (true)
, _dcp_content_type (0)
, _format (0)
, _scaler (Scaler::from_id ("bicubic"))
, _name (o._name)
, _use_dci_name (o._use_dci_name)
, _content (o._content)
+ , _trust_content_header (o._trust_content_header)
, _dcp_content_type (o._dcp_content_type)
, _format (o._format)
, _crop (o._crop)
f << "name " << _name << "\n";
f << "use_dci_name " << _use_dci_name << "\n";
f << "content " << _content << "\n";
+ f << "trust_content_header " << (_trust_content_header ? "1" : "0") << "\n";
if (_dcp_content_type) {
f << "dcp_content_type " << _dcp_content_type->pretty_name () << "\n";
}
_use_dci_name = (v == "1");
} else if (k == "content") {
_content = v;
+ } else if (k == "trust_content_header") {
+ _trust_content_header = (v == "1");
} else if (k == "dcp_content_type") {
_dcp_content_type = DCPContentType::from_pretty_name (v);
} else if (k == "format") {
signal_changed (CONTENT);
set_content_digest (md5_digest (content_path ()));
-
+
examine_content ();
} catch (...) {
}
}
+
+void
+Film::set_trust_content_header (bool t)
+{
+ {
+ boost::mutex::scoped_lock lm (_state_mutex);
+ _trust_content_header = t;
+ }
+
+ signal_changed (TRUST_CONTENT_HEADER);
+
+ if (!_trust_content_header) && !content().empty()) {
+ /* We just said that we don't trust the content's header */
+ examine_content ();
+ }
+}
void
Film::set_dcp_content_type (DCPContentType const * t)
NAME,
USE_DCI_NAME,
CONTENT,
+ TRUST_CONTENT_HEADER,
DCP_CONTENT_TYPE,
FORMAT,
CROP,
return _content;
}
+ bool trust_content_header () const {
+ boost::mutex::scoped_lock lm (_state_mutex);
+ return _trust_content_header;
+ }
+
DCPContentType const * dcp_content_type () const {
boost::mutex::scoped_lock lm (_state_mutex);
return _dcp_content_type;
void set_name (std::string);
void set_use_dci_name (bool);
void set_content (std::string);
+ void set_trust_content_header (bool);
void set_dcp_content_type (DCPContentType const *);
void set_format (Format const *);
void set_crop (Crop);
* or an absolute path.
*/
std::string _content;
+ bool _trust_content_header;
/** 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.) */
Size native_size () const;
+ SourceFrame length () const {
+ /* We don't know */
+ return 0;
+ }
+
int audio_channels () const {
return 0;
}
/* Set up the decoder to use the film's set streams */
_decoders.first->set_subtitle_stream (f->subtitle_stream ());
- _decoders.second->set_audio_stream (f->audio_stream ());
+ if (_decoders.second) {
+ _decoders.second->set_audio_stream (f->audio_stream ());
+ }
if (_matcher) {
_decoders.first->connect_video (_matcher);
_decoders.first->connect_video (_encoder);
}
- if (_matcher && _delay_line) {
+ if (_matcher && _delay_line && _decoders.second) {
_decoders.second->connect_audio (_delay_line);
_delay_line->connect_audio (_matcher);
_matcher->connect_audio (_gain);
_decoders.first->set_progress ();
}
- if (!done[1] && dynamic_pointer_cast<Decoder> (_decoders.second) != dynamic_pointer_cast<Decoder> (_decoders.first)) {
+ if (!done[1] && _decoders.second && dynamic_pointer_cast<Decoder> (_decoders.second) != dynamic_pointer_cast<Decoder> (_decoders.first)) {
done[1] = _decoders.second->pass ();
} else {
done[1] = true;
void go ();
+ boost::shared_ptr<VideoDecoder> video_decoder () const {
+ return _decoders.first;
+ }
+
protected:
/** A Job that is running this Transcoder, or 0 */
Job* _job;
virtual float frames_per_second () const = 0;
/** @return native size in pixels */
virtual Size native_size () const = 0;
+ /** @return length (in source video frames), according to our content's header */
+ virtual SourceFrame length () const = 0;
virtual int time_base_numerator () const = 0;
virtual int time_base_denominator () const = 0;
_content = new wxFilePickerCtrl (_film_panel, wxID_ANY, wxT (""), wxT ("Select Content File"), wxT("*.*"));
_film_sizer->Add (_content, 1, wxEXPAND);
+ _trust_content_header = new wxCheckBox (_film_panel, wxID_ANY, wxT ("Trust content's header"));
+ video_control (_trust_content_header);
+ _film_sizer->Add (_trust_content_header, 1);
+ _film_sizer->AddSpacer (0);
+
add_label_to_sizer (_film_sizer, _film_panel, "Content Type");
_dcp_content_type = new wxComboBox (_film_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
_film_sizer->Add (_dcp_content_type);
_edit_dci_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_dci_button_clicked), 0, this);
_format->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this);
_content->Connect (wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::content_changed), 0, this);
+ _trust_content_header->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::trust_content_header_changed), 0, this);
_left_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::left_crop_changed), 0, this);
_right_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::right_crop_changed), 0, this);
_top_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::top_crop_changed), 0, this);
}
}
+void
+FilmEditor::trust_content_header_changed (wxCommandEvent &)
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_trust_content_header (_trust_content_header->GetValue ());
+}
+
/** Called when the DCP A/B switch has been toggled */
void
FilmEditor::dcp_ab_toggled (wxCommandEvent &)
setup_subtitle_control_sensitivity ();
setup_streams ();
break;
+ case Film::TRUST_CONTENT_HEADER:
+ checked_set (_trust_content_header, _film->trust_content_header ());
+ break;
case Film::SUBTITLE_STREAMS:
setup_subtitle_control_sensitivity ();
setup_streams ();
film_changed (Film::NAME);
film_changed (Film::USE_DCI_NAME);
film_changed (Film::CONTENT);
+ film_changed (Film::TRUST_CONTENT_HEADER);
film_changed (Film::DCP_CONTENT_TYPE);
film_changed (Film::FORMAT);
film_changed (Film::CROP);
_edit_dci_button->Enable (s);
_format->Enable (s);
_content->Enable (s);
+ _trust_content_header->Enable (s);
_left_crop->Enable (s);
_right_crop->Enable (s);
_top_crop->Enable (s);
void top_crop_changed (wxCommandEvent &);
void bottom_crop_changed (wxCommandEvent &);
void content_changed (wxCommandEvent &);
+ void trust_content_header_changed (wxCommandEvent &);
void format_changed (wxCommandEvent &);
void dcp_trim_start_changed (wxCommandEvent &);
void dcp_trim_end_changed (wxCommandEvent &);
wxComboBox* _format;
/** The Film's content file */
wxFilePickerCtrl* _content;
+ wxCheckBox* _trust_content_header;
/** The Film's left crop */
wxSpinCtrl* _left_crop;
/** The Film's right crop */