Support MPEG2 decompression.
authorCarl Hetherington <cth@carlh.net>
Tue, 14 Nov 2023 21:01:07 +0000 (22:01 +0100)
committerCarl Hetherington <cth@carlh.net>
Sun, 21 Apr 2024 23:42:00 +0000 (01:42 +0200)
30 files changed:
benchmark/wscript
cscript
examples/wscript
libdcp-1.0.pc.in
src/asset_factory.cc
src/asset_reader.h
src/dcp.cc
src/exceptions.h
src/ffmpeg_image.h [new file with mode: 0644]
src/mono_mpeg2_picture_asset.cc [new file with mode: 0644]
src/mono_mpeg2_picture_asset.h [new file with mode: 0644]
src/mono_mpeg2_picture_asset_reader.h [new file with mode: 0644]
src/mono_mpeg2_picture_frame.cc [new file with mode: 0644]
src/mono_mpeg2_picture_frame.h [new file with mode: 0644]
src/mpeg2_picture_asset.cc [new file with mode: 0644]
src/mpeg2_picture_asset.h [new file with mode: 0644]
src/mpeg2_transcode.cc [new file with mode: 0644]
src/mpeg2_transcode.h [new file with mode: 0644]
src/reel.cc
src/reel_mono_picture_asset.cc
src/reel_mono_picture_asset.h
src/reel_picture_asset.cc
src/reel_picture_asset.h
src/wscript
test/dcp_test.cc
test/kdm_test.cc
test/mono_mpeg2_picture_read_test.cc [new file with mode: 0644]
test/wscript
tools/wscript
wscript

index f3e2d6603bbbf93ebff6cc8115c86a361bd9ee35..7305bdaa3b0d461f89cc0842dba99dddeb1ab0b7 100644 (file)
@@ -35,7 +35,7 @@ def build(bld):
     for p in ['rgb_to_xyz', 'j2k_transcode']:
         obj = bld(features='cxx cxxprogram')
         obj.name = p
-        obj.uselib = 'BOOST_FILESYSTEM ASDCPLIB_DCPOMATIC CXML'
+        obj.uselib = 'BOOST_FILESYSTEM ASDCPLIB_DCPOMATIC CXML AVCODEC AVUTIL'
         obj.cppflags = ['-g', '-O2']
         obj.use = 'libdcp%s' % bld.env.API_VERSION
         obj.source = "%s.cc" % p
diff --git a/cscript b/cscript
index f62065e8ff3b9c57556562777bd2e5176b59e82c..d9ee7cf7c2e69e24d0d8e74c56439b05938486cd 100644 (file)
--- a/cscript
+++ b/cscript
@@ -35,11 +35,22 @@ import os
 import shutil
 
 def dependencies(target, options):
-    return (
+    deps = [
         ('libcxml', 'v0.17.9', options),
         ('openjpeg', 'ad8edaacd54a862940d0a77c41ecda5858b54d6e'),
         ('asdcplib', '4b5d6e8d27dfd5fb282590538068662f4dbbf1c9')
-    )
+    ]
+
+    if target.platform == 'linux':
+        ffmpeg_options = { 'shared': False }
+    else:
+        ffmpeg_options = {}
+
+    if target.platform != 'linux' or target.distro != 'arch':
+        # Use distro-provided FFmpeg on Arch, otherwise our own
+        deps.append(('ffmpeg', '0b73d2f5e70a04a67aa902902c42e3025ef3bb77', ffmpeg_options))
+
+    return deps
 
 def build(target, options):
     cmd = './waf configure --disable-examples --disable-dumpimage --disable-benchmarks --prefix=%s' % target.directory
index faaa3e9ab91240dc48a6466150cd5bda7aa78d23..68cd79ee7af1b8034ec40a8b5192067194abbb44 100644 (file)
@@ -21,7 +21,7 @@ def build(bld):
         obj = bld(features='cxx cxxprogram')
         obj.name   = example
         obj.use    = 'libdcp%s' % bld.env.API_VERSION
-        obj.uselib = 'OPENJPEG CXML OPENMP ASDCPLIB_DCPOMATIC BOOST_FILESYSTEM OPENSSL XMLSEC1 MAGICK'
+        obj.uselib = 'OPENJPEG CXML OPENMP ASDCPLIB_DCPOMATIC BOOST_FILESYSTEM OPENSSL XMLSEC1 MAGICK AVCODEC AVUTIL'
         obj.source = example + '.cc'
         obj.target = example
         obj.install_path = ''
index b838487ed1542e1c4273f8d0082f45de95a7a771..a8d1007f1483ef17bc96caecd557b22e8fcb6379 100644 (file)
@@ -5,6 +5,6 @@ includedir=@includedir@
 Name: libdcp
 Description: DCP reading and writing library
 Version: @version@
-Requires: openssl libxml++-@xmlpp_api@ xmlsec1 libasdcp-dcpomatic xerces-c
+Requires: openssl libxml++-@xmlpp_api@ xmlsec1 libasdcp-dcpomatic xerces-c libavutil libavcodec
 Libs: @libs@
 Cflags: -I${includedir}
index 240b768572c299582a679e760980e28d19d4de27..26811366657d780bd28b465ff623fa8e69a7ccb2 100644 (file)
@@ -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);
index 3bf9b8b9f858616b3ecabf71c41c84992abb514c..091ac915a97cc574dd0db7a928985e8231a6d8dd 100644 (file)
@@ -91,6 +91,7 @@ protected:
 private:
        friend class AtmosAsset;
        friend class MonoJ2KPictureAsset;
+       friend class MonoMPEG2PictureAsset;
        friend class SoundAsset;
        friend class StereoJ2KPictureAsset;
 
index 816fc178b64c6536d75a760d0a76fd3848f6f6cc..f0379202b5a53be4051df53f9d4e57a38f2b153b 100644 (file)
@@ -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))
index 8d85f02a02557b580ef0500800cc4550c8db8fd6..88ec253c36595e9fa0e65553f208031ef14863a5 100644 (file)
@@ -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 (file)
index 0000000..56b6857
--- /dev/null
@@ -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 (file)
index 0000000..548e936
--- /dev/null
@@ -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 (file)
index 0000000..a740cc6
--- /dev/null
@@ -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 (file)
index 0000000..75155f4
--- /dev/null
@@ -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 (file)
index 0000000..3da6721
--- /dev/null
@@ -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 (file)
index 0000000..13853a4
--- /dev/null
@@ -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 (file)
index 0000000..6cd0b42
--- /dev/null
@@ -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 (file)
index 0000000..df88220
--- /dev/null
@@ -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 (file)
index 0000000..ab77f0e
--- /dev/null
@@ -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 (file)
index 0000000..ef5c835
--- /dev/null
@@ -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
index fc47239d4aec2015d5e93f215c26e0947c600ccf..acd7c7fc3334338f16d3a122c00cc98274a12cda 100644 (file)
@@ -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());
index 04c16d256a757891badffb7b3904cbedc39f7ea7..81eb4df976dfaed170725efd22ec995845ac9902 100644 (file)
@@ -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)
 {
 
index 54429ad4bd4ad49beaf8706c75d174739f06189a..13c6545e82707416dbc3cc83b4a181d4447ed07a 100644 (file)
@@ -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;
 };
index ca22b6fcf73c49940cc87ad14aa5f3487f03794a..37a6bfcc56721109d1596f1e2e52e2745e683084 100644 (file)
@@ -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 ())
index 6185f3dd076f76ebae88668b9b4b7fc20b57808b..9f42a5b6bc18615fcac8ed70e0cd5d39d417995a 100644 (file)
@@ -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 */
index 2ce8e556a4d8b4261500e7fd1ace977a9f1b9e81..47b716cfffe7529f1457f29888a37fdca1920890 100644 (file)
@@ -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']
index 4647d8ae9b755ff8ff45ba6ad96373f76faf350b..63de990f776ecbbc2ef9ff86b3efa11876ff3e94 100644 (file)
@@ -203,9 +203,9 @@ test_rewriting_sound(string name, bool modify)
        dcp::DCP B ("build/test/" + name);
        auto reel = make_shared<dcp::Reel>();
 
-       BOOST_REQUIRE (A_picture->mono_asset());
-       BOOST_REQUIRE (A_picture->mono_asset()->file());
-       copy_file (A_picture->mono_asset()->file().get(), path("build") / "test" / name / picture);
+       BOOST_REQUIRE(A_picture->mono_j2k_asset());
+       BOOST_REQUIRE(A_picture->mono_j2k_asset()->file());
+       copy_file(A_picture->mono_j2k_asset()->file().get(), path("build") / "test" / name / picture);
        reel->add(make_shared<dcp::ReelMonoPictureAsset>(make_shared<dcp::MonoJ2KPictureAsset>(path("build") / "test" / name / picture), 0));
 
        auto reader = A_sound->asset()->start_read();
index 4b261d4850f97a4adc44ee5814c215035d2321ac..75a6019e56ac29567703175d63f9ffd697a410f2 100644 (file)
@@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE (vf_kdm_test)
        ov->write_xml ();
 
        auto ov_reel = ov->cpls()[0]->reels()[0];
-       auto ov_reel_picture = make_shared<dcp::ReelMonoPictureAsset>(dynamic_pointer_cast<dcp::ReelMonoPictureAsset>(ov_reel->main_picture())->mono_asset(), 0);
+       auto ov_reel_picture = make_shared<dcp::ReelMonoPictureAsset>(dynamic_pointer_cast<dcp::ReelMonoPictureAsset>(ov_reel->main_picture())->mono_j2k_asset(), 0);
        auto ov_reel_sound = make_shared<dcp::ReelSoundAsset>(ov_reel->main_sound()->asset(), 0);
 
        /* Make VF */
diff --git a/test/mono_mpeg2_picture_read_test.cc b/test/mono_mpeg2_picture_read_test.cc
new file mode 100644 (file)
index 0000000..f0d4aac
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+    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 "mono_mpeg2_picture_asset.h"
+#include "mpeg2_transcode.h"
+#include "test.h"
+#include <boost/test/unit_test.hpp>
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+}
+
+
+BOOST_AUTO_TEST_CASE(mpeg_mono_picture_read_test)
+{
+       dcp::MonoMPEG2PictureAsset asset(private_test / "data" / "mas" / "r2.mxf" );
+       std::cout << "frame rate " << asset.frame_rate().numerator << "\n";
+       std::cout << "duration " << asset.intrinsic_duration() << "\n";
+
+       auto reader = asset.start_read();
+
+       dcp::MPEG2Decompressor decompressor;
+       for (auto i = 0; i < asset.intrinsic_duration(); ++i) {
+               auto images = decompressor.decompress_frame(reader->get_frame(i));
+               BOOST_CHECK_EQUAL(images.size(), i == 0 ? 0U : 1U);
+       }
+
+       auto images = decompressor.flush();
+       BOOST_CHECK_EQUAL(images.size(), 1U);
+}
+
index 92c835981e241bca21b656a161809ad47966327a..ee5b754b6998fbda500167bad218706c5f37e7fd 100644 (file)
@@ -53,7 +53,7 @@ def configure(conf):
 def build(bld):
     obj = bld(features='cxx cxxprogram')
     obj.name   = 'tests'
-    obj.uselib = 'BOOST_TEST BOOST_FILESYSTEM BOOST_DATETIME OPENJPEG CXML XMLSEC1 SNDFILE OPENMP ASDCPLIB_DCPOMATIC LIBXML++ OPENSSL XERCES DL'
+    obj.uselib = 'BOOST_TEST BOOST_FILESYSTEM BOOST_DATETIME OPENJPEG CXML XMLSEC1 SNDFILE OPENMP ASDCPLIB_DCPOMATIC LIBXML++ OPENSSL XERCES DL AVCODEC AVUTIL'
     obj.cppflags = ['-fno-inline', '-fno-elide-constructors', '-g', '-O0']
     if bld.env['CXX_NAME'] == 'gcc':
         obj.cppflags.append('-fno-default-inline')
@@ -94,6 +94,7 @@ def build(bld):
                  make_digest_test.cc
                  markers_test.cc
                  mca_test.cc
+                 mono_mpeg2_picture_read_test.cc
                  kdm_test.cc
                  key_test.cc
                  language_tag_test.cc
@@ -124,7 +125,7 @@ def build(bld):
 
     obj = bld(features='cxx cxxprogram')
     obj.name   = 'subs_in_out'
-    obj.uselib = 'BOOST_TEST BOOST_FILESYSTEM OPENJPEG CXML OPENMP ASDCPLIB_DCPOMATIC XMLSEC1 OPENSSL DL LIBXML++'
+    obj.uselib = 'BOOST_TEST BOOST_FILESYSTEM OPENJPEG CXML OPENMP ASDCPLIB_DCPOMATIC XMLSEC1 OPENSSL DL LIBXML++ AVCODEC AVUTIL'
     obj.cppflags = ['-fno-inline', '-fno-elide-constructors', '-g', '-O0']
     if bld.env['CXX_NAME'] == 'gcc':
         obj.cppflags.append('-fno-default-inline')
@@ -141,7 +142,7 @@ def build(bld):
 
     obj = bld(features='cxx cxxprogram')
     obj.name   = 'rewrite_subs'
-    obj.uselib = 'BOOST_TEST BOOST_FILESYSTEM OPENJPEG CXML OPENMP ASDCPLIB_DCPOMATIC XMLSEC1 OPENSSL DL LIBXML++'
+    obj.uselib = 'BOOST_TEST BOOST_FILESYSTEM OPENJPEG CXML OPENMP ASDCPLIB_DCPOMATIC XMLSEC1 OPENSSL DL LIBXML++ AVCODEC AVUTIL'
     obj.cppflags = ['-fno-inline', '-fno-elide-constructors', '-g', '-O0']
     if bld.env['CXX_NAME'] == 'gcc':
         obj.cppflags.append('-fno-default-inline')
index 53b74e7074d98cf164668d94286e371fb2c8cb54..d282c88ccb8a373078c80e030011d874d915c700 100644 (file)
@@ -32,7 +32,7 @@
 #
 
 def build(bld):
-    uselib = 'OPENJPEG CXML OPENMP ASDCPLIB_DCPOMATIC BOOST_FILESYSTEM LIBXML++ XMLSEC1 OPENSSL XERCES DL MAGICK'
+    uselib = 'OPENJPEG CXML OPENMP ASDCPLIB_DCPOMATIC BOOST_FILESYSTEM LIBXML++ XMLSEC1 OPENSSL XERCES DL MAGICK AVCODEC AVUTIL'
 
     for f in ['diff', 'info', 'verify']:
         obj = bld(features='cxx cxxprogram')
diff --git a/wscript b/wscript
index d48e7135a48a1bda29c132c7e19d80b222edc5bb..89026bcda491aa6f6d07a3216d42da1e783f8769 100644 (file)
--- a/wscript
+++ b/wscript
@@ -248,6 +248,9 @@ def configure(conf):
                    lib=['boost_date_time%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix],
                    uselib_store='BOOST_DATETIME')
 
+    conf.check_cfg(package='libavcodec', args='--cflags --libs', uselib_store='AVCODEC', mandatory=True)
+    conf.check_cfg(package='libavutil', args='--cflags --libs', uselib_store='AVUTIL', mandatory=True)
+
     if not conf.env.DISABLE_TESTS:
         conf.recurse('test')
         if conf.options.enable_gcov: