diff options
| author | Carl Hetherington <cth@carlh.net> | 2023-11-14 22:01:07 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2024-04-22 01:42:00 +0200 |
| commit | 066471f69400fc72e9c126ab36f5427329f30220 (patch) | |
| tree | dd15f6b6aa7a74b5cca2ce5f300723b1b72d2f11 /src | |
| parent | 0fbfe3d2dd5de9eab299f2ff569bff29cc6f8e19 (diff) | |
Support MPEG2 decompression.
Diffstat (limited to 'src')
| -rw-r--r-- | src/asset_factory.cc | 4 | ||||
| -rw-r--r-- | src/asset_reader.h | 1 | ||||
| -rw-r--r-- | src/dcp.cc | 2 | ||||
| -rw-r--r-- | src/exceptions.h | 18 | ||||
| -rw-r--r-- | src/ffmpeg_image.h | 86 | ||||
| -rw-r--r-- | src/mono_mpeg2_picture_asset.cc | 77 | ||||
| -rw-r--r-- | src/mono_mpeg2_picture_asset.h | 64 | ||||
| -rw-r--r-- | src/mono_mpeg2_picture_asset_reader.h | 58 | ||||
| -rw-r--r-- | src/mono_mpeg2_picture_frame.cc | 83 | ||||
| -rw-r--r-- | src/mono_mpeg2_picture_frame.h | 76 | ||||
| -rw-r--r-- | src/mpeg2_picture_asset.cc | 74 | ||||
| -rw-r--r-- | src/mpeg2_picture_asset.h | 77 | ||||
| -rw-r--r-- | src/mpeg2_transcode.cc | 143 | ||||
| -rw-r--r-- | src/mpeg2_transcode.h | 89 | ||||
| -rw-r--r-- | src/reel.cc | 2 | ||||
| -rw-r--r-- | src/reel_mono_picture_asset.cc | 2 | ||||
| -rw-r--r-- | src/reel_mono_picture_asset.h | 19 | ||||
| -rw-r--r-- | src/reel_picture_asset.cc | 2 | ||||
| -rw-r--r-- | src/reel_picture_asset.h | 32 | ||||
| -rw-r--r-- | src/wscript | 14 |
20 files changed, 908 insertions, 15 deletions
diff --git a/src/asset_factory.cc b/src/asset_factory.cc index 240b7685..26811366 100644 --- a/src/asset_factory.cc +++ b/src/asset_factory.cc @@ -41,6 +41,7 @@ #include "atmos_asset.h" #include "compose.hpp" #include "mono_j2k_picture_asset.h" +#include "mono_mpeg2_picture_asset.h" #include "smpte_subtitle_asset.h" #include "sound_asset.h" #include "stereo_j2k_picture_asset.h" @@ -68,8 +69,9 @@ dcp::asset_factory (boost::filesystem::path path, bool ignore_incorrect_picture_ } switch (type) { case ASDCP::ESS_UNKNOWN: + throw ReadError("Unknown asset type"); case ASDCP::ESS_MPEG2_VES: - throw ReadError ("MPEG2 video essences are not supported"); + return make_shared<MonoMPEG2PictureAsset>(path); case ASDCP::ESS_JPEG_2000: try { return make_shared<MonoJ2KPictureAsset>(path); diff --git a/src/asset_reader.h b/src/asset_reader.h index 3bf9b8b9..091ac915 100644 --- a/src/asset_reader.h +++ b/src/asset_reader.h @@ -91,6 +91,7 @@ protected: private: friend class AtmosAsset; friend class MonoJ2KPictureAsset; + friend class MonoMPEG2PictureAsset; friend class SoundAsset; friend class StereoJ2KPictureAsset; @@ -52,6 +52,7 @@ #include "interop_subtitle_asset.h" #include "metadata.h" #include "mono_j2k_picture_asset.h" +#include "mono_mpeg2_picture_asset.h" #include "j2k_picture_asset.h" #include "pkl.h" #include "raw_convert.h" @@ -247,6 +248,7 @@ DCP::read (vector<dcp::VerificationNote>* notes, bool ignore_incorrect_picture_m } } else if ( *pkl_type == remove_parameters(J2KPictureAsset::static_pkl_type(standard)) || + *pkl_type == remove_parameters(MPEG2PictureAsset::static_pkl_type(standard)) || *pkl_type == remove_parameters(SoundAsset::static_pkl_type(standard)) || *pkl_type == remove_parameters(AtmosAsset::static_pkl_type(standard)) || *pkl_type == remove_parameters(SMPTESubtitleAsset::static_pkl_type(standard)) diff --git a/src/exceptions.h b/src/exceptions.h index 8d85f02a..88ec253c 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -140,6 +140,24 @@ public: }; +class MPEG2CodecError : public MiscError +{ +public: + explicit MPEG2CodecError(std::string message) + : MiscError(message) + {} +}; + + +class MPEG2DecompressionError : public ReadError +{ +public: + explicit MPEG2DecompressionError(std::string message) + : ReadError(message) + {} +}; + + class BadContentKindError : public ReadError { public: diff --git a/src/ffmpeg_image.h b/src/ffmpeg_image.h new file mode 100644 index 00000000..56b68574 --- /dev/null +++ b/src/ffmpeg_image.h @@ -0,0 +1,86 @@ +/* + Copyright (C) 2023 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#ifndef LIBDCP_FFMPEG_IMAGE_H +#define LIBDCP_FFMPEG_IMAGE_H + + +extern "C" { +#include <libavutil/frame.h> +} +#include <algorithm> +#include <vector> + + +namespace dcp { + + +class FFmpegImage +{ +public: + explicit FFmpegImage(AVFrame* frame) + : _frame(frame) + {} + + FFmpegImage(FFmpegImage const& other) = delete; + FFmpegImage& operator=(FFmpegImage const& other) = delete; + + FFmpegImage(FFmpegImage&& other) { + std::swap(_frame, other._frame); + } + + FFmpegImage& operator=(FFmpegImage&& other) { + std::swap(_frame, other._frame); + return *this; + } + + ~FFmpegImage() + { + av_frame_free(&_frame); + } + + AVFrame const * frame() const { + return _frame; + } + +private: + AVFrame* _frame = nullptr; +}; + + +} + + +#endif + diff --git a/src/mono_mpeg2_picture_asset.cc b/src/mono_mpeg2_picture_asset.cc new file mode 100644 index 00000000..548e936a --- /dev/null +++ b/src/mono_mpeg2_picture_asset.cc @@ -0,0 +1,77 @@ +/* + Copyright (C) 2023 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#include "filesystem.h" +#include "mono_mpeg2_picture_asset.h" +#include "mono_mpeg2_picture_asset_reader.h" +#include <asdcp/AS_DCP.h> + + +using std::shared_ptr; +using namespace dcp; + + +MonoMPEG2PictureAsset::MonoMPEG2PictureAsset(boost::filesystem::path file) + : MPEG2PictureAsset(file) +{ + Kumu::FileReaderFactory factory; + ASDCP::MPEG2::MXFReader reader(factory); + auto const result = reader.OpenRead(dcp::filesystem::fix_long_path(file).string().c_str()); + if (ASDCP_FAILURE(result)) { + boost::throw_exception(MXFFileError("could not open MXF file for reading", file.string(), result)); + } + + ASDCP::MPEG2::VideoDescriptor desc; + if (ASDCP_FAILURE(reader.FillVideoDescriptor(desc))) { + boost::throw_exception(ReadError("could not read video MXF information")); + } + + read_video_descriptor(desc); + + ASDCP::WriterInfo info; + if (ASDCP_FAILURE(reader.FillWriterInfo(info))) { + boost::throw_exception(ReadError("could not read video MXF information")); + } + + _id = read_writer_info(info); +} + + +shared_ptr<MonoMPEG2PictureAssetReader> +MonoMPEG2PictureAsset::start_read () const +{ + /* Can't use make_shared here as the MonoMPEG2PictureAssetReader constructor is private */ + return shared_ptr<MonoMPEG2PictureAssetReader>(new MonoMPEG2PictureAssetReader(this, key(), standard())); + +} diff --git a/src/mono_mpeg2_picture_asset.h b/src/mono_mpeg2_picture_asset.h new file mode 100644 index 00000000..a740cc6c --- /dev/null +++ b/src/mono_mpeg2_picture_asset.h @@ -0,0 +1,64 @@ +/* + Copyright (C) 2023 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#ifndef LIBDCP_MONO_MPEG2_PICTURE_ASSET_H +#define LIBDCP_MONO_MPEG2_PICTURE_ASSET_H + + +/** @file src/mono_mpeg2_picture_asset.h + * @brief MonoMPEG2PictureAsset class + */ + + +#include "mpeg2_picture_asset.h" +#include "mono_mpeg2_picture_asset_reader.h" + + +namespace dcp { + + +class MonoMPEG2PictureAsset : public MPEG2PictureAsset +{ +public: + explicit MonoMPEG2PictureAsset(boost::filesystem::path file); + + std::shared_ptr<MonoMPEG2PictureAssetReader> start_read() const; +}; + + + +} + + +#endif diff --git a/src/mono_mpeg2_picture_asset_reader.h b/src/mono_mpeg2_picture_asset_reader.h new file mode 100644 index 00000000..75155f40 --- /dev/null +++ b/src/mono_mpeg2_picture_asset_reader.h @@ -0,0 +1,58 @@ +/* + Copyright (C) 2023 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +/** @file src/mono_mpeg2_picture_asset_reader.h + * @brief MonoJ2KPictureAssetReader typedef + */ + + +#ifndef LIBDCP_MONO_MPEG2_PICTURE_ASSET_READER_H +#define LIBDCP_MONO_MPEG2_PICTURE_ASSET_READER_H + + +#include "asset_reader.h" +#include "mono_mpeg2_picture_frame.h" + + +namespace dcp { + + +typedef AssetReader<ASDCP::MPEG2::MXFReader, MonoMPEG2PictureFrame> MonoMPEG2PictureAssetReader; + + +} + + +#endif + diff --git a/src/mono_mpeg2_picture_frame.cc b/src/mono_mpeg2_picture_frame.cc new file mode 100644 index 00000000..3da67212 --- /dev/null +++ b/src/mono_mpeg2_picture_frame.cc @@ -0,0 +1,83 @@ +/* + Copyright (C) 2023 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#include "compose.hpp" +#include "mono_mpeg2_picture_frame.h" + + +using std::make_shared; +using std::shared_ptr; +using namespace dcp; + + + +/** Make a picture frame from a 2D (monoscopic) asset. + * @param reader Reader for the asset's MXF file. + * @param n Frame within the asset, not taking EntryPoint into account. + * @param c Context for decryption, or 0. + * @param check_hmac true to check the HMAC and give an error if it is not as expected. + */ +MonoMPEG2PictureFrame::MonoMPEG2PictureFrame(ASDCP::MPEG2::MXFReader* reader, int n, shared_ptr<DecryptionContext> context, bool check_hmac) +{ + /* XXX: unfortunate guesswork on this buffer size */ + _buffer = make_shared<ASDCP::MPEG2::FrameBuffer>(4 * Kumu::Megabyte); + + auto const r = reader->ReadFrame(n, *_buffer, context->context(), check_hmac ? context->hmac() : nullptr); + + if (ASDCP_FAILURE(r)) { + boost::throw_exception(ReadError(String::compose("could not read video frame %1 (%2)", n, static_cast<int>(r)))); + } +} + + +uint8_t const * +MonoMPEG2PictureFrame::data() const +{ + return _buffer->RoData(); +} + + +uint8_t * +MonoMPEG2PictureFrame::data() +{ + return _buffer->Data(); +} + + +int +MonoMPEG2PictureFrame::size() const +{ + return _buffer->Size(); +} + diff --git a/src/mono_mpeg2_picture_frame.h b/src/mono_mpeg2_picture_frame.h new file mode 100644 index 00000000..13853a4e --- /dev/null +++ b/src/mono_mpeg2_picture_frame.h @@ -0,0 +1,76 @@ +/* + Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#ifndef LIBDCP_MONO_MPEG2_PICTURE_FRAME_H +#define LIBDCP_MONO_MPEG2_PICTURE_FRAME_H + + +#include "asset_reader.h" +#include "data.h" + + +namespace dcp { + + +class MonoMPEG2PictureFrame : public Data +{ +public: + MonoMPEG2PictureFrame(MonoMPEG2PictureFrame const&) = delete; + MonoMPEG2PictureFrame& operator=(MonoMPEG2PictureFrame const&) = delete; + + /** @return Pointer to MPEG2 data */ + uint8_t const * data() const override; + + /** @return Pointer to MPEG2 data */ + uint8_t* data () override; + + /** @return Size of MPEG2 data in bytes */ + int size() const override; + +private: + /* XXX: this is a bit of a shame, but I tried friend MonoMPEG2PictureAssetReader and it's + rejected by some (seemingly older) GCCs. + */ + friend class AssetReader<ASDCP::MPEG2::MXFReader, MonoMPEG2PictureFrame>; + + MonoMPEG2PictureFrame(ASDCP::MPEG2::MXFReader* reader, int n, std::shared_ptr<DecryptionContext>, bool check_hmac); + + std::shared_ptr<ASDCP::MPEG2::FrameBuffer> _buffer; +}; + + +} + + +#endif diff --git a/src/mpeg2_picture_asset.cc b/src/mpeg2_picture_asset.cc new file mode 100644 index 00000000..6cd0b428 --- /dev/null +++ b/src/mpeg2_picture_asset.cc @@ -0,0 +1,74 @@ +/* + Copyright (C) 2023 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#include "mpeg2_picture_asset.h" + + +using std::string; +using namespace dcp; + + +MPEG2PictureAsset::MPEG2PictureAsset(boost::filesystem::path file) + : PictureAsset(file) +{ + +} + + +void +MPEG2PictureAsset::read_video_descriptor(ASDCP::MPEG2::VideoDescriptor const& descriptor) +{ + _size.width = descriptor.StoredWidth; + _size.height = descriptor.StoredHeight; + _edit_rate = Fraction(descriptor.EditRate.Numerator, descriptor.EditRate.Denominator); + _intrinsic_duration = descriptor.ContainerDuration; + _frame_rate = Fraction(descriptor.SampleRate.Numerator, descriptor.SampleRate.Denominator); + _screen_aspect_ratio = Fraction(descriptor.AspectRatio.Numerator, descriptor.AspectRatio.Denominator); +} + + +string +MPEG2PictureAsset::pkl_type (Standard standard) const +{ + DCP_ASSERT(standard == Standard::INTEROP); + return "application/x-smpte-mxf;asdcpKind=Picture"; +} + + +string +MPEG2PictureAsset::static_pkl_type(Standard standard) +{ + DCP_ASSERT(standard == Standard::INTEROP); + return "application/x-smpte-mxf;asdcpKind=Picture"; +} diff --git a/src/mpeg2_picture_asset.h b/src/mpeg2_picture_asset.h new file mode 100644 index 00000000..df882202 --- /dev/null +++ b/src/mpeg2_picture_asset.h @@ -0,0 +1,77 @@ +/* + Copyright (C) 2023 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#ifndef LIBDCP_MPEG2_PICTURE_ASSET_H +#define LIBDCP_MPEG2_PICTURE_ASSET_H + + +/** @file src/mpeg2_picture_asset.h + * @brief MPEG2PictureAsset class + */ + + +#include "picture_asset.h" +#include <boost/filesystem/path.hpp> + + +namespace ASDCP { + namespace MPEG2 { + struct VideoDescriptor; + } +} + + +namespace dcp { + + +class MPEG2PictureAsset : public PictureAsset +{ +public: + explicit MPEG2PictureAsset(boost::filesystem::path file); + + static std::string static_pkl_type(Standard standard); + +protected: + void read_video_descriptor(ASDCP::MPEG2::VideoDescriptor const& descriptor); + +private: + std::string pkl_type(Standard standard) const override; +}; + + +} + + +#endif + diff --git a/src/mpeg2_transcode.cc b/src/mpeg2_transcode.cc new file mode 100644 index 00000000..ab77f0e1 --- /dev/null +++ b/src/mpeg2_transcode.cc @@ -0,0 +1,143 @@ +/* + Copyright (C) 2023 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#include "compose.hpp" +#include "exceptions.h" +#include "mono_mpeg2_picture_frame.h" +#include "mpeg2_transcode.h" +extern "C" { +#include <libavcodec/avcodec.h> +} + + +using std::shared_ptr; +using std::vector; +using namespace dcp; + + +MPEG2Codec::~MPEG2Codec() +{ + avcodec_free_context(&_context); +} + + + +MPEG2Decompressor::MPEG2Decompressor() +{ + _codec = avcodec_find_decoder_by_name("mpeg2video"); + if (!_codec) { + throw MPEG2CodecError("could not find codec"); + } + + _context = avcodec_alloc_context3(_codec); + if (!_context) { + throw MPEG2CodecError("could not allocate codec context"); + } + + int const r = avcodec_open2(_context, _codec, nullptr); + if (r < 0) { + avcodec_free_context(&_context); + throw MPEG2CodecError("could not open codec"); + } + + _decompressed_frame = av_frame_alloc(); + if (!_decompressed_frame) { + throw std::bad_alloc(); + } +} + + +MPEG2Decompressor::~MPEG2Decompressor() +{ + av_frame_free(&_decompressed_frame); +} + + +vector<FFmpegImage> +MPEG2Decompressor::decompress_frame(shared_ptr<const MonoMPEG2PictureFrame> frame) +{ + /* XXX: can we avoid this? */ + auto copy = av_malloc(frame->size() + AV_INPUT_BUFFER_PADDING_SIZE); + if (!copy) { + throw std::bad_alloc(); + } + memcpy(copy, frame->data(), frame->size()); + + AVPacket packet; + av_init_packet(&packet); + av_packet_from_data(&packet, reinterpret_cast<uint8_t*>(copy), frame->size()); + + auto images = decompress_packet(&packet); + + av_packet_unref(&packet); + + return images; +} + + +vector<FFmpegImage> +MPEG2Decompressor::flush() +{ + return decompress_packet(nullptr); +} + + +vector<FFmpegImage> +MPEG2Decompressor::decompress_packet(AVPacket* packet) +{ + int const r = avcodec_send_packet(_context, packet); + if (r < 0) { + throw MPEG2DecompressionError(String::compose("avcodec_send_packet failed (%1)", r)); + } + + vector<FFmpegImage> images; + while (true) { + int const r = avcodec_receive_frame(_context, _decompressed_frame); + if (r == AVERROR(EAGAIN) || r == AVERROR_EOF) { + break; + } else if (r < 0) { + throw MPEG2DecompressionError("avcodec_receive_frame failed"); + } + + auto clone = av_frame_clone(_decompressed_frame); + if (!clone) { + throw std::bad_alloc(); + } + + images.push_back(FFmpegImage(clone)); + } + + return images; +} + diff --git a/src/mpeg2_transcode.h b/src/mpeg2_transcode.h new file mode 100644 index 00000000..ef5c8351 --- /dev/null +++ b/src/mpeg2_transcode.h @@ -0,0 +1,89 @@ +/* + Copyright (C) 2023 Carl Hetherington <cth@carlh.net> + + This file is part of libdcp. + + libdcp 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. + + libdcp 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 libdcp. If not, see <http://www.gnu.org/licenses/>. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. +*/ + + +#ifndef LIBDCP_MPEG2_TRANSCODE_H +#define LIBDCP_MPEG2_TRANSCODE_H + + +#include "ffmpeg_image.h" +#include <memory> + + +struct AVCodec; +struct AVCodecContext; +struct AVFrame; +struct AVPacket; + + +namespace dcp { + + +class MonoMPEG2PictureFrame; + + +class MPEG2Codec +{ +public: + MPEG2Codec() = default; + virtual ~MPEG2Codec(); + + MPEG2Codec(MPEG2Codec const&) = delete; + MPEG2Codec& operator=(MPEG2Codec const&) = delete; + +protected: + AVCodec const* _codec; + AVCodecContext* _context; +}; + + +class MPEG2Decompressor : public MPEG2Codec +{ +public: + MPEG2Decompressor(); + ~MPEG2Decompressor(); + + std::vector<FFmpegImage> decompress_frame(std::shared_ptr<const MonoMPEG2PictureFrame> frame); + std::vector<FFmpegImage> flush(); + +private: + std::vector<FFmpegImage> decompress_packet(AVPacket* packet); + + AVFrame* _decompressed_frame; +}; + + +} + + +#endif diff --git a/src/reel.cc b/src/reel.cc index fc47239d..acd7c7fc 100644 --- a/src/reel.cc +++ b/src/reel.cc @@ -327,7 +327,7 @@ Reel::give_kdm_to_assets (DecryptedKDM const & kdm) { for (auto const& i: kdm.keys()) { if (_main_picture && i.id() == _main_picture->key_id() && _main_picture->asset_ref().resolved()) { - _main_picture->asset()->set_key (i.key()); + _main_picture->j2k_asset()->set_key(i.key()); } if (_main_sound && i.id() == _main_sound->key_id() && _main_sound->asset_ref().resolved()) { _main_sound->asset()->set_key (i.key()); diff --git a/src/reel_mono_picture_asset.cc b/src/reel_mono_picture_asset.cc index 04c16d25..81eb4df9 100644 --- a/src/reel_mono_picture_asset.cc +++ b/src/reel_mono_picture_asset.cc @@ -47,7 +47,7 @@ using std::shared_ptr; using namespace dcp; -ReelMonoPictureAsset::ReelMonoPictureAsset (std::shared_ptr<MonoJ2KPictureAsset> asset, int64_t entry_point) +ReelMonoPictureAsset::ReelMonoPictureAsset(std::shared_ptr<PictureAsset> asset, int64_t entry_point) : ReelPictureAsset (asset, entry_point) { diff --git a/src/reel_mono_picture_asset.h b/src/reel_mono_picture_asset.h index 54429ad4..13c6545e 100644 --- a/src/reel_mono_picture_asset.h +++ b/src/reel_mono_picture_asset.h @@ -43,6 +43,7 @@ #include "reel_picture_asset.h" #include "mono_j2k_picture_asset.h" +#include "mono_mpeg2_picture_asset.h" namespace dcp { @@ -57,19 +58,29 @@ class MonoJ2KPictureAsset; class ReelMonoPictureAsset : public ReelPictureAsset { public: - ReelMonoPictureAsset (std::shared_ptr<MonoJ2KPictureAsset> asset, int64_t entry_point); + ReelMonoPictureAsset(std::shared_ptr<PictureAsset> asset, int64_t entry_point); explicit ReelMonoPictureAsset (std::shared_ptr<const cxml::Node>); - /** @return the MonoJ2KPictureAsset that this object refers to */ - std::shared_ptr<const MonoJ2KPictureAsset> mono_asset () const { + /** @return the MonoJ2KPictureAsset that this object refers to, if applicable */ + std::shared_ptr<const MonoJ2KPictureAsset> mono_j2k_asset() const { return asset_of_type<const MonoJ2KPictureAsset>(); } /** @return the MonoJ2KPictureAsset that this object refers to */ - std::shared_ptr<MonoJ2KPictureAsset> mono_asset () { + std::shared_ptr<MonoJ2KPictureAsset> mono_j2k_asset() { return asset_of_type<MonoJ2KPictureAsset>(); } + /** @return the MonoMPEG2PictureAsset that this object refers to, if applicable */ + std::shared_ptr<const MonoMPEG2PictureAsset> mono_mpeg2_asset() const { + return asset_of_type<const MonoMPEG2PictureAsset>(); + } + + /** @return the MonoMPEG2PictureAsset that this object refers to */ + std::shared_ptr<MonoMPEG2PictureAsset> mono_mpeg2_asset() { + return asset_of_type<MonoMPEG2PictureAsset>(); + } + private: std::string cpl_node_name (Standard standard) const override; }; diff --git a/src/reel_picture_asset.cc b/src/reel_picture_asset.cc index ca22b6fc..37a6bfcc 100644 --- a/src/reel_picture_asset.cc +++ b/src/reel_picture_asset.cc @@ -59,7 +59,7 @@ using boost::optional; using namespace dcp; -ReelPictureAsset::ReelPictureAsset (shared_ptr<J2KPictureAsset> asset, int64_t entry_point) +ReelPictureAsset::ReelPictureAsset(shared_ptr<PictureAsset> asset, int64_t entry_point) : ReelFileAsset (asset, asset->key_id(), asset->id(), asset->edit_rate(), asset->intrinsic_duration(), entry_point) , _frame_rate (asset->frame_rate ()) , _screen_aspect_ratio (asset->screen_aspect_ratio ()) diff --git a/src/reel_picture_asset.h b/src/reel_picture_asset.h index 6185f3dd..9f42a5b6 100644 --- a/src/reel_picture_asset.h +++ b/src/reel_picture_asset.h @@ -43,6 +43,7 @@ #include "reel_file_asset.h" #include "j2k_picture_asset.h" +#include "mpeg2_picture_asset.h" namespace dcp { @@ -54,20 +55,41 @@ namespace dcp { class ReelPictureAsset : public ReelFileAsset { public: - ReelPictureAsset (std::shared_ptr<J2KPictureAsset> asset, int64_t entry_point); + ReelPictureAsset(std::shared_ptr<PictureAsset> asset, int64_t entry_point); explicit ReelPictureAsset (std::shared_ptr<const cxml::Node>); - /** @return the J2KPictureAsset that this object refers to */ - std::shared_ptr<const J2KPictureAsset> asset () const { + /** @return the PictureAsset that this object refers to, if applicable */ + std::shared_ptr<const PictureAsset> asset() const { + return asset_of_type<const PictureAsset>(); + } + + /** @return the PictureAsset that this object refers to, if applicable */ + std::shared_ptr<PictureAsset> asset() { + return asset_of_type<PictureAsset>(); + } + + /** @return the J2KPictureAsset that this object refers to, if applicable */ + std::shared_ptr<const J2KPictureAsset> j2k_asset() const { return asset_of_type<const J2KPictureAsset>(); } - /** @return the J2KPictureAsset that this object refers to */ - std::shared_ptr<J2KPictureAsset> asset () { + /** @return the J2KPictureAsset that this object refers to, if applicable */ + std::shared_ptr<J2KPictureAsset> j2k_asset() { return asset_of_type<J2KPictureAsset>(); } + /** @return the MPEG2PictureAsset that this object refers to, if applicable */ + std::shared_ptr<const MPEG2PictureAsset> mpeg2_asset() const { + return asset_of_type<const MPEG2PictureAsset>(); + } + + /** @return the MPEG2PictureAsset that this object refers to, if applicable */ + std::shared_ptr<MPEG2PictureAsset> mpeg2_asset() { + return asset_of_type<MPEG2PictureAsset>(); + } + virtual xmlpp::Element* write_to_cpl(xmlpp::Element* node, Standard standard) const override; + bool equals(std::shared_ptr<const ReelPictureAsset>, EqualityOptions const&, NoteHandler) const; /** @return picture frame rate */ diff --git a/src/wscript b/src/wscript index 2ce8e556..47b716cf 100644 --- a/src/wscript +++ b/src/wscript @@ -77,6 +77,10 @@ def build(bld): mono_j2k_picture_asset.cc mono_j2k_picture_asset_writer.cc mono_j2k_picture_frame.cc + mono_mpeg2_picture_asset.cc + mono_mpeg2_picture_frame.cc + mpeg2_picture_asset.cc + mpeg2_transcode.cc mxf.cc name_format.cc object.cc @@ -171,6 +175,7 @@ def build(bld): identity_transfer_function.h interop_load_font_node.h interop_subtitle_asset.h + ffmpeg_image.h j2k_picture_asset.h j2k_picture_asset_writer.h j2k_transcode.h @@ -185,6 +190,11 @@ def build(bld): mono_j2k_picture_asset_reader.h mono_j2k_picture_asset_writer.h mono_j2k_picture_frame.h + mono_mpeg2_picture_asset.h + mono_mpeg2_picture_asset_reader.h + mono_mpeg2_picture_frame.h + mpeg2_picture_asset.h + mpeg2_transcode.h mxf.h name_format.h object.h @@ -250,7 +260,7 @@ def build(bld): obj.name = 'libdcp%s' % bld.env.API_VERSION obj.target = 'dcp%s' % bld.env.API_VERSION obj.export_includes = ['.'] - obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1 ASDCPLIB_DCPOMATIC XERCES' + obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1 ASDCPLIB_DCPOMATIC XERCES AVCODEC AVUTIL' obj.source = source # Library for gcov @@ -262,7 +272,7 @@ def build(bld): obj.name = 'libdcp%s_gcov' % bld.env.API_VERSION obj.target = 'dcp%s_gcov' % bld.env.API_VERSION obj.export_includes = ['.'] - obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1 ASDCPLIB_DCPOMATIC XERCES' + obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1 ASDCPLIB_DCPOMATIC XERCES AVCODEC AVUTIL' obj.use = 'libkumu-libdcp%s libasdcp-libdcp%s' % (bld.env.API_VERSION, bld.env.API_VERSION) obj.source = source obj.cppflags = ['-fprofile-arcs', '-ftest-coverage', '-fno-inline', '-fno-default-inline', '-fno-elide-constructors', '-g', '-O0'] |
