diff options
| author | Carl Hetherington <cth@carlh.net> | 2024-03-19 14:02:47 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2024-04-22 13:03:04 +0200 |
| commit | f72a79c93626e773214f1a0318adf2445ac2ee72 (patch) | |
| tree | 7b84a9c0c000618893a3fa3d609507c855735669 /src | |
| parent | 5b2e3126602d508498a99bce256f5f465f095d43 (diff) | |
Support encoding of MPEG2 DCPs.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/dcp_film_encoder.cc | 11 | ||||
| -rw-r--r-- | src/lib/film.cc | 16 | ||||
| -rw-r--r-- | src/lib/film.h | 7 | ||||
| -rw-r--r-- | src/lib/film_property.h | 1 | ||||
| -rw-r--r-- | src/lib/mpeg2_encoder.cc | 80 | ||||
| -rw-r--r-- | src/lib/mpeg2_encoder.h | 42 | ||||
| -rw-r--r-- | src/lib/reel_writer.cc | 114 | ||||
| -rw-r--r-- | src/lib/reel_writer.h | 13 | ||||
| -rw-r--r-- | src/lib/video_encoding.cc | 56 | ||||
| -rw-r--r-- | src/lib/video_encoding.h | 34 | ||||
| -rw-r--r-- | src/lib/writer.cc | 14 | ||||
| -rw-r--r-- | src/lib/writer.h | 7 | ||||
| -rw-r--r-- | src/lib/wscript | 2 | ||||
| -rw-r--r-- | src/wx/dcp_panel.cc | 16 |
14 files changed, 366 insertions, 47 deletions
diff --git a/src/lib/dcp_film_encoder.cc b/src/lib/dcp_film_encoder.cc index b508b66b6..17f531693 100644 --- a/src/lib/dcp_film_encoder.cc +++ b/src/lib/dcp_film_encoder.cc @@ -33,6 +33,7 @@ #include "film.h" #include "j2k_encoder.h" #include "job.h" +#include "mpeg2_encoder.h" #include "player.h" #include "player_video.h" #include "referenced_reel_asset.h" @@ -67,10 +68,18 @@ using namespace dcpomatic; DCPFilmEncoder::DCPFilmEncoder(shared_ptr<const Film> film, weak_ptr<Job> job) : FilmEncoder(film, job) , _writer(film, job) - , _encoder(new J2KEncoder(film, _writer)) , _finishing (false) , _non_burnt_subtitles (false) { + switch (_film->video_encoding()) { + case VideoEncoding::JPEG2000: + _encoder.reset(new J2KEncoder(film, _writer)); + break; + case VideoEncoding::MPEG2: + _encoder.reset(new MPEG2Encoder(film, _writer)); + break; + } + _player_video_connection = _player.Video.connect(bind(&DCPFilmEncoder::video, this, _1, _2)); _player_audio_connection = _player.Audio.connect(bind(&DCPFilmEncoder::audio, this, _1, _2)); _player_text_connection = _player.Text.connect(bind(&DCPFilmEncoder::text, this, _1, _2, _3, _4)); diff --git a/src/lib/film.cc b/src/lib/film.cc index 3de536a6d..54c68d756 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -169,6 +169,7 @@ Film::Film (optional<boost::filesystem::path> dir) , _three_d (false) , _sequence (true) , _interop (Config::instance()->default_interop ()) + , _video_encoding(VideoEncoding::JPEG2000) , _limit_to_smpte_bv20(false) , _audio_processor (0) , _reel_type (ReelType::SINGLE) @@ -251,6 +252,9 @@ Film::video_identifier () const if (_interop) { s += "_I"; + if (_video_encoding == VideoEncoding::MPEG2) { + s += "_M"; + } } else { s += "_S"; if (_limit_to_smpte_bv20) { @@ -405,6 +409,7 @@ Film::metadata (bool with_content_paths) const cxml::add_text_child(root, "ThreeD", _three_d ? "1" : "0"); cxml::add_text_child(root, "Sequence", _sequence ? "1" : "0"); cxml::add_text_child(root, "Interop", _interop ? "1" : "0"); + cxml::add_text_child(root, "VideoEncoding", video_encoding_to_string(_video_encoding)); cxml::add_text_child(root, "LimitToSMPTEBv20", _limit_to_smpte_bv20 ? "1" : "0"); cxml::add_text_child(root, "Encrypted", _encrypted ? "1" : "0"); cxml::add_text_child(root, "Key", _key.hex ()); @@ -595,6 +600,9 @@ Film::read_metadata (optional<boost::filesystem::path> path) _three_d = f.bool_child ("ThreeD"); _interop = f.bool_child ("Interop"); + if (auto encoding = f.optional_string_child("VideoEncoding")) { + _video_encoding = video_encoding_from_string(*encoding); + } _limit_to_smpte_bv20 = f.optional_bool_child("LimitToSMPTEBv20").get_value_or(false); _key = dcp::Key (f.string_child ("Key")); _context_id = f.optional_string_child("ContextID").get_value_or (dcp::make_uuid ()); @@ -1220,6 +1228,14 @@ Film::set_interop (bool i) void +Film::set_video_encoding(VideoEncoding encoding) +{ + FilmChangeSignaller ch(this, FilmProperty::VIDEO_ENCODING); + _video_encoding = encoding; +} + + +void Film::set_limit_to_smpte_bv20(bool limit) { FilmChangeSignaller ch(this, FilmProperty::LIMIT_TO_SMPTE_BV20); diff --git a/src/lib/film.h b/src/lib/film.h index 392f1dc5e..ff3dee6fc 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -41,6 +41,7 @@ #include "transcode_job.h" #include "types.h" #include "util.h" +#include "video_encoding.h" #include <dcp/encrypted_kdm.h> #include <dcp/file.h> #include <dcp/key.h> @@ -275,6 +276,10 @@ public: return _interop; } + VideoEncoding video_encoding() const { + return _video_encoding; + } + bool limit_to_smpte_bv20() const { return _limit_to_smpte_bv20; } @@ -408,6 +413,7 @@ public: void set_isdcf_date_today (); void set_sequence (bool); void set_interop (bool); + void set_video_encoding(VideoEncoding encoding); void set_limit_to_smpte_bv20(bool); void set_audio_processor (AudioProcessor const * processor); void set_reel_type (ReelType); @@ -529,6 +535,7 @@ private: bool _three_d; bool _sequence; bool _interop; + VideoEncoding _video_encoding; bool _limit_to_smpte_bv20; AudioProcessor const * _audio_processor; ReelType _reel_type; diff --git a/src/lib/film_property.h b/src/lib/film_property.h index b755a4887..ebda0e807 100644 --- a/src/lib/film_property.h +++ b/src/lib/film_property.h @@ -47,6 +47,7 @@ enum class FilmProperty { THREE_D, SEQUENCE, INTEROP, + VIDEO_ENCODING, LIMIT_TO_SMPTE_BV20, AUDIO_PROCESSOR, REEL_TYPE, diff --git a/src/lib/mpeg2_encoder.cc b/src/lib/mpeg2_encoder.cc new file mode 100644 index 000000000..49bff19d4 --- /dev/null +++ b/src/lib/mpeg2_encoder.cc @@ -0,0 +1,80 @@ +/* + Copyright (C) 2024 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic 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. + + DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "mpeg2_encoder.h" +#include "writer.h" +#include <dcp/ffmpeg_image.h> +extern "C" { +#include <libavutil/pixfmt.h> +} + + +using std::shared_ptr; + + +MPEG2Encoder::MPEG2Encoder(shared_ptr<const Film> film, Writer& writer) + : VideoEncoder(film, writer) + , _transcoder(film->frame_size(), film->video_frame_rate(), film->video_bit_rate()) +{ + +} + + +void +MPEG2Encoder::encode(shared_ptr<PlayerVideo> pv, dcpomatic::DCPTime time) +{ + VideoEncoder::encode(pv, time); + + auto image = pv->image( + [](AVPixelFormat) { return AV_PIX_FMT_YUV420P; }, + VideoRange::VIDEO, + false + ); + + dcp::FFmpegImage ffmpeg_image(time.get() * _film->video_frame_rate() / dcpomatic::DCPTime::HZ); + + DCPOMATIC_ASSERT(image->size() == ffmpeg_image.size()); + + auto height = image->size().height; + + for (int y = 0; y < height; ++y) { + memcpy(ffmpeg_image.y() + ffmpeg_image.y_stride() * y, image->data()[0] + image->stride()[0] * y, ffmpeg_image.y_stride()); + } + + for (int y = 0; y < height / 2; ++y) { + memcpy(ffmpeg_image.u() + ffmpeg_image.u_stride() * y, image->data()[1] + image->stride()[1] * y, ffmpeg_image.u_stride()); + memcpy(ffmpeg_image.v() + ffmpeg_image.v_stride() * y, image->data()[2] + image->stride()[2] * y, ffmpeg_image.v_stride()); + } + + if (auto compressed = _transcoder.compress_frame(std::move(ffmpeg_image))) { + _writer.write(compressed->first, compressed->second); + } +} + + +void +MPEG2Encoder::end() +{ + if (auto compressed = _transcoder.flush()) { + _writer.write(compressed->first, compressed->second); + } +} + diff --git a/src/lib/mpeg2_encoder.h b/src/lib/mpeg2_encoder.h new file mode 100644 index 000000000..1b2259d26 --- /dev/null +++ b/src/lib/mpeg2_encoder.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2024 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic 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. + + DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "video_encoder.h" +#include <dcp/mpeg2_transcode.h> + + +class MPEG2Encoder : public VideoEncoder +{ +public: + MPEG2Encoder(std::shared_ptr<const Film> film, Writer& writer); + + void encode(std::shared_ptr<PlayerVideo> pv, dcpomatic::DCPTime time) override; + + void pause() override {} + void resume() override {} + + /** Called when a processing run has finished */ + void end() override; + +private: + dcp::MPEG2Compressor _transcoder; +}; + diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc index 201c23e71..8dc0d7f06 100644 --- a/src/lib/reel_writer.cc +++ b/src/lib/reel_writer.cc @@ -146,30 +146,45 @@ ReelWriter::ReelWriter ( dcp::filesystem::rename(asset.string() + ".tmp", asset); } + auto const rate = dcp::Fraction(film()->video_frame_rate(), 1); - if (film()->three_d()) { - _picture_asset = std::make_shared<dcp::StereoJ2KPictureAsset>(dcp::Fraction(film()->video_frame_rate(), 1), standard); - } else { - _picture_asset = std::make_shared<dcp::MonoJ2KPictureAsset>(dcp::Fraction(film()->video_frame_rate(), 1), standard); - } + auto setup = [this](shared_ptr<dcp::PictureAsset> asset) { + asset->set_size(film()->frame_size()); + asset->set_metadata(mxf_metadata()); - _picture_asset->set_size (film()->frame_size()); - _picture_asset->set_metadata (mxf_metadata()); + if (film()->encrypted()) { + asset->set_key(film()->key()); + asset->set_context_id(film()->context_id()); + } + }; - if (film()->encrypted()) { - _picture_asset->set_key (film()->key()); - _picture_asset->set_context_id (film()->context_id()); + if (film()->video_encoding() == VideoEncoding::JPEG2000) { + if (film()->three_d()) { + _j2k_picture_asset = std::make_shared<dcp::StereoJ2KPictureAsset>(rate, standard); + } else { + _j2k_picture_asset = std::make_shared<dcp::MonoJ2KPictureAsset>(rate, standard); + } + setup(_j2k_picture_asset); + _j2k_picture_asset->set_file(asset); + _j2k_picture_asset_writer = _j2k_picture_asset->start_write(asset, _first_nonexistent_frame > 0 ? dcp::Behaviour::OVERWRITE_EXISTING : dcp::Behaviour::MAKE_NEW); + } else { + _mpeg2_picture_asset = std::make_shared<dcp::MonoMPEG2PictureAsset>(rate); + setup(_mpeg2_picture_asset); + _mpeg2_picture_asset->set_file(asset); + _mpeg2_picture_asset_writer = _mpeg2_picture_asset->start_write(asset, _first_nonexistent_frame > 0 ? dcp::Behaviour::OVERWRITE_EXISTING : dcp::Behaviour::MAKE_NEW); } - _picture_asset->set_file (asset); - _picture_asset_writer = _picture_asset->start_write(asset, _first_nonexistent_frame > 0 ? dcp::Behaviour::OVERWRITE_EXISTING : dcp::Behaviour::MAKE_NEW); } else if (!text_only) { /* We already have a complete picture asset that we can just re-use */ /* XXX: what about if the encryption key changes? */ - if (film()->three_d()) { - _picture_asset = make_shared<dcp::StereoJ2KPictureAsset>(asset); + if (film()->video_encoding() == VideoEncoding::JPEG2000) { + if (film()->three_d()) { + _j2k_picture_asset = make_shared<dcp::StereoJ2KPictureAsset>(asset); + } else { + _j2k_picture_asset = make_shared<dcp::MonoJ2KPictureAsset>(asset); + } } else { - _picture_asset = make_shared<dcp::MonoJ2KPictureAsset>(asset); + _mpeg2_picture_asset = make_shared<dcp::MonoMPEG2PictureAsset>(asset); } } @@ -272,7 +287,7 @@ ReelWriter::check_existing_picture_asset (boost::filesystem::path asset) void ReelWriter::write (shared_ptr<const Data> encoded, Frame frame, Eyes eyes) { - if (!_picture_asset_writer) { + if (!_j2k_picture_asset_writer) { /* We're not writing any data */ return; } @@ -300,21 +315,28 @@ ReelWriter::write (shared_ptr<const dcp::AtmosFrame> atmos, AtmosMetadata metada void -ReelWriter::fake_write (int size) +ReelWriter::write(shared_ptr<dcp::MonoMPEG2PictureFrame> image) +{ + _mpeg2_picture_asset_writer->write(image->data(), image->size()); +} + + +void +ReelWriter::fake_write(dcp::J2KFrameInfo const& info) { - if (!_picture_asset_writer) { + if (!_j2k_picture_asset_writer) { /* We're not writing any data */ return; } - _picture_asset_writer->fake_write (size); + _j2k_picture_asset_writer->fake_write(info); } void ReelWriter::repeat_write (Frame frame, Eyes eyes) { - if (!_picture_asset_writer) { + if (!_j2k_picture_asset_writer) { /* We're not writing any data */ return; } @@ -327,10 +349,16 @@ ReelWriter::repeat_write (Frame frame, Eyes eyes) void ReelWriter::finish (boost::filesystem::path output_dcp) { - if (_picture_asset_writer && !_picture_asset_writer->finalize ()) { - /* Nothing was written to the picture asset */ - LOG_GENERAL ("Nothing was written to reel %1 of %2", _reel_index, _reel_count); - _picture_asset.reset (); + if (_j2k_picture_asset_writer && !_j2k_picture_asset_writer->finalize()) { + /* Nothing was written to the J2K picture asset */ + LOG_GENERAL("Nothing was written to J2K asset for reel %1 of %2", _reel_index, _reel_count); + _j2k_picture_asset.reset(); + } + + if (_mpeg2_picture_asset_writer && !_mpeg2_picture_asset_writer->finalize()) { + /* Nothing was written to the MPEG2 picture asset */ + LOG_GENERAL("Nothing was written to MPEG2 asset for reel %1 of %2", _reel_index, _reel_count); + _mpeg2_picture_asset.reset(); } if (_sound_asset_writer && !_sound_asset_writer->finalize ()) { @@ -338,12 +366,21 @@ ReelWriter::finish (boost::filesystem::path output_dcp) _sound_asset.reset (); } + shared_ptr<dcp::PictureAsset> picture_asset; + if (_j2k_picture_asset) { + picture_asset = _j2k_picture_asset; + } else if (_mpeg2_picture_asset) { + picture_asset = _mpeg2_picture_asset; + } + /* Hard-link any video asset file into the DCP */ - if (_picture_asset) { - DCPOMATIC_ASSERT (_picture_asset->file()); - boost::filesystem::path video_from = _picture_asset->file().get(); - boost::filesystem::path video_to = output_dcp; - video_to /= video_asset_filename (_picture_asset, _reel_index, _reel_count, _content_summary); + if (picture_asset) { + auto const file = picture_asset->file(); + DCPOMATIC_ASSERT(file); + + auto video_from = *file; + auto video_to = output_dcp; + video_to /= video_asset_filename(picture_asset, _reel_index, _reel_count, _content_summary); /* There may be an existing "to" file if we are recreating a DCP in the same place without changing any video. */ @@ -371,7 +408,7 @@ ReelWriter::finish (boost::filesystem::path output_dcp) } } - _picture_asset->set_file (video_to); + picture_asset->set_file(video_to); } /* Move the audio asset into the DCP */ @@ -495,17 +532,17 @@ ReelWriter::create_reel_picture (shared_ptr<dcp::Reel> reel, list<ReferencedReel { shared_ptr<dcp::ReelPictureAsset> reel_asset; - if (_picture_asset) { + if (_j2k_picture_asset) { /* We have made a picture asset of our own. Put it into the reel */ - auto mono = dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>(_picture_asset); - if (mono) { + if (auto mono = dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>(_j2k_picture_asset)) { reel_asset = make_shared<dcp::ReelMonoPictureAsset>(mono, 0); } - auto stereo = dynamic_pointer_cast<dcp::StereoJ2KPictureAsset>(_picture_asset); - if (stereo) { + if (auto stereo = dynamic_pointer_cast<dcp::StereoJ2KPictureAsset>(_j2k_picture_asset)) { reel_asset = make_shared<dcp::ReelStereoPictureAsset>(stereo, 0); } + } else if (_mpeg2_picture_asset) { + reel_asset = make_shared<dcp::ReelMonoPictureAsset>(_mpeg2_picture_asset, 0); } else { LOG_GENERAL ("no picture asset of our own; look through %1", refs.size()); /* We don't have a picture asset of our own; hopefully we have one to reference */ @@ -734,8 +771,11 @@ try { vector<shared_ptr<const dcp::Asset>> assets; - if (_picture_asset) { - assets.push_back(_picture_asset); + if (_j2k_picture_asset) { + assets.push_back(_j2k_picture_asset); + } + if (_mpeg2_picture_asset) { + assets.push_back(_mpeg2_picture_asset); } if (_sound_asset) { assets.push_back(_sound_asset); diff --git a/src/lib/reel_writer.h b/src/lib/reel_writer.h index 30abdd563..f6273f8e9 100644 --- a/src/lib/reel_writer.h +++ b/src/lib/reel_writer.h @@ -31,7 +31,10 @@ #include "weak_film.h" #include <dcp/atmos_asset_writer.h> #include <dcp/file.h> +#include <dcp/ffmpeg_image.h> #include <dcp/j2k_picture_asset_writer.h> +#include <dcp/mono_mpeg2_picture_frame.h> +#include <dcp/mpeg2_picture_asset_writer.h> class AudioBuffers; @@ -46,6 +49,7 @@ namespace dcp { class MonoJ2KPictureAssetWriter; class J2KPictureAsset; class J2KPictureAssetWriter; + class MPEG2PictureAsset; class Reel; class ReelAsset; class ReelPictureAsset; @@ -70,11 +74,12 @@ public: ); void write (std::shared_ptr<const dcp::Data> encoded, Frame frame, Eyes eyes); - void fake_write (int size); + void fake_write(dcp::J2KFrameInfo const& info); void repeat_write (Frame frame, Eyes eyes); void write (std::shared_ptr<const AudioBuffers> audio); void write(PlayerText text, TextType type, boost::optional<DCPTextTrack> track, dcpomatic::DCPTimePeriod period, FontIdMap const& fonts, std::shared_ptr<dcpomatic::Font> chosen_interop_font); void write (std::shared_ptr<const dcp::AtmosFrame> atmos, AtmosMetadata metadata); + void write(std::shared_ptr<dcp::MonoMPEG2PictureFrame> image); void finish (boost::filesystem::path output_dcp); std::shared_ptr<dcp::Reel> create_reel ( @@ -131,9 +136,11 @@ private: dcp::ArrayData _default_font; - std::shared_ptr<dcp::J2KPictureAsset> _picture_asset; + std::shared_ptr<dcp::J2KPictureAsset> _j2k_picture_asset; + std::shared_ptr<dcp::MPEG2PictureAsset> _mpeg2_picture_asset; /** picture asset writer, or 0 if we are not writing any picture because we already have one */ - std::shared_ptr<dcp::J2KPictureAssetWriter> _picture_asset_writer; + std::shared_ptr<dcp::J2KPictureAssetWriter> _j2k_picture_asset_writer; + std::shared_ptr<dcp::MPEG2PictureAssetWriter> _mpeg2_picture_asset_writer; std::shared_ptr<dcp::SoundAsset> _sound_asset; std::shared_ptr<dcp::SoundAssetWriter> _sound_asset_writer; std::shared_ptr<dcp::SubtitleAsset> _subtitle_asset; diff --git a/src/lib/video_encoding.cc b/src/lib/video_encoding.cc new file mode 100644 index 000000000..ead1a2eaf --- /dev/null +++ b/src/lib/video_encoding.cc @@ -0,0 +1,56 @@ +/* + Copyright (C) 2024 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic 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. + + DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "dcpomatic_assert.h" +#include "video_encoding.h" + + +using std::string; + + +string +video_encoding_to_string(VideoEncoding encoding) +{ + switch (encoding) { + case VideoEncoding::JPEG2000: + return "jpeg2000"; + case VideoEncoding::MPEG2: + return "mpeg2"; + } + + DCPOMATIC_ASSERT(false); +} + + +VideoEncoding +video_encoding_from_string(string const& encoding) +{ + if (encoding == "jpeg2000") { + return VideoEncoding::JPEG2000; + } + + if (encoding == "mpeg2") { + return VideoEncoding::MPEG2; + } + + DCPOMATIC_ASSERT(false); +} + diff --git a/src/lib/video_encoding.h b/src/lib/video_encoding.h new file mode 100644 index 000000000..45ee0b21e --- /dev/null +++ b/src/lib/video_encoding.h @@ -0,0 +1,34 @@ +/* + Copyright (C) 2024 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic 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. + + DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include <string> + + +enum class VideoEncoding +{ + JPEG2000, + MPEG2 +}; + + +std::string video_encoding_to_string(VideoEncoding encoding); +VideoEncoding video_encoding_from_string(std::string const& encoding); + diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 1c04f4065..3dd22718f 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -40,6 +40,7 @@ #include "version.h" #include "writer.h" #include <dcp/cpl.h> +#include <dcp/mono_mpeg2_picture_frame.h> #include <dcp/locale_convert.h> #include <dcp/raw_convert.h> #include <dcp/reel_closed_caption_asset.h> @@ -169,6 +170,13 @@ Writer::write (shared_ptr<const Data> encoded, Frame frame, Eyes eyes) } +void +Writer::write(shared_ptr<dcp::MonoMPEG2PictureFrame> image, Frame frame) +{ + _reels[video_reel(frame)].write(image); +} + + bool Writer::can_repeat (Frame frame) const { @@ -230,7 +238,7 @@ Writer::fake_write (Frame frame, Eyes eyes) QueueItem qi; qi.type = QueueItem::Type::FAKE; - qi.size = J2KFrameInfo(film()->info_file_handle(_reels[reel].period(), true), frame_in_reel, eyes).size; + qi.info = J2KFrameInfo(film()->info_file_handle(_reels[reel].period(), true), frame_in_reel, eyes); DCPOMATIC_ASSERT((film()->three_d() && eyes != Eyes::BOTH) || (!film()->three_d() && eyes == Eyes::BOTH)); @@ -401,7 +409,7 @@ try if (i.type == QueueItem::Type::FULL) { LOG_WARNING (N_("- type FULL, frame %1, eyes %2"), i.frame, (int) i.eyes); } else { - LOG_WARNING (N_("- type FAKE, size %1, frame %2, eyes %3"), i.size, i.frame, (int) i.eyes); + LOG_WARNING (N_("- type FAKE, size %1, frame %2, eyes %3"), i.info.size, i.frame, (int) i.eyes); } } } @@ -432,7 +440,7 @@ try break; case QueueItem::Type::FAKE: LOG_DEBUG_ENCODE (N_("Writer FAKE-writes %1"), qi.frame); - reel.fake_write (qi.size); + reel.fake_write(qi.info); ++_fake_written; break; case QueueItem::Type::REPEAT: diff --git a/src/lib/writer.h b/src/lib/writer.h index 1cd278221..1dd88d8a9 100644 --- a/src/lib/writer.h +++ b/src/lib/writer.h @@ -37,6 +37,8 @@ #include "text_type.h" #include "weak_film.h" #include <dcp/atmos_frame.h> +#include <dcp/frame_info.h> +#include <dcp/mono_mpeg2_picture_frame.h> #include <boost/thread.hpp> #include <boost/thread/condition.hpp> #include <list> @@ -74,8 +76,8 @@ public: /** encoded data for FULL */ std::shared_ptr<const dcp::Data> encoded; - /** size of data for FAKE */ - int size = 0; + /** info for FAKE */ + dcp::J2KFrameInfo info; /** reel index */ size_t reel = 0; /** frame index within the reel */ @@ -122,6 +124,7 @@ public: void write (std::vector<std::shared_ptr<dcpomatic::Font>> fonts); void write (ReferencedReelAsset asset); void write (std::shared_ptr<const dcp::AtmosFrame> atmos, dcpomatic::DCPTime time, AtmosMetadata metadata); + void write (std::shared_ptr<dcp::MonoMPEG2PictureFrame> image, Frame frame); void finish (boost::filesystem::path output_dcp); void set_encoder_threads (int threads); diff --git a/src/lib/wscript b/src/lib/wscript index 8e839cb49..86cf2e0b6 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -154,6 +154,7 @@ sources = """ maths_util.cc memory_util.cc mid_side_decoder.cc + mpeg2_encoder.cc named_channel.cc overlaps.cc pixel_quanta.cc @@ -212,6 +213,7 @@ sources = """ video_content.cc video_decoder.cc video_encoder.cc + video_encoding.cc video_filter_graph.cc video_filter_graph_set.cc video_frame_type.cc diff --git a/src/wx/dcp_panel.cc b/src/wx/dcp_panel.cc index c2f32fbb9..4ae89d443 100644 --- a/src/wx/dcp_panel.cc +++ b/src/wx/dcp_panel.cc @@ -151,6 +151,7 @@ DCPPanel::add_standards() _standard->add(_("SMPTE (Bv2.0 only)"), N_("smpte-bv20")); } _standard->add(_("Interop"), N_("interop")); + _standard->add(_("MPEG2 Interop"), N_("mpeg2-interop")); _sizer->Layout(); } @@ -162,7 +163,11 @@ DCPPanel::set_standard() DCPOMATIC_ASSERT(!_film->limit_to_smpte_bv20() || _standard->GetCount() == 3); if (_film->interop()) { - checked_set(_standard, "interop"); + if (_film->video_encoding() == VideoEncoding::JPEG2000) { + checked_set(_standard, "interop"); + } else { + checked_set(_standard, "mpeg2-interop"); + } } else { checked_set(_standard, _film->limit_to_smpte_bv20() ? "smpte-bv20" : "smpte"); } @@ -184,12 +189,18 @@ DCPPanel::standard_changed () if (*data == N_("interop")) { _film->set_interop(true); _film->set_limit_to_smpte_bv20(false); + _film->set_video_encoding(VideoEncoding::JPEG2000); } else if (*data == N_("smpte")) { _film->set_interop(false); _film->set_limit_to_smpte_bv20(false); + _film->set_video_encoding(VideoEncoding::JPEG2000); } else if (*data == N_("smpte-bv20")) { _film->set_interop(false); _film->set_limit_to_smpte_bv20(true); + _film->set_video_encoding(VideoEncoding::JPEG2000); + } else if (*data == N_("mpeg2-interop")) { + _film->set_interop(true); + _film->set_video_encoding(VideoEncoding::MPEG2); } } @@ -445,6 +456,9 @@ DCPPanel::film_changed(FilmProperty p) setup_dcp_name (); _markers->Enable (!_film->interop()); break; + case FilmProperty::VIDEO_ENCODING: + set_standard(); + break; case FilmProperty::LIMIT_TO_SMPTE_BV20: set_standard(); break; |
