diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-07-09 20:35:39 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-07-09 20:35:39 +0100 |
| commit | 89115db77729a2c99f1a09ff6a461720e16f889e (patch) | |
| tree | e528ec014b820d4e0efa28893dcee91cd76ee618 /src/lib | |
| parent | f8ad440cf187c517b7800f3efdfc0954025c4422 (diff) | |
| parent | d2ff6a6b0256e256b6df416f280c846072f7682f (diff) | |
Merge master.
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/combiner.cc | 74 | ||||
| -rw-r--r-- | src/lib/config.cc | 3 | ||||
| -rw-r--r-- | src/lib/cross.cc | 110 | ||||
| -rw-r--r-- | src/lib/cross.h | 6 | ||||
| -rw-r--r-- | src/lib/encoder.cc | 1 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.cc | 17 | ||||
| -rw-r--r-- | src/lib/film.cc | 20 | ||||
| -rw-r--r-- | src/lib/film.h | 8 | ||||
| -rw-r--r-- | src/lib/ratio.h | 5 | ||||
| -rw-r--r-- | src/lib/trimmer.cc | 123 | ||||
| -rw-r--r-- | src/lib/util.h | 1 | ||||
| -rw-r--r-- | src/lib/version.h | 1 | ||||
| -rw-r--r-- | src/lib/writer.cc | 4 | ||||
| -rw-r--r-- | src/lib/wscript | 2 |
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++' |
