deps = [
('libcxml', 'v0.17.9', options),
('openjpeg', 'ad8edaacd54a862940d0a77c41ecda5858b54d6e'),
- ('asdcplib', '4b5d6e8d27dfd5fb282590538068662f4dbbf1c9')
+ ('asdcplib', 'v1.0.1')
]
if target.platform == 'linux':
};
+class MPEG2CompressionError : public MiscError
+{
+public:
+ explicit MPEG2CompressionError(std::string message)
+ : MiscError(message)
+ {}
+};
+
+
class BadContentKindError : public ReadError
{
public:
--- /dev/null
+/*
+ Copyright (C) 2024 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 "ffmpeg_image.h"
+#include "types.h"
+extern "C" {
+#include <libavutil/pixfmt.h>
+}
+
+
+using namespace dcp;
+
+
+FFmpegImage::FFmpegImage(int64_t pts)
+{
+ auto const width = size().width;
+ auto const height = size().height;
+
+ _frame = av_frame_alloc();
+ if (!_frame) {
+ throw std::bad_alloc();
+ }
+
+ _frame->buf[0] = av_buffer_alloc(width * height);
+ _frame->buf[1] = av_buffer_alloc(width * height / 4);
+ _frame->buf[2] = av_buffer_alloc(width * height / 4);
+
+ _frame->linesize[0] = width;
+ _frame->linesize[1] = width / 2;
+ _frame->linesize[2] = width / 2;
+
+ for (auto i = 0; i < 3; ++i) {
+ _frame->data[i] = _frame->buf[i]->data;
+ }
+
+ _frame->width = width;
+ _frame->height = height;
+ _frame->format = AV_PIX_FMT_YUV420P;
+ _frame->pts = pts;
+}
+
+
+void
+FFmpegImage::set_pts(int64_t pts)
+{
+ _frame->pts = pts;
+}
+
+
+uint8_t*
+FFmpegImage::y()
+{
+ return _frame->data[0];
+}
+
+
+int
+FFmpegImage::y_stride() const
+{
+ return _frame->linesize[0];
+}
+
+
+uint8_t*
+FFmpegImage::u()
+{
+ return _frame->data[1];
+}
+
+
+int
+FFmpegImage::u_stride() const
+{
+ return _frame->linesize[1];
+}
+
+
+uint8_t*
+FFmpegImage::v()
+{
+ return _frame->data[2];
+}
+
+
+int
+FFmpegImage::v_stride() const
+{
+ return _frame->linesize[2];
+}
+
+
#define LIBDCP_FFMPEG_IMAGE_H
+#include "types.h"
extern "C" {
#include <libavutil/frame.h>
}
class FFmpegImage
{
public:
+ explicit FFmpegImage(int64_t pts);
+
explicit FFmpegImage(AVFrame* frame)
: _frame(frame)
{}
return _frame;
}
+ uint8_t* y();
+ int y_stride() const;
+
+ uint8_t* u();
+ int u_stride() const;
+
+ uint8_t* v();
+ int v_stride() const;
+
+ Size size() const {
+ return { 1920, 1080 };
+ }
+
+ void set_pts(int64_t pts);
+
private:
AVFrame* _frame = nullptr;
};
#define LIBDCP_FRAME_INFO_H
+#include "warnings.h"
+LIBDCP_DISABLE_WARNINGS
+#include <asdcp/AS_DCP.h>
+LIBDCP_ENABLE_WARNINGS
#include <stdint.h>
#include <string>
};
+struct MPEG2FrameInfo : public FrameInfo
+{
+ MPEG2FrameInfo() = default;
+
+ MPEG2FrameInfo(
+ uint64_t offset_,
+ uint64_t size_,
+ std::string hash_,
+ ASDCP::MPEG2::FrameType_t type_,
+ bool gop_start_,
+ bool closed_gop_,
+ uint8_t temporal_offset_
+ )
+ : FrameInfo(offset_, size_, hash_)
+ , type(type_)
+ , gop_start(gop_start_)
+ , closed_gop(closed_gop_)
+ , temporal_offset(temporal_offset_)
+ {}
+
+ ASDCP::MPEG2::FrameType_t type;
+ bool gop_start;
+ bool closed_gop;
+ uint8_t temporal_offset;
+};
+
+
}
namespace dcp {
-struct ASDCPStateBase
+struct ASDCPJ2KStateBase
{
- ASDCPStateBase ()
+ ASDCPJ2KStateBase()
: frame_buffer (4 * Kumu::Megabyte)
{}
using namespace dcp;
-struct MonoJ2KPictureAssetWriter::ASDCPState : public ASDCPStateBase
+struct MonoJ2KPictureAssetWriter::ASDCPState : public ASDCPJ2KStateBase
{
ASDCP::JP2K::MXFWriter mxf_writer;
};
#include "filesystem.h"
#include "mono_mpeg2_picture_asset.h"
#include "mono_mpeg2_picture_asset_reader.h"
+#include "mono_mpeg2_picture_asset_writer.h"
#include <asdcp/AS_DCP.h>
return shared_ptr<MonoMPEG2PictureAssetReader>(new MonoMPEG2PictureAssetReader(this, key(), standard()));
}
+
+
+shared_ptr<MPEG2PictureAssetWriter>
+MonoMPEG2PictureAsset::start_write(boost::filesystem::path file, Behaviour behaviour)
+{
+ /* Can't use make_shared here as the MonoJ2KPictureAssetWriter constructor is private */
+ return shared_ptr<MonoMPEG2PictureAssetWriter>(new MonoMPEG2PictureAssetWriter(this, file, behaviour == Behaviour::OVERWRITE_EXISTING));
+}
*/
+#include "behaviour.h"
#include "mpeg2_picture_asset.h"
#include "mono_mpeg2_picture_asset_reader.h"
namespace dcp {
+class MonoMPEG2PictureAssetWriter;
+
+
class MonoMPEG2PictureAsset : public MPEG2PictureAsset
{
public:
+ MonoMPEG2PictureAsset(Fraction edit_rate)
+ : MPEG2PictureAsset(edit_rate)
+ {}
+
explicit MonoMPEG2PictureAsset(boost::filesystem::path file);
+ std::shared_ptr<MPEG2PictureAssetWriter> start_write(boost::filesystem::path file, Behaviour behaviour) override;
std::shared_ptr<MonoMPEG2PictureAssetReader> start_read() const;
};
--- /dev/null
+/*
+ Copyright (C) 2024 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_writer.h"
+#include "mpeg2_picture_asset.h"
+#include "mpeg2_picture_asset_writer.h"
+
+
+using std::string;
+using namespace dcp;
+
+
+struct MonoMPEG2PictureAssetWriter::ASDCPState : public ASDCPMPEG2StateBase
+{
+ ASDCP::MPEG2::MXFWriter mxf_writer;
+};
+
+
+
+
+MonoMPEG2PictureAssetWriter::MonoMPEG2PictureAssetWriter(MPEG2PictureAsset* asset, boost::filesystem::path file, bool overwrite)
+ : MPEG2PictureAssetWriter(asset, file, overwrite)
+ , _state(new MonoMPEG2PictureAssetWriter::ASDCPState)
+{
+ asset->set_file(file);
+}
+
+
+MonoMPEG2PictureAssetWriter::~MonoMPEG2PictureAssetWriter()
+{
+ try {
+ /* Last-resort finalization to close the file, at least */
+ if (!_finalized) {
+ _state->mxf_writer.Finalize();
+ }
+ } catch (...) {}
+}
+
+
+void
+MonoMPEG2PictureAssetWriter::start(uint8_t const * data, int size)
+{
+ dcp::start(this, _state, _picture_asset, data, size);
+ _picture_asset->set_frame_rate (_picture_asset->edit_rate());
+}
+
+
+MPEG2FrameInfo
+MonoMPEG2PictureAssetWriter::write(uint8_t const * data, int size)
+{
+ DCP_ASSERT(!_finalized);
+
+ if (!_started) {
+ start(data, size);
+ }
+
+ ASDCP::MPEG2::FrameBuffer buffer;
+ buffer.SetData(const_cast<uint8_t*>(data), size);
+ buffer.Size(size);
+ buffer.PlaintextOffset(0);
+
+ auto const before_offset = _state->mxf_writer.Tell();
+
+ string hash;
+ auto const r = _state->mxf_writer.WriteFrame(buffer, _crypto_context->context(), _crypto_context->hmac(), &hash);
+ if (ASDCP_FAILURE(r)) {
+ boost::throw_exception(MXFFileError("error in writing video MXF", _file.string(), r));
+ }
+
+ ++_frames_written;
+ return MPEG2FrameInfo(
+ before_offset,
+ _state->mxf_writer.Tell() - before_offset,
+ hash,
+ buffer.FrameType(),
+ buffer.GOPStart(),
+ buffer.ClosedGOP(),
+ buffer.TemporalOffset()
+ );
+}
+
+
+void
+MonoMPEG2PictureAssetWriter::fake_write(MPEG2FrameInfo const& info)
+{
+ DCP_ASSERT(_started);
+ DCP_ASSERT(!_finalized);
+
+ DCP_ASSERT(false);
+
+ auto r = _state->mxf_writer.FakeWriteFrame(info.size, info.type, info.gop_start, info.closed_gop, info.temporal_offset);
+ if (ASDCP_FAILURE(r)) {
+ boost::throw_exception(MXFFileError("error in writing video MXF", _file.string(), r));
+ }
+
+ ++_frames_written;
+}
+
+
+bool
+MonoMPEG2PictureAssetWriter::finalize()
+{
+ if (_started) {
+ auto r = _state->mxf_writer.Finalize();
+ if (ASDCP_FAILURE(r)) {
+ boost::throw_exception(MXFFileError("error in finalizing video MXF", _file.string(), r));
+ }
+ }
+
+ _picture_asset->_intrinsic_duration = _frames_written;
+ return MPEG2PictureAssetWriter::finalize();
+}
+
--- /dev/null
+/*
+ Copyright (C) 2024 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_writer.h"
+
+
+#include "mpeg2_picture_asset_writer_common.cc"
+
+
+namespace dcp {
+
+
+class MonoMPEG2PictureAsset;
+
+
+class MonoMPEG2PictureAssetWriter : public MPEG2PictureAssetWriter
+{
+public:
+ ~MonoMPEG2PictureAssetWriter();
+
+ MPEG2FrameInfo write(uint8_t const *, int) override;
+ void fake_write(MPEG2FrameInfo const& info) override;
+ bool finalize() override;
+
+private:
+ friend class MonoMPEG2PictureAsset;
+
+ MonoMPEG2PictureAssetWriter(MPEG2PictureAsset* asset, boost::filesystem::path file, bool overwrite);
+
+ void start(uint8_t const *, int);
+
+ struct ASDCPState;
+ std::shared_ptr<ASDCPState> _state;
+};
+
+
+}
+MonoMPEG2PictureFrame::MonoMPEG2PictureFrame(uint8_t const* data, int size)
+{
+ _buffer = make_shared<ASDCP::MPEG2::FrameBuffer>(size);
+ memcpy(_buffer->Data(), data, size);
+ _buffer->Size(size);
+}
+
+
/** 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.
class MonoMPEG2PictureFrame : public Data
{
public:
+ MonoMPEG2PictureFrame(uint8_t const * data, int size);
+
MonoMPEG2PictureFrame(MonoMPEG2PictureFrame const&) = delete;
MonoMPEG2PictureFrame& operator=(MonoMPEG2PictureFrame const&) = delete;
+ /* XXX: couldn't we just return the frame buffer */
+
/** @return Pointer to MPEG2 data */
uint8_t const * data() const override;
MonoMPEG2PictureFrame(ASDCP::MPEG2::MXFReader* reader, int n, std::shared_ptr<DecryptionContext>, bool check_hmac);
+ /* XXX why is this a shared_ptr? */
std::shared_ptr<ASDCP::MPEG2::FrameBuffer> _buffer;
};
*/
+#include "behaviour.h"
+#include "mpeg2_picture_asset_writer.h"
#include "picture_asset.h"
#include <boost/filesystem/path.hpp>
class MPEG2PictureAsset : public PictureAsset
{
public:
+ MPEG2PictureAsset(Fraction edit_rate)
+ : PictureAsset(edit_rate, Standard::INTEROP)
+ {}
+
explicit MPEG2PictureAsset(boost::filesystem::path file);
+ virtual std::shared_ptr<MPEG2PictureAssetWriter> start_write(
+ boost::filesystem::path file,
+ Behaviour behaviour
+ ) = 0;
+
static std::string static_pkl_type(Standard standard);
protected:
+ friend class MonoMPEG2PictureAssetWriter;
+
void read_video_descriptor(ASDCP::MPEG2::VideoDescriptor const& descriptor);
private:
--- /dev/null
+/*
+ Copyright (C) 2024 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"
+#include "mpeg2_picture_asset_writer.h"
+
+
+using namespace dcp;
+
+
+MPEG2PictureAssetWriter::MPEG2PictureAssetWriter(MPEG2PictureAsset* asset, boost::filesystem::path file, bool overwrite)
+ : AssetWriter(asset, file)
+ , _picture_asset(asset)
+ , _overwrite(overwrite)
+{
+ asset->set_file(file);
+}
--- /dev/null
+/*
+ Copyright (C) 2024 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_WRITER_H
+#define LIBDCP_MPEG2_PICTURE_ASSET_WRITER_H
+
+
+#include "asset_writer.h"
+#include "frame_info.h"
+
+
+namespace dcp {
+
+
+class Data;
+class MPEG2PictureAsset;
+
+
+class MPEG2PictureAssetWriter : public AssetWriter
+{
+public:
+ virtual MPEG2FrameInfo write(uint8_t const* data, int size) = 0;
+ virtual void fake_write(MPEG2FrameInfo const& info) = 0;
+
+ MPEG2FrameInfo write(Data const& data);
+
+protected:
+ template <class P, class Q>
+ friend void start(MPEG2PictureAssetWriter *, std::shared_ptr<P>, Q *, uint8_t const *, int);
+
+ MPEG2PictureAssetWriter(MPEG2PictureAsset* asset, boost::filesystem::path file, bool overwrite);
+
+ MPEG2PictureAsset* _picture_asset = nullptr;
+ bool _overwrite = false;
+};
+
+
+}
+
+
+#endif
+
--- /dev/null
+/*
+ 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.
+*/
+
+
+/** @file src/mpeg2_picture_asset_writer_common.cc
+ * @brief Common parts of MPEG2PictureAssetWriter
+ */
+
+
+#include "filesystem.h"
+
+
+using std::shared_ptr;
+
+
+namespace dcp {
+
+
+class MPEG2PictureAssetWriter;
+
+
+struct ASDCPMPEG2StateBase
+{
+ ASDCP::MPEG2::Parser mpeg2_parser;
+ ASDCP::WriterInfo writer_info;
+ ASDCP::MPEG2::VideoDescriptor video_descriptor;
+};
+
+
+}
+
+
+template <class P, class Q>
+void dcp::start(MPEG2PictureAssetWriter* writer, shared_ptr<P> state, Q* asset, uint8_t const * data, int size)
+{
+ asset->set_file (writer->_file);
+
+ if (ASDCP_FAILURE(state->mpeg2_parser.OpenRead(data, size))) {
+ boost::throw_exception(MiscError("could not parse MPEG2 frame"));
+ }
+
+ state->mpeg2_parser.FillVideoDescriptor(state->video_descriptor);
+ state->video_descriptor.EditRate = ASDCP::Rational(asset->edit_rate().numerator, asset->edit_rate().denominator);
+
+ asset->set_size(Size(state->video_descriptor.StoredWidth, state->video_descriptor.StoredHeight));
+ asset->set_screen_aspect_ratio(Fraction(state->video_descriptor.AspectRatio.Numerator, state->video_descriptor.AspectRatio.Denominator));
+
+ asset->fill_writer_info(&state->writer_info, asset->id());
+
+ auto r = state->mxf_writer.OpenWrite(
+ dcp::filesystem::fix_long_path(*asset->file()).string().c_str(),
+ state->writer_info,
+ state->video_descriptor,
+ 16384,
+ writer->_overwrite
+ );
+
+ if (ASDCP_FAILURE(r)) {
+ boost::throw_exception(MXFFileError("could not open MXF file for writing", asset->file()->string(), r));
+ }
+
+ writer->_started = true;
+}
#include "exceptions.h"
#include "mono_mpeg2_picture_frame.h"
#include "mpeg2_transcode.h"
+#include "scope_guard.h"
extern "C" {
#include <libavcodec/avcodec.h>
}
+using std::make_shared;
using std::shared_ptr;
using std::vector;
+using boost::optional;
using namespace dcp;
return images;
}
+
+MPEG2Compressor::MPEG2Compressor(dcp::Size size, int video_frame_rate, int64_t bit_rate)
+{
+ _codec = avcodec_find_encoder_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");
+ }
+
+ _context->width = size.width;
+ _context->height = size.height;
+ _context->time_base = AVRational{1, video_frame_rate};
+ _context->pix_fmt = AV_PIX_FMT_YUV420P;
+ _context->bit_rate = bit_rate;
+
+ int const r = avcodec_open2(_context, _codec, nullptr);
+ if (r < 0) {
+ avcodec_free_context(&_context);
+ throw MPEG2CodecError("could not open codec");
+ }
+}
+
+
+optional<MPEG2Compressor::IndexedFrame>
+MPEG2Compressor::send_and_receive(AVFrame const* frame)
+{
+ int r = avcodec_send_frame(_context, frame);
+ if (r < 0) {
+ throw MPEG2CompressionError(String::compose("avcodec_send_frame failed (%1", r));
+ }
+
+ auto packet = av_packet_alloc();
+ if (!packet) {
+ throw MPEG2CompressionError("could not allocate packet");
+ }
+
+ r = avcodec_receive_packet(_context, packet);
+ if (r < 0 && r != AVERROR(EAGAIN)) {
+ throw MPEG2CompressionError(String::compose("avcodec_receive_packet failed (%1)", r));
+ }
+
+ ScopeGuard sg = [&packet]() {
+ av_packet_free(&packet);
+ };
+
+ if (packet->size == 0) {
+ return {};
+ }
+
+ DCP_ASSERT(_context->time_base.num == 1);
+ return IndexedFrame{make_shared<MonoMPEG2PictureFrame>(packet->data, packet->size), std::round(static_cast<double>(packet->pts) / _context->time_base.den)};
+}
+
+
+optional<MPEG2Compressor::IndexedFrame>
+MPEG2Compressor::compress_frame(FFmpegImage const& image)
+{
+ return send_and_receive(image.frame());
+}
+
+
+optional<MPEG2Compressor::IndexedFrame>
+MPEG2Compressor::flush()
+{
+ return send_and_receive(nullptr);
+}
};
+class MPEG2Compressor : public MPEG2Codec
+{
+public:
+ MPEG2Compressor(dcp::Size size, int video_frame_rate, int64_t bit_rate);
+
+ MPEG2Compressor(MPEG2Compressor const&) = delete;
+ MPEG2Compressor& operator=(MPEG2Compressor const&) = delete;
+
+ /** Frame data with frame index within the asset */
+ typedef std::pair<std::shared_ptr<MonoMPEG2PictureFrame>, int64_t> IndexedFrame;
+
+ boost::optional<IndexedFrame> compress_frame(FFmpegImage const& image);
+ boost::optional<IndexedFrame> flush();
+
+private:
+ boost::optional<IndexedFrame> send_and_receive(AVFrame const* frame);
+};
+
+
}
class MXFMetadata;
class J2KPictureAssetWriter;
+class MPEG2PictureAssetWriter;
/** @class MXF
protected:
template <class P, class Q>
friend void start (J2KPictureAssetWriter* writer, std::shared_ptr<P> state, Q* mxf, uint8_t const * data, int size);
+ template <class P, class Q>
+ friend void start (MPEG2PictureAssetWriter* writer, std::shared_ptr<P> state, Q* mxf, uint8_t const * data, int size);
MXF ();
using namespace dcp;
-struct StereoJ2KPictureAssetWriter::ASDCPState : public ASDCPStateBase
+struct StereoJ2KPictureAssetWriter::ASDCPState : public ASDCPJ2KStateBase
{
ASDCP::JP2K::MXFSWriter mxf_writer;
};
exceptions.cc
file.cc
filesystem.cc
+ ffmpeg_image.cc
font_asset.cc
fsk.cc
gamma_transfer_function.cc
mono_j2k_picture_asset_writer.cc
mono_j2k_picture_frame.cc
mono_mpeg2_picture_asset.cc
+ mono_mpeg2_picture_asset_writer.cc
mono_mpeg2_picture_frame.cc
mpeg2_picture_asset.cc
+ mpeg2_picture_asset_writer.cc
mpeg2_transcode.cc
mxf.cc
name_format.cc
local_time.h
locale_convert.h
metadata.h
+ mpeg2_picture_asset_writer.h
modified_gamma_transfer_function.h
mono_j2k_picture_asset.h
mono_j2k_picture_asset_reader.h
mono_j2k_picture_frame.h
mono_mpeg2_picture_asset.h
mono_mpeg2_picture_asset_reader.h
+ mono_mpeg2_picture_asset_writer.h
mono_mpeg2_picture_frame.h
mpeg2_picture_asset.h
mpeg2_transcode.h
--- /dev/null
+/*
+ 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_write_test)
+{
+ boost::filesystem::path dir = "build/test/mpeg2_mono_picture_write_test";
+
+ boost::system::error_code ec;
+ boost::filesystem::remove_all(dir);
+ boost::filesystem::create_directories(dir);
+
+ dcp::MonoMPEG2PictureAsset asset(dcp::Fraction{24, 1});
+ auto writer = asset.start_write(dir / "test.mxf", dcp::Behaviour::MAKE_NEW);
+
+ dcp::MPEG2Compressor compressor({1920, 1080}, 24, 50000000);
+ dcp::FFmpegImage image(int64_t(0));
+ for (auto y = 0; y < 1080; ++y) {
+ uint8_t* py = image.y() + y * image.y_stride();
+ for (auto x = 0; x < 1920; ++x) {
+ if (x < 640) {
+ *py++ = 76;
+ } else if (x < 1280) {
+ *py++ = 149;
+ } else {
+ *py++ = 29;
+ }
+ }
+ }
+
+ for (auto y = 0; y < 540; ++y) {
+ uint8_t* pu = image.u() + y * image.u_stride();
+ uint8_t* pv = image.v() + y * image.v_stride();
+ for (auto x = 0; x < 960; ++x) {
+ if (x < 320) {
+ *pu++ = 84;
+ *pv++ = 255;
+ } else if (x < 640) {
+ *pu++ = 43;
+ *pv++ = 21;
+ } else {
+ *pu++ = 255;
+ *pv++ = 107;
+ }
+ }
+ }
+
+ for (auto i = 0; i < 24; ++i) {
+ image.set_pts(int64_t(i));
+ if (auto compressed = compressor.compress_frame(image)) {
+ writer->write(compressed->first->data(), compressed->first->size());
+ }
+ }
+
+ if (auto compressed = compressor.flush()) {
+ writer->write(compressed->first->data(), compressed->first->size());
+ }
+
+ writer->finalize();
+}
+
markers_test.cc
mca_test.cc
mono_mpeg2_picture_read_test.cc
+ mono_mpeg2_picture_write_test.cc
kdm_test.cc
key_test.cc
language_tag_test.cc