summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2013-07-09 20:35:39 +0100
committerCarl Hetherington <cth@carlh.net>2013-07-09 20:35:39 +0100
commit89115db77729a2c99f1a09ff6a461720e16f889e (patch)
treee528ec014b820d4e0efa28893dcee91cd76ee618 /src/lib
parentf8ad440cf187c517b7800f3efdfc0954025c4422 (diff)
parentd2ff6a6b0256e256b6df416f280c846072f7682f (diff)
Merge master.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/combiner.cc74
-rw-r--r--src/lib/config.cc3
-rw-r--r--src/lib/cross.cc110
-rw-r--r--src/lib/cross.h6
-rw-r--r--src/lib/encoder.cc1
-rw-r--r--src/lib/ffmpeg_decoder.cc17
-rw-r--r--src/lib/film.cc20
-rw-r--r--src/lib/film.h8
-rw-r--r--src/lib/ratio.h5
-rw-r--r--src/lib/trimmer.cc123
-rw-r--r--src/lib/util.h1
-rw-r--r--src/lib/version.h1
-rw-r--r--src/lib/writer.cc4
-rw-r--r--src/lib/wscript2
14 files changed, 355 insertions, 20 deletions
diff --git a/src/lib/combiner.cc b/src/lib/combiner.cc
new file mode 100644
index 000000000..f7d634832
--- /dev/null
+++ b/src/lib/combiner.cc
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2012 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 "combiner.h"
+#include "image.h"
+
+using boost::shared_ptr;
+
+Combiner::Combiner (shared_ptr<Log> log)
+ : TimedVideoProcessor (log)
+{
+
+}
+
+/** Process video for the left half of the frame.
+ * Subtitle parameter will be ignored.
+ * @param image Frame image.
+ */
+void
+Combiner::process_video (shared_ptr<const Image> image, bool, shared_ptr<Subtitle>, double)
+{
+ _image.reset (new SimpleImage (image));
+}
+
+/** Process video for the right half of the frame.
+ * @param image Frame image.
+ * @param sub Subtitle (which will be put onto the whole frame)
+ */
+void
+Combiner::process_video_b (shared_ptr<const Image> image, bool, shared_ptr<Subtitle> sub, double t)
+{
+ if (!_image) {
+ /* It's possible for filters in the A-side to mean that we get a B frame
+ before any A; just skip the B frame in that case. This at least prevents
+ a crash, but may not be right.
+ */
+ return;
+ }
+
+ /* Copy the right half of this image into our _image */
+ /* XXX: this should probably be in the Image class */
+ for (int i = 0; i < image->components(); ++i) {
+ int const line_size = image->line_size()[i];
+ int const half_line_size = line_size / 2;
+
+ uint8_t* p = _image->data()[i];
+ uint8_t* q = image->data()[i];
+
+ for (int j = 0; j < image->lines (i); ++j) {
+ memcpy (p + half_line_size, q + half_line_size, half_line_size);
+ p += _image->stride()[i];
+ q += image->stride()[i];
+ }
+ }
+
+ Video (_image, false, sub, t);
+ _image.reset ();
+}
diff --git a/src/lib/config.cc b/src/lib/config.cc
index c9ec730f2..e0fbcc703 100644
--- a/src/lib/config.cc
+++ b/src/lib/config.cc
@@ -38,6 +38,7 @@ using std::ifstream;
using std::string;
using std::ofstream;
using std::list;
+using std::max;
using boost::shared_ptr;
using boost::lexical_cast;
using boost::optional;
@@ -46,7 +47,7 @@ Config* Config::_instance = 0;
/** Construct default configuration */
Config::Config ()
- : _num_local_encoding_threads (2)
+ : _num_local_encoding_threads (max (2U, boost::thread::hardware_concurrency()))
, _server_port (6192)
, _tms_path (N_("."))
, _sound_processor (SoundProcessor::from_id (N_("dolby_cp750")))
diff --git a/src/lib/cross.cc b/src/lib/cross.cc
index ffd44eb02..ee0ef89b2 100644
--- a/src/lib/cross.cc
+++ b/src/lib/cross.cc
@@ -20,19 +20,27 @@
#include <fstream>
#include <boost/algorithm/string.hpp>
#include "cross.h"
-#ifdef DCPOMATIC_POSIX
+#include "compose.hpp"
+#include "log.h"
+#ifdef DCPOMATIC_LINUX
#include <unistd.h>
+#include <mntent.h>
#endif
#ifdef DCPOMATIC_WINDOWS
-#include "windows.h"
+#include <windows.h>
+#undef DATADIR
+#include <shlwapi.h>
#endif
#ifdef DCPOMATIC_OSX
#include <sys/sysctl.h>
#endif
using std::pair;
+using std::list;
using std::ifstream;
using std::string;
+using std::make_pair;
+using boost::shared_ptr;
void
dcpomatic_sleep (int s)
@@ -81,3 +89,101 @@ cpu_info ()
return info;
}
+void
+run_ffprobe (boost::filesystem::path content, boost::filesystem::path out, shared_ptr<Log> log)
+{
+#ifdef DCPOMATIC_WINDOWS
+ SECURITY_ATTRIBUTES security;
+ security.nLength = sizeof (security);
+ security.bInheritHandle = TRUE;
+ security.lpSecurityDescriptor = 0;
+
+ HANDLE child_stderr_read;
+ HANDLE child_stderr_write;
+ if (!CreatePipe (&child_stderr_read, &child_stderr_write, &security, 0)) {
+ log->log ("ffprobe call failed (could not CreatePipe)");
+ return;
+ }
+
+ wchar_t dir[512];
+ GetModuleFileName (GetModuleHandle (0), dir, sizeof (dir));
+ PathRemoveFileSpec (dir);
+ SetCurrentDirectory (dir);
+
+ STARTUPINFO startup_info;
+ ZeroMemory (&startup_info, sizeof (startup_info));
+ startup_info.cb = sizeof (startup_info);
+ startup_info.hStdError = child_stderr_write;
+ startup_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ wchar_t command[512];
+ wcscpy (command, L"ffprobe.exe \"");
+
+ wchar_t file[512];
+ MultiByteToWideChar (CP_UTF8, 0, content.string().c_str(), -1, file, sizeof(file));
+ wcscat (command, file);
+
+ wcscat (command, L"\"");
+
+ PROCESS_INFORMATION process_info;
+ ZeroMemory (&process_info, sizeof (process_info));
+ if (!CreateProcess (0, command, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &startup_info, &process_info)) {
+ log->log ("ffprobe call failed (could not CreateProcess)");
+ return;
+ }
+
+ FILE* o = fopen (out.string().c_str(), "w");
+ if (!o) {
+ log->log ("ffprobe call failed (could not create output file)");
+ return;
+ }
+
+ CloseHandle (child_stderr_write);
+
+ while (1) {
+ char buffer[512];
+ DWORD read;
+ if (!ReadFile(child_stderr_read, buffer, sizeof(buffer), &read, 0) || read == 0) {
+ break;
+ }
+ fwrite (buffer, read, 1, o);
+ }
+
+ fclose (o);
+
+ WaitForSingleObject (process_info.hProcess, INFINITE);
+ CloseHandle (process_info.hProcess);
+ CloseHandle (process_info.hThread);
+ CloseHandle (child_stderr_read);
+#else
+ string ffprobe = "ffprobe \"" + content.string() + "\" 2> \"" + out.string() + "\"";
+ log->log (String::compose ("Probing with %1", ffprobe));
+ system (ffprobe.c_str ());
+#endif
+}
+
+list<pair<string, string> >
+mount_info ()
+{
+ list<pair<string, string> > m;
+
+#ifdef DCPOMATIC_LINUX
+ FILE* f = setmntent ("/etc/mtab", "r");
+ if (!f) {
+ return m;
+ }
+
+ while (1) {
+ struct mntent* mnt = getmntent (f);
+ if (!mnt) {
+ break;
+ }
+
+ m.push_back (make_pair (mnt->mnt_dir, mnt->mnt_type));
+ }
+
+ endmntent (f);
+#endif
+
+ return m;
+}
diff --git a/src/lib/cross.h b/src/lib/cross.h
index d185286b1..a00fee679 100644
--- a/src/lib/cross.h
+++ b/src/lib/cross.h
@@ -17,9 +17,15 @@
*/
+#include <boost/filesystem.hpp>
+
#ifdef DCPOMATIC_WINDOWS
#define WEXITSTATUS(w) (w)
#endif
+class Log;
+
void dcpomatic_sleep (int);
extern std::pair<std::string, int> cpu_info ();
+extern void run_ffprobe (boost::filesystem::path, boost::filesystem::path, boost::shared_ptr<Log>);
+extern std::list<std::pair<std::string, std::string> > mount_info ();
diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc
index c3865d2c1..d3181acd9 100644
--- a/src/lib/encoder.cc
+++ b/src/lib/encoder.cc
@@ -40,6 +40,7 @@ using std::stringstream;
using std::vector;
using std::list;
using std::cout;
+using std::min;
using std::make_pair;
using boost::shared_ptr;
using boost::optional;
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index a3fdaf9b1..bf0949130 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -78,9 +78,6 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC
double
FFmpegDecoder::compute_pts_offset (double first_video, double first_audio, float video_frame_rate)
{
- assert (first_video >= 0);
- assert (first_audio >= 0);
-
double const old_first_video = first_video;
/* Round the first video to a frame boundary */
@@ -94,6 +91,8 @@ FFmpegDecoder::compute_pts_offset (double first_video, double first_audio, float
FFmpegDecoder::~FFmpegDecoder ()
{
+ boost::mutex::scoped_lock lm (_mutex);
+
if (_subtitle_codec_context) {
avcodec_close (_subtitle_codec_context);
}
@@ -370,17 +369,9 @@ FFmpegDecoder::decode_audio_packet ()
}
}
-
- int const data_size = av_samples_get_buffer_size (
- 0, audio_codec_context()->channels, _frame->nb_samples, audio_sample_format (), 1
- );
-
- assert (audio_codec_context()->channels == _ffmpeg_content->audio_channels());
- audio (deinterleave_audio (_frame->data, data_size), _audio_position);
+ copy_packet.data += decode_result;
+ copy_packet.size -= decode_result;
}
-
- copy_packet.data += decode_result;
- copy_packet.size -= decode_result;
}
}
}
diff --git a/src/lib/film.cc b/src/lib/film.cc
index ad565aca0..fa75ab1f1 100644
--- a/src/lib/film.cc
+++ b/src/lib/film.cc
@@ -101,6 +101,7 @@ Film::Film (string d)
, _dci_metadata (Config::instance()->default_dci_metadata ())
, _dcp_video_frame_rate (24)
, _dcp_audio_channels (MAX_AUDIO_CHANNELS)
+ , _minimum_audio_channels (0)
, _dirty (false)
{
set_dci_date_today ();
@@ -127,7 +128,6 @@ Film::Film (string d)
}
set_directory (result.string ());
- _log.reset (new FileLog (file ("log")));
}
Film::Film (Film const & o)
@@ -149,6 +149,7 @@ Film::Film (Film const & o)
, _dci_metadata (o._dci_metadata)
, _dcp_video_frame_rate (o._dcp_video_frame_rate)
, _dci_date (o._dci_date)
+ , _minimum_audio_channels (o._minimum_audio_channels)
, _dirty (o._dirty)
{
_playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2));
@@ -184,7 +185,6 @@ Film::info_dir () const
string
Film::internal_video_mxf_dir () const
{
- boost::filesystem::path p;
return dir ("video");
}
@@ -268,6 +268,10 @@ Film::make_dcp ()
#endif
pair<string, int> const c = cpu_info ();
log()->log (String::compose ("CPU: %1, %2 processors", c.first, c.second));
+ list<pair<string, string> > const m = mount_info ();
+ for (list<pair<string, string> >::const_iterator i = m.begin(); i != m.end(); ++i) {
+ log()->log (String::compose ("Mount: %1 %2", i->first, i->second));
+ }
if (container() == 0) {
throw MissingSettingError (_("container"));
@@ -353,6 +357,7 @@ Film::write_metadata () const
root->add_child("DCPVideoFrameRate")->add_child_text (lexical_cast<string> (_dcp_video_frame_rate));
root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date));
root->add_child("DCPAudioChannels")->add_child_text (lexical_cast<string> (_dcp_audio_channels));
+ root->add_child("MinimumAudioChannels")->add_child_text (lexical_cast<string> (_minimum_audio_channels));
_playlist->as_xml (root->add_child ("Playlist"));
doc.write_to_file_formatted (file ("metadata.xml"));
@@ -400,6 +405,7 @@ Film::read_metadata ()
_dcp_video_frame_rate = f.number_child<int> ("DCPVideoFrameRate");
_dci_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
_dcp_audio_channels = f.number_child<int> ("DCPAudioChannels");
+ _minimum_audio_channels = f.number_child<int> ("MinimumAudioChannels");
_playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"));
@@ -642,6 +648,16 @@ Film::set_dci_metadata (DCIMetadata m)
void
+Film::set_minimum_audio_channels (int c)
+{
+ {
+ boost::mutex::scoped_lock lm (_state_mutex);
+ _minimum_audio_channels = c;
+ }
+ signal_changed (MINIMUM_AUDIO_CHANNELS);
+}
+
+void
Film::set_dcp_video_frame_rate (int f)
{
{
diff --git a/src/lib/film.h b/src/lib/film.h
index 5bb9acf29..f5a7c1246 100644
--- a/src/lib/film.h
+++ b/src/lib/film.h
@@ -141,6 +141,7 @@ public:
J2K_BANDWIDTH,
DCI_METADATA,
DCP_VIDEO_FRAME_RATE,
+ MINIMUM_AUDIO_CHANNELS
};
@@ -217,6 +218,11 @@ public:
return _dcp_audio_channels;
}
+ int minimum_audio_channels () const {
+ boost::mutex::scoped_lock lm (_state_mutex);
+ return _minimum_audio_channels;
+ }
+
/* SET */
void set_directory (std::string);
@@ -236,6 +242,7 @@ public:
void set_dci_metadata (DCIMetadata);
void set_dcp_video_frame_rate (int);
void set_dci_date_today ();
+ void set_minimum_audio_channels (int);
/** Emitted when some property has of the Film has changed */
mutable boost::signals2::signal<void (Property)> Changed;
@@ -298,6 +305,7 @@ private:
/** The date that we should use in a DCI name */
boost::gregorian::date _dci_date;
int _dcp_audio_channels;
+ int _minimum_audio_channels;
/** true if our state has changed since we last saved it */
mutable bool _dirty;
diff --git a/src/lib/ratio.h b/src/lib/ratio.h
index 6916a7491..5480eee12 100644
--- a/src/lib/ratio.h
+++ b/src/lib/ratio.h
@@ -17,6 +17,9 @@
*/
+#ifndef DCPOMATIC_RATIO_H
+#define DCPOMATIC_RATIO_H
+
#include <vector>
#include <libdcp/util.h>
@@ -64,3 +67,5 @@ private:
static std::vector<Ratio const *> _ratios;
};
+
+#endif
diff --git a/src/lib/trimmer.cc b/src/lib/trimmer.cc
new file mode 100644
index 000000000..99f04793f
--- /dev/null
+++ b/src/lib/trimmer.cc
@@ -0,0 +1,123 @@
+/*
+ Copyright (C) 2013 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 <stdint.h>
+#include "trimmer.h"
+
+using std::cout;
+using std::max;
+using boost::shared_ptr;
+
+/** @param audio_sample_rate Audio sampling rate, or 0 if there is no audio */
+Trimmer::Trimmer (
+ shared_ptr<Log> log,
+ int video_trim_start,
+ int video_trim_end,
+ int video_length,
+ int audio_sample_rate,
+ float frames_per_second,
+ int dcp_frames_per_second
+ )
+ : AudioVideoProcessor (log)
+ , _video_start (video_trim_start)
+ , _video_end (video_length - video_trim_end)
+ , _video_in (0)
+ , _audio_in (0)
+{
+ FrameRateConversion frc (frames_per_second, dcp_frames_per_second);
+
+ if (frc.skip) {
+ _video_start /= 2;
+ _video_end /= 2;
+ } else if (frc.repeat) {
+ _video_start *= 2;
+ _video_end *= 2;
+ }
+
+ if (audio_sample_rate) {
+ _audio_start = video_frames_to_audio_frames (_video_start, audio_sample_rate, frames_per_second);
+ _audio_end = video_frames_to_audio_frames (_video_end, audio_sample_rate, frames_per_second);
+ }
+
+ /* XXX: this is a hack; if there is no trim at the end, set
+ the audio end point to infinity so that
+ shorter-video-than-audio does not trim audio (which breaks
+ the current set of regression tests). This could be
+ removed if a) the regression tests are regenerated and b) I
+ can work out what DCP length should be.
+
+ There is also a problem whereby black video frames inserted
+ at the start of the output by the matcher are not taken into account,
+ so if black frames are inserted it means more gets trimmed off the
+ end than should be. Hack around this in similar fashion with the
+ _video_end = INT_MAX line.
+ */
+ if (video_trim_end == 0) {
+ _video_end = INT_MAX;
+ _audio_end = INT64_MAX;
+ }
+}
+
+void
+Trimmer::process_video (shared_ptr<const Image> image, bool same, shared_ptr<Subtitle> sub)
+{
+ if (_video_in >= _video_start && _video_in < _video_end) {
+ Video (image, same, sub);
+ }
+
+ ++_video_in;
+}
+
+void
+Trimmer::process_audio (shared_ptr<const AudioBuffers> audio)
+{
+ int64_t offset = _audio_start - _audio_in;
+ if (offset > audio->frames()) {
+ /* we haven't reached the start of the untrimmed section yet */
+ _audio_in += audio->frames ();
+ return;
+ }
+
+ if (offset < 0) {
+ offset = 0;
+ }
+
+ int64_t length = _audio_end - max (_audio_in, _audio_start);
+ if (length < 0) {
+ _audio_in += audio->frames ();
+ return;
+ }
+
+ if (length > (audio->frames() - offset)) {
+ length = audio->frames () - offset;
+ }
+
+ _audio_in += audio->frames ();
+
+ if (offset != 0 || length != audio->frames ()) {
+ shared_ptr<AudioBuffers> copy (new AudioBuffers (audio));
+ copy->move (offset, 0, length);
+ copy->set_frames (length);
+ audio = copy;
+ }
+
+ Audio (audio);
+}
+
diff --git a/src/lib/util.h b/src/lib/util.h
index c68bb4f16..7af8ffedf 100644
--- a/src/lib/util.h
+++ b/src/lib/util.h
@@ -52,6 +52,7 @@ extern "C" {
#define MAX_AUDIO_CHANNELS 6
class Scaler;
+class Film;
extern std::string seconds_to_hms (int);
extern std::string time_to_hms (Time);
diff --git a/src/lib/version.h b/src/lib/version.h
index 518862fc4..b70be8343 100644
--- a/src/lib/version.h
+++ b/src/lib/version.h
@@ -1,3 +1,4 @@
extern char const * dcpomatic_version;
extern char const * dcpomatic_git_commit;
+extern char const * dcpomatic_cxx_flags;
diff --git a/src/lib/writer.cc b/src/lib/writer.cc
index cf81d5b70..cbb84a940 100644
--- a/src/lib/writer.cc
+++ b/src/lib/writer.cc
@@ -18,6 +18,7 @@
*/
#include <fstream>
+#include <cerrno>
#include <libdcp/picture_asset.h>
#include <libdcp/sound_asset.h>
#include <libdcp/picture_frame.h>
@@ -342,8 +343,9 @@ Writer::check_existing_picture_mxf ()
boost::filesystem::path p;
p /= _film->internal_video_mxf_dir ();
p /= _film->internal_video_mxf_filename ();
- FILE* mxf = fopen (p.string().c_str(), N_("rb"));
+ FILE* mxf = fopen (p.string().c_str(), "rb");
if (!mxf) {
+ _film->log()->log (String::compose ("Could not open existing MXF at %1 (errno=%2)", p.string(), errno));
return;
}
diff --git a/src/lib/wscript b/src/lib/wscript
index 3e7f2e33b..2f8653984 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -73,7 +73,7 @@ def build(bld):
obj.source = sources + ' version.cc'
if bld.env.TARGET_WINDOWS:
- obj.uselib += ' WINSOCK2 BFD DBGHELP IBERTY'
+ obj.uselib += ' WINSOCK2 BFD DBGHELP IBERTY SHLWAPI'
obj.source += ' stack.cpp'
if bld.env.STATIC:
obj.uselib += ' XML++'