+2014-03-07 Carl Hetherington <cth@carlh.net>
+
+ * Add subtitle view.
+
+ 2014-06-03 Carl Hetherington <cth@carlh.net>
+
+ * Fix bad resampling of separate sound file sources that
+ have specified video frame rates.
+
+ * Version 1.69.20 released.
+
+ 2014-06-03 Carl Hetherington <cth@carlh.net>
+
+ * Re-calculate and update audio plots when the mapping is changed.
+
+ * Change the -3dB preset to -6dB since we are talking about
+ amplitude, not power.
+
+ * Version 1.69.19 released.
+
+ 2014-06-02 Carl Hetherington <cth@carlh.net>
+
+ * Empirical hack to prevent over-read of array
+ by libswscale; may fix crashes at the start of
+ DCP encodes.
+
2014-05-29 Carl Hetherington <cth@carlh.net>
* Version 1.69.18 released.
#include <libxml++/libxml++.h>
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "audio_mapping.h"
#include "util.h"
+ #include "md5_digester.h"
using std::list;
using std::cout;
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>
-#include <libdcp/rec709_linearised_gamma_lut.h>
-#include <libdcp/srgb_linearised_gamma_lut.h>
-#include <libdcp/gamma_lut.h>
-#include <libdcp/xyz_frame.h>
-#include <libdcp/rgb_xyz.h>
-#include <libdcp/colour_matrix.h>
-#include <libdcp/raw_convert.h>
+#include <boost/lexical_cast.hpp>
- #include <openssl/md5.h>
+#include <dcp/gamma_lut.h>
+#include <dcp/xyz_frame.h>
+#include <dcp/rgb_xyz.h>
+#include <dcp/colour_matrix.h>
+#include <dcp/raw_convert.h>
#include <libcxml/cxml.h>
#include "film.h"
#include "dcp_video_frame.h"
#include "image.h"
#include "exceptions.h"
#include "scaler.h"
+#include "timer.h"
+#include "rect.h"
+ #include "md5_digester.h"
#include "i18n.h"
string
Image::digest () const
{
- MD5_CTX md5_context;
- MD5_Init (&md5_context);
+ MD5Digester digester;
for (int i = 0; i < components(); ++i) {
- MD5_Update (&md5_context, data()[i], line_size()[i]);
- }
-
- unsigned char digest[MD5_DIGEST_LENGTH];
- MD5_Final (digest, &md5_context);
-
- stringstream s;
- for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
- s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]);
+ digester.add (data()[i], line_size()[i]);
}
- return s.str ();
+ return digester.get ();
}
-
}
}
+ /** @param already_resampled true if this data has already been through the chain up to the resampler */
void
-Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers> audio, AudioContent::Frame frame, bool already_resampled)
+Player::playlist_changed ()
{
- shared_ptr<Piece> piece = weak_piece.lock ();
- if (!piece) {
- return;
- }
+ _have_valid_pieces = false;
+ Changed (false);
+}
- shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
- assert (content);
+void
+Player::set_video_container_size (dcp::Size s)
+{
+ _video_container_size = s;
- if (!already_resampled) {
- /* Gain */
- if (content->audio_gain() != 0) {
- shared_ptr<AudioBuffers> gain (new AudioBuffers (audio));
- gain->apply_gain (content->audio_gain ());
- audio = gain;
- }
-
- /* Resample */
- if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
- shared_ptr<Resampler> r = resampler (content, true);
- pair<shared_ptr<const AudioBuffers>, AudioContent::Frame> ro = r->run (audio, frame);
- audio = ro.first;
- frame = ro.second;
- }
- }
-
- Time const relative_time = _film->audio_frames_to_time (frame);
+ _black_image.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
+ _black_image->make_black ();
+}
- if (content->trimmed (relative_time)) {
- return;
+void
+Player::film_changed (Film::Property p)
+{
+ /* Here we should notice Film properties that affect our output, and
+ alert listeners that our output now would be different to how it was
+ last time we were run.
+ */
+
+ if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
+ Changed (false);
}
+}
- Time time = content->position() + (content->audio_delay() * TIME_HZ / 1000) + relative_time - content->trim_start ();
+list<PositionImage>
+Player::process_content_image_subtitles (shared_ptr<SubtitleContent> content, list<shared_ptr<ContentImageSubtitle> > subs) const
+{
+ list<PositionImage> all;
- /* Remap channels */
- shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames()));
- dcp_mapped->make_silent ();
-
- AudioMapping map = content->audio_mapping ();
- for (int i = 0; i < map.content_channels(); ++i) {
- for (int j = 0; j < _film->audio_channels(); ++j) {
- if (map.get (i, static_cast<libdcp::Channel> (j)) > 0) {
- dcp_mapped->accumulate_channel (
- audio.get(),
- i,
- static_cast<libdcp::Channel> (j),
- map.get (i, static_cast<libdcp::Channel> (j))
- );
- }
+ for (list<shared_ptr<ContentImageSubtitle> >::const_iterator i = subs.begin(); i != subs.end(); ++i) {
+ if (!(*i)->image) {
+ continue;
}
+
+ dcpomatic::Rect<double> in_rect = (*i)->rectangle;
+ dcp::Size scaled_size;
+
+ in_rect.x += content->subtitle_x_offset ();
+ in_rect.y += content->subtitle_y_offset ();
+
+ /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */
+ scaled_size.width = in_rect.width * _video_container_size.width * content->subtitle_scale ();
+ scaled_size.height = in_rect.height * _video_container_size.height * content->subtitle_scale ();
+
+ /* Then we need a corrective translation, consisting of two parts:
+ *
+ * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
+ * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
+ *
+ * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
+ * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and
+ * (height_before_subtitle_scale * (1 - subtitle_scale) / 2).
+ *
+ * Combining these two translations gives these expressions.
+ */
+
+ all.push_back (
+ PositionImage (
+ (*i)->image->scale (
+ scaled_size,
+ Scaler::from_id ("bicubic"),
+ (*i)->image->pixel_format (),
+ true
+ ),
+ Position<int> (
+ rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - content->subtitle_scale ()) / 2))),
+ rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - content->subtitle_scale ()) / 2)))
+ )
+ )
+ );
}
- audio = dcp_mapped;
+ return all;
+}
- /* We must cut off anything that comes before the start of all time */
- if (time < 0) {
- int const frames = - time * _film->audio_frame_rate() / TIME_HZ;
- if (frames >= audio->frames ()) {
- return;
+list<PositionImage>
+Player::process_content_text_subtitles (list<shared_ptr<ContentTextSubtitle> > sub) const
+{
+ list<PositionImage> all;
+ for (list<shared_ptr<ContentTextSubtitle> >::const_iterator i = sub.begin(); i != sub.end(); ++i) {
+ if (!(*i)->subs.empty ()) {
+ all.push_back (render_subtitles ((*i)->subs, _video_container_size));
}
-
- shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->channels(), audio->frames() - frames));
- trimmed->copy_from (audio.get(), audio->frames() - frames, frames, 0);
-
- audio = trimmed;
- time = 0;
}
- _audio_merger.push (audio, time);
- piece->audio_position += _film->audio_frames_to_time (audio->frames ());
+ return all;
}
void
#endif
#include <glib.h>
#include <openjpeg.h>
- #include <openssl/md5.h>
+#include <pangomm/init.h>
#include <magick/MagickCore.h>
#include <magick/version.h>
-#include <libdcp/version.h>
-#include <libdcp/util.h>
-#include <libdcp/signer_chain.h>
-#include <libdcp/signer.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/version.h>
+#include <dcp/util.h>
+#include <dcp/signer_chain.h>
+#include <dcp/signer.h>
+#include <dcp/raw_convert.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include "job.h"
#include "cross.h"
#include "video_content.h"
+#include "rect.h"
+ #include "md5_digester.h"
#ifdef DCPOMATIC_WINDOWS
#include "stack.hpp"
#endif
fclose (f);
}
- unsigned char digest[MD5_DIGEST_LENGTH];
- MD5_Final (digest, &md5_context);
-
- stringstream s;
- for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
- s << std::hex << std::setfill('0') << std::setw(2) << ((int) digest[i]);
- }
-
- return s.str ();
+ return digester.get ();
}
-static bool
-about_equal (float a, float b)
-{
- /* A film of F seconds at f FPS will be Ff frames;
- Consider some delta FPS d, so if we run the same
- film at (f + d) FPS it will last F(f + d) seconds.
-
- Hence the difference in length over the length of the film will
- be F(f + d) - Ff frames
- = Ff + Fd - Ff frames
- = Fd frames
- = Fd/f seconds
-
- So if we accept a difference of 1 frame, ie 1/f seconds, we can
- say that
-
- 1/f = Fd/f
- ie 1 = Fd
- ie d = 1/F
-
- So for a 3hr film, ie F = 3 * 60 * 60 = 10800, the acceptable
- FPS error is 1/F ~= 0.0001 ~= 10-e4
- */
-
- return (fabs (a - b) < 1e-4);
-}
-
/** @param An arbitrary audio frame rate.
* @return The appropriate DCP-approved frame rate (48kHz or 96kHz).
*/
#include "config.h"
#include "job.h"
#include "cross.h"
+#include "audio_buffers.h"
+ #include "md5_digester.h"
#include "i18n.h"
kdm.cc
json_server.cc
log.cc
- piece.cc
+ md5_digester.cc
player.cc
player_video_frame.cc
playlist.cc
}
void
- AudioMappingView::minus3dB ()
+ AudioMappingView::minus6dB ()
{
- _map.set (_menu_row, static_cast<dcp::Channel> (_menu_column - 1), 1 / sqrt (2));
- _map.set (_menu_row, static_cast<libdcp::Channel> (_menu_column - 1), pow (10, -6.0 / 20));
++ _map.set (_menu_row, static_cast<dcp::Channel> (_menu_column - 1), pow (10, -6.0 / 20));
map_changed ();
}