Support playback of MPEG2 DCPs.
authorCarl Hetherington <cth@carlh.net>
Wed, 29 Nov 2023 23:46:00 +0000 (00:46 +0100)
committerCarl Hetherington <cth@carlh.net>
Mon, 22 Apr 2024 11:03:04 +0000 (13:03 +0200)
cscript
src/lib/dcp_decoder.cc
src/lib/dcp_decoder.h
src/lib/dcp_examiner.cc

diff --git a/cscript b/cscript
index 2b1c57c953493f4a2ad3063d02a760a4ec07eab0..b971ff2ea5ef806148b2b335c727ae63f751e8b3 100644 (file)
--- a/cscript
+++ b/cscript
@@ -533,19 +533,7 @@ def make_spec(filename, version, target, options, requires=None):
     print('/usr/bin/gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :', file=f)
 
 def dependencies(target, options):
-
-    if target.platform == 'linux':
-        ffmpeg_options = { 'shared': False }
-    else:
-        ffmpeg_options = {}
-
-    if target.platform != 'linux' or target.distro != 'arch':
-        deps = [('ffmpeg', '0b73d2f5e70a04a67aa902902c42e3025ef3bb77', ffmpeg_options)]
-    else:
-        # Use distro-provided FFmpeg on Arch
-        deps = []
-
-    deps.append(('libdcp', '816365d20e0c6ef37b6bf499a42a0d3ecad22c05', {'c++17': target.platform == 'osx'}))
+    deps = [('libdcp', 'v1.9.6', {'c++17': target.platform == 'osx'})]
     deps.append(('libsub', 'v1.6.47'))
     deps.append(('leqm-nrt', '30dcaea1373ac62fba050e02ce5b0c1085797a23'))
     deps.append(('rtaudio', 'f619b76'))
index ce471202f9dad0ba7d85ae1924992af10c79bb71..e471a237fd7e0ab1c87866142c5e32c9ad3a0a85 100644 (file)
 #include "constants.h"
 #include "dcp_content.h"
 #include "dcp_decoder.h"
+#include "dcpomatic_log.h"
 #include "digester.h"
 #include "ffmpeg_image_proxy.h"
 #include "frame_interval_checker.h"
 #include "image.h"
 #include "j2k_image_proxy.h"
+#include "raw_image_proxy.h"
 #include "text_decoder.h"
 #include "util.h"
 #include "video_decoder.h"
@@ -40,6 +42,7 @@
 #include <dcp/mono_j2k_picture_asset.h>
 #include <dcp/mono_j2k_picture_asset_reader.h>
 #include <dcp/mono_j2k_picture_frame.h>
+#include <dcp/mono_mpeg2_picture_asset.h>
 #include <dcp/reel.h>
 #include <dcp/reel_atmos_asset.h>
 #include <dcp/reel_closed_caption_asset.h>
@@ -170,7 +173,7 @@ DCPDecoder::pass ()
        */
        pass_texts (_next, picture_asset->size());
 
-       if ((_j2k_mono_reader || _j2k_stereo_reader) && (_decode_referenced || !_dcp_content->reference_video())) {
+       if ((_j2k_mono_reader || _j2k_stereo_reader || _mpeg2_mono_reader) && (_decode_referenced || !_dcp_content->reference_video())) {
                auto const entry_point = (*_reel)->main_picture()->entry_point().get_value_or(0);
                if (_j2k_mono_reader) {
                        video->emit (
@@ -207,6 +210,23 @@ DCPDecoder::pass ()
                                        ),
                                ContentTime::from_frames(_offset + frame, vfr)
                                );
+               } else if (_mpeg2_mono_reader) {
+                       /* XXX: got to flush this at some point */
+                       try {
+                               for (auto const& image: _mpeg2_decompressor->decompress_frame(_mpeg2_mono_reader->get_frame(entry_point + frame))) {
+                                       video->emit(
+                                               film(),
+                                               /* XXX: should this be PADDED? */
+                                               std::make_shared<RawImageProxy>(std::make_shared<Image>(image.frame(), Image::Alignment::COMPACT)),
+                                               /* XXX: this will be wrong */
+                                               ContentTime::from_frames(_offset + frame, vfr)
+                                       );
+                               }
+                       } catch (dcp::MPEG2DecompressionError& e) {
+                               LOG_ERROR("Failed to decompress MPEG video frame %1 (%2)", entry_point + frame, e.what());
+                       } catch (dcp::ReadError& e) {
+                               LOG_ERROR("Failed to read MPEG2 video frame %1 (%2)", entry_point + frame, e.what());
+                       }
                }
        }
 
@@ -370,8 +390,10 @@ DCPDecoder::get_readers ()
 {
        _j2k_mono_reader.reset();
        _j2k_stereo_reader.reset();
+       _mpeg2_mono_reader.reset();
        _sound_reader.reset();
        _atmos_reader.reset();
+       _mpeg2_decompressor.reset();
        _atmos_metadata = boost::none;
 
        if (_reel == _reels.end() || !_dcp_content->can_be_played ()) {
@@ -382,13 +404,18 @@ DCPDecoder::get_readers ()
                auto asset = (*_reel)->main_picture()->asset ();
                auto j2k_mono = dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>(asset);
                auto j2k_stereo = dynamic_pointer_cast<dcp::StereoJ2KPictureAsset>(asset);
-               DCPOMATIC_ASSERT(j2k_mono || j2k_stereo)
+               auto mpeg2_mono = dynamic_pointer_cast<dcp::MonoMPEG2PictureAsset>(asset);
+               DCPOMATIC_ASSERT(j2k_mono || j2k_stereo || mpeg2_mono)
                if (j2k_mono) {
                        _j2k_mono_reader = j2k_mono->start_read();
                        _j2k_mono_reader->set_check_hmac(false);
                } else if (j2k_stereo) {
                        _j2k_stereo_reader = j2k_stereo->start_read();
                        _j2k_stereo_reader->set_check_hmac(false);
+               } else {
+                       _mpeg2_mono_reader = mpeg2_mono->start_read();
+                       _mpeg2_mono_reader->set_check_hmac(false);
+                       _mpeg2_decompressor = std::make_shared<dcp::MPEG2Decompressor>();
                }
        }
 
index 0143ef881cf7f43d5a06b6ad927f6ce03840cef3..ee0f306946a7f49055bac3e4b4e3cd6683ee631b 100644 (file)
@@ -29,6 +29,8 @@
 #include "font_id_allocator.h"
 #include <dcp/mono_j2k_picture_asset_reader.h>
 #include <dcp/stereo_j2k_picture_asset_reader.h>
+#include <dcp/mono_mpeg2_picture_asset_reader.h>
+#include <dcp/mpeg2_transcode.h>
 #include <dcp/sound_asset_reader.h>
 #include <dcp/subtitle_asset.h>
 
@@ -98,11 +100,15 @@ private:
        std::shared_ptr<dcp::MonoJ2KPictureAssetReader> _j2k_mono_reader;
        /** Reader for current J2K stereo picture asset, if applicable */
        std::shared_ptr<dcp::StereoJ2KPictureAssetReader> _j2k_stereo_reader;
+       /** Reader for current MPEG2 mono picture asset, if applicable */
+       std::shared_ptr<dcp::MonoMPEG2PictureAssetReader> _mpeg2_mono_reader;
        /** Reader for current sound asset, if applicable */
        std::shared_ptr<dcp::SoundAssetReader> _sound_reader;
        std::shared_ptr<dcp::AtmosAssetReader> _atmos_reader;
        boost::optional<AtmosMetadata> _atmos_metadata;
 
+       std::shared_ptr<dcp::MPEG2Decompressor> _mpeg2_decompressor;
+
        bool _decode_referenced = false;
        boost::optional<int> _forced_reduction;
 
index 584ba4116315d0398ee52f7929729a348080a9e9..92491947cf67665da99576da0e1fe128464194a8 100644 (file)
@@ -35,6 +35,8 @@
 #include <dcp/mono_j2k_picture_asset.h>
 #include <dcp/mono_j2k_picture_asset_reader.h>
 #include <dcp/mono_j2k_picture_frame.h>
+#include <dcp/mono_mpeg2_picture_asset.h>
+#include <dcp/mpeg2_transcode.h>
 #include <dcp/reel.h>
 #include <dcp/reel_atmos_asset.h>
 #include <dcp/reel_closed_caption_asset.h>
@@ -295,6 +297,7 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content, bool tolerant)
                                }
                                auto j2k_mono = dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>(pic);
                                auto j2k_stereo = dynamic_pointer_cast<dcp::StereoJ2KPictureAsset>(pic);
+                               auto mpeg2_mono = dynamic_pointer_cast<dcp::MonoMPEG2PictureAsset>(pic);
 
                                if (j2k_mono) {
                                        auto reader = j2k_mono->start_read();
@@ -304,6 +307,11 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content, bool tolerant)
                                        auto reader = j2k_stereo->start_read();
                                        reader->set_check_hmac (false);
                                        reader->get_frame(0)->xyz_image(dcp::Eye::LEFT);
+                               } else if (mpeg2_mono) {
+                                       auto reader = mpeg2_mono->start_read();
+                                       reader->set_check_hmac(false);
+                                       dcp::MPEG2Decompressor decompressor;
+                                       decompressor.decompress_frame(reader->get_frame(0));
                                }
                        }