From 9fd79fb05fa47833ed431d83d73fd6d9a4a9f774 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 17 Jan 2014 22:16:05 +0000 Subject: Asset -> MXF in some cases. --- src/cpl.cc | 16 +-- src/dcp.cc | 4 +- src/kdm.cc | 6 +- src/mono_picture_asset.cc | 146 -------------------- src/mono_picture_asset.h | 50 ------- src/mono_picture_asset_writer.cc | 109 --------------- src/mono_picture_asset_writer.h | 60 --------- src/mono_picture_mxf.cc | 146 ++++++++++++++++++++ src/mono_picture_mxf.h | 50 +++++++ src/mono_picture_mxf_writer.cc | 109 +++++++++++++++ src/mono_picture_mxf_writer.h | 60 +++++++++ src/mxf.cc | 155 +++++++++++++++++++++ src/mxf.h | 126 +++++++++++++++++ src/mxf_asset.cc | 155 --------------------- src/mxf_asset.h | 124 ----------------- src/picture_asset.cc | 211 ----------------------------- src/picture_asset.h | 103 -------------- src/picture_asset_writer.cc | 94 ------------- src/picture_asset_writer.h | 78 ----------- src/picture_asset_writer_common.cc | 60 --------- src/picture_mxf.cc | 211 +++++++++++++++++++++++++++++ src/picture_mxf.h | 103 ++++++++++++++ src/picture_mxf_writer.cc | 94 +++++++++++++ src/picture_mxf_writer.h | 78 +++++++++++ src/picture_mxf_writer_common.cc | 60 +++++++++ src/reel.cc | 12 +- src/reel.h | 16 +-- src/sound_asset.cc | 269 ------------------------------------- src/sound_asset.h | 109 --------------- src/sound_mxf.cc | 269 +++++++++++++++++++++++++++++++++++++ src/sound_mxf.h | 109 +++++++++++++++ src/stereo_picture_asset.cc | 150 --------------------- src/stereo_picture_asset.h | 49 ------- src/stereo_picture_asset_writer.cc | 119 ---------------- src/stereo_picture_asset_writer.h | 62 --------- src/stereo_picture_mxf.cc | 150 +++++++++++++++++++++ src/stereo_picture_mxf.h | 49 +++++++ src/stereo_picture_mxf_writer.cc | 119 ++++++++++++++++ src/stereo_picture_mxf_writer.h | 62 +++++++++ src/wscript | 28 ++-- test/cpl_sar.cc | 4 +- test/decryption_test.cc | 6 +- test/frame_info_test.cc | 3 +- test/recovery_test.cc | 10 +- test/round_trip_test.cc | 12 +- tools/dcpinfo.cc | 4 +- 46 files changed, 2011 insertions(+), 2008 deletions(-) delete mode 100644 src/mono_picture_asset.cc delete mode 100644 src/mono_picture_asset.h delete mode 100644 src/mono_picture_asset_writer.cc delete mode 100644 src/mono_picture_asset_writer.h create mode 100644 src/mono_picture_mxf.cc create mode 100644 src/mono_picture_mxf.h create mode 100644 src/mono_picture_mxf_writer.cc create mode 100644 src/mono_picture_mxf_writer.h create mode 100644 src/mxf.cc create mode 100644 src/mxf.h delete mode 100644 src/mxf_asset.cc delete mode 100644 src/mxf_asset.h delete mode 100644 src/picture_asset.cc delete mode 100644 src/picture_asset.h delete mode 100644 src/picture_asset_writer.cc delete mode 100644 src/picture_asset_writer.h delete mode 100644 src/picture_asset_writer_common.cc create mode 100644 src/picture_mxf.cc create mode 100644 src/picture_mxf.h create mode 100644 src/picture_mxf_writer.cc create mode 100644 src/picture_mxf_writer.h create mode 100644 src/picture_mxf_writer_common.cc delete mode 100644 src/sound_asset.cc delete mode 100644 src/sound_asset.h create mode 100644 src/sound_mxf.cc create mode 100644 src/sound_mxf.h delete mode 100644 src/stereo_picture_asset.cc delete mode 100644 src/stereo_picture_asset.h delete mode 100644 src/stereo_picture_asset_writer.cc delete mode 100644 src/stereo_picture_asset_writer.h create mode 100644 src/stereo_picture_mxf.cc create mode 100644 src/stereo_picture_mxf.h create mode 100644 src/stereo_picture_mxf_writer.cc create mode 100644 src/stereo_picture_mxf_writer.h diff --git a/src/cpl.cc b/src/cpl.cc index 843f98d9..55683b64 100644 --- a/src/cpl.cc +++ b/src/cpl.cc @@ -21,9 +21,9 @@ #include "cpl.h" #include "parse/cpl.h" #include "util.h" -#include "mono_picture_asset.h" -#include "stereo_picture_asset.h" -#include "sound_asset.h" +#include "mono_picture_mxf.h" +#include "stereo_picture_mxf.h" +#include "sound_mxf.h" #include "subtitle_asset.h" #include "parse/asset_map.h" #include "reel.h" @@ -94,8 +94,8 @@ CPL::CPL (boost::filesystem::path directory, string file, list ass _fps = p->edit_rate.numerator; _length += p->duration; - shared_ptr picture; - shared_ptr sound; + shared_ptr picture; + shared_ptr sound; shared_ptr subtitle; /* Some rather twisted logic to decide if we are 3D or not; @@ -109,7 +109,7 @@ CPL::CPL (boost::filesystem::path directory, string file, list ass try { pair > asset = asset_from_id (asset_maps, p->id); - picture.reset (new MonoPictureAsset (asset.first, asset.second->chunks.front()->path)); + picture.reset (new MonoPictureMXF (asset.first, asset.second->chunks.front()->path)); picture->read (); picture->set_edit_rate (_fps); @@ -129,7 +129,7 @@ CPL::CPL (boost::filesystem::path directory, string file, list ass try { pair > asset = asset_from_id (asset_maps, p->id); - picture.reset (new StereoPictureAsset (asset.first, asset.second->chunks.front()->path)); + picture.reset (new StereoPictureMXF (asset.first, asset.second->chunks.front()->path)); picture->read (); picture->set_edit_rate (_fps); @@ -153,7 +153,7 @@ CPL::CPL (boost::filesystem::path directory, string file, list ass try { pair > asset = asset_from_id (asset_maps, (*i)->asset_list->main_sound->id); - sound.reset (new SoundAsset (asset.first, asset.second->chunks.front()->path)); + sound.reset (new SoundMXF (asset.first, asset.second->chunks.front()->path)); shared_ptr s = (*i)->asset_list->main_sound; sound->read (); diff --git a/src/dcp.cc b/src/dcp.cc index e0730020..495b14a4 100644 --- a/src/dcp.cc +++ b/src/dcp.cc @@ -33,8 +33,8 @@ #include #include #include "dcp.h" -#include "sound_asset.h" -#include "picture_asset.h" +#include "sound_mxf.h" +#include "picture_mxf.h" #include "subtitle_asset.h" #include "util.h" #include "metadata.h" diff --git a/src/kdm.cc b/src/kdm.cc index 25e87ca1..27ef64f2 100644 --- a/src/kdm.cc +++ b/src/kdm.cc @@ -32,7 +32,7 @@ #include "exceptions.h" #include "signer.h" #include "cpl.h" -#include "mxf_asset.h" +#include "mxf.h" #include "xml/kdm_smpte.h" using std::list; @@ -127,7 +127,7 @@ KDM::KDM ( list > assets = cpl->assets (); for (list >::iterator i = assets.begin(); i != assets.end(); ++i) { /* XXX: non-MXF assets? */ - shared_ptr mxf = boost::dynamic_pointer_cast (*i); + shared_ptr mxf = boost::dynamic_pointer_cast (*i); if (mxf) { apu.key_id_list.push_back (xml::TypedKeyId (mxf->key_type(), "urn:uuid:" + mxf->key_id())); } @@ -140,7 +140,7 @@ KDM::KDM ( for (list >::iterator i = assets.begin(); i != assets.end(); ++i) { /* XXX: non-MXF assets? */ - shared_ptr mxf = boost::dynamic_pointer_cast (*i); + shared_ptr mxf = boost::dynamic_pointer_cast (*i); if (mxf) { KDMKey kkey ( signer, cpl->id (), mxf->key_type (), mxf->key_id (), diff --git a/src/mono_picture_asset.cc b/src/mono_picture_asset.cc deleted file mode 100644 index 295d8a87..00000000 --- a/src/mono_picture_asset.cc +++ /dev/null @@ -1,146 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "mono_picture_asset.h" -#include "mono_picture_asset_writer.h" -#include "AS_DCP.h" -#include "KM_fileio.h" -#include "exceptions.h" -#include "mono_picture_frame.h" - -using std::string; -using std::vector; -using boost::shared_ptr; -using boost::dynamic_pointer_cast; -using boost::lexical_cast; -using namespace dcp; - -MonoPictureAsset::MonoPictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name) - : PictureAsset (directory, mxf_name) -{ - -} - -void -MonoPictureAsset::read () -{ - ASDCP::JP2K::MXFReader reader; - Kumu::Result_t r = reader.OpenRead (path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::JP2K::PictureDescriptor desc; - if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) { - boost::throw_exception (DCPReadError ("could not read video MXF information")); - } - - _size.width = desc.StoredWidth; - _size.height = desc.StoredHeight; - _edit_rate = desc.EditRate.Numerator; - assert (desc.EditRate.Denominator == 1); - _intrinsic_duration = desc.ContainerDuration; -} - -boost::filesystem::path -MonoPictureAsset::path_from_list (int f, vector const & files) const -{ - return files[f]; -} - -shared_ptr -MonoPictureAsset::get_frame (int n) const -{ - return shared_ptr (new MonoPictureFrame (path(), n, _decryption_context)); -} - -bool -MonoPictureAsset::equals (shared_ptr other, EqualityOptions opt, boost::function note) const -{ - if (!MXFAsset::equals (other, opt, note)) { - return false; - } - - ASDCP::JP2K::MXFReader reader_A; - Kumu::Result_t r = reader_A.OpenRead (path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::JP2K::MXFReader reader_B; - r = reader_B.OpenRead (other->path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::JP2K::PictureDescriptor desc_A; - if (ASDCP_FAILURE (reader_A.FillPictureDescriptor (desc_A))) { - boost::throw_exception (DCPReadError ("could not read video MXF information")); - } - ASDCP::JP2K::PictureDescriptor desc_B; - if (ASDCP_FAILURE (reader_B.FillPictureDescriptor (desc_B))) { - boost::throw_exception (DCPReadError ("could not read video MXF information")); - } - - if (!descriptor_equals (desc_A, desc_B, note)) { - return false; - } - - shared_ptr other_picture = dynamic_pointer_cast (other); - assert (other_picture); - - for (int i = 0; i < _intrinsic_duration; ++i) { - if (i >= other_picture->intrinsic_duration()) { - return false; - } - - note (PROGRESS, "Comparing video frame " + lexical_cast (i) + " of " + lexical_cast (_intrinsic_duration)); - shared_ptr frame_A = get_frame (i); - shared_ptr frame_B = other_picture->get_frame (i); - - if (!frame_buffer_equals ( - i, opt, note, - frame_A->j2k_data(), frame_A->j2k_size(), - frame_B->j2k_data(), frame_B->j2k_size() - )) { - return false; - } - } - - return true; -} - -shared_ptr -MonoPictureAsset::start_write (bool overwrite) -{ - /* XXX: can't we use shared_ptr here? */ - return shared_ptr (new MonoPictureAssetWriter (this, overwrite)); -} - -string -MonoPictureAsset::cpl_node_name () const -{ - return "MainPicture"; -} - -int -MonoPictureAsset::edit_rate_factor () const -{ - return 1; -} diff --git a/src/mono_picture_asset.h b/src/mono_picture_asset.h deleted file mode 100644 index 76a1f990..00000000 --- a/src/mono_picture_asset.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef LIBDCP_MONO_PICTURE_ASSET_H -#define LIBDCP_MONO_PICTURE_ASSET_H - -#include "picture_asset.h" - -namespace dcp { - -/** A 2D (monoscopic) picture asset */ -class MonoPictureAsset : public PictureAsset -{ -public: - MonoPictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name); - - void read (); - - /** Start a progressive write to a MonoPictureAsset */ - boost::shared_ptr start_write (bool); - - boost::shared_ptr get_frame (int n) const; - bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; - -private: - boost::filesystem::path path_from_list (int f, std::vector const & files) const; - void construct (boost::function, bool, MXFMetadata const &); - std::string cpl_node_name () const; - int edit_rate_factor () const; -}; - -} - -#endif diff --git a/src/mono_picture_asset_writer.cc b/src/mono_picture_asset_writer.cc deleted file mode 100644 index 87f37a2d..00000000 --- a/src/mono_picture_asset_writer.cc +++ /dev/null @@ -1,109 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "AS_DCP.h" -#include "KM_fileio.h" -#include "mono_picture_asset_writer.h" -#include "exceptions.h" -#include "picture_asset.h" - -#include "picture_asset_writer_common.cc" - -using std::istream; -using std::ostream; -using std::string; -using boost::shared_ptr; -using boost::lexical_cast; -using namespace dcp; - -struct MonoPictureAssetWriter::ASDCPState : public ASDCPStateBase -{ - ASDCP::JP2K::MXFWriter mxf_writer; -}; - -/** @param a Asset to write to. `a' must not be deleted while - * this writer class still exists, or bad things will happen. - */ -MonoPictureAssetWriter::MonoPictureAssetWriter (PictureAsset* asset, bool overwrite) - : PictureAssetWriter (asset, overwrite) - , _state (new MonoPictureAssetWriter::ASDCPState) -{ - _state->encryption_context = asset->encryption_context (); -} - -void -MonoPictureAssetWriter::start (uint8_t* data, int size) -{ - dcp::start (this, _state, _asset, data, size); -} - -FrameInfo -MonoPictureAssetWriter::write (uint8_t* data, int size) -{ - assert (!_finalized); - - if (!_started) { - start (data, size); - } - - if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) { - boost::throw_exception (MiscError ("could not parse J2K frame")); - } - - uint64_t const before_offset = _state->mxf_writer.Tell (); - - string hash; - ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0, &hash); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r)); - } - - ++_frames_written; - return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash); -} - -void -MonoPictureAssetWriter::fake_write (int size) -{ - assert (_started); - assert (!_finalized); - - Kumu::Result_t r = _state->mxf_writer.FakeWriteFrame (size); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r)); - } - - ++_frames_written; -} - -void -MonoPictureAssetWriter::finalize () -{ - assert (!_finalized); - - Kumu::Result_t r = _state->mxf_writer.Finalize(); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string(), r)); - } - - _finalized = true; - _asset->set_intrinsic_duration (_frames_written); - _asset->set_duration (_frames_written); -} - diff --git a/src/mono_picture_asset_writer.h b/src/mono_picture_asset_writer.h deleted file mode 100644 index b4da70b4..00000000 --- a/src/mono_picture_asset_writer.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include -#include -#include -#include -#include "picture_asset_writer.h" - -namespace dcp { - -/** A helper class for writing to MonoPictureAssets progressively (i.e. writing frame-by-frame, - * rather than giving libdcp all the frames in one go). - * - * Objects of this class can only be created with MonoPictureAsset::start_write(). - * - * Frames can be written to the MonoPictureAsset by calling write() with a JPEG2000 image - * (a verbatim .j2c file). finalize() must be called after the last frame has been written. - * The action of finalize() can't be done in MonoPictureAssetWriter's destructor as it may - * throw an exception. - */ -class MonoPictureAssetWriter : public PictureAssetWriter -{ -public: - FrameInfo write (uint8_t *, int); - void fake_write (int size); - void finalize (); - -private: - friend class MonoPictureAsset; - - MonoPictureAssetWriter (PictureAsset *, bool); - void start (uint8_t *, int); - - /* do this with an opaque pointer so we don't have to include - ASDCP headers - */ - - struct ASDCPState; - boost::shared_ptr _state; -}; - -} diff --git a/src/mono_picture_mxf.cc b/src/mono_picture_mxf.cc new file mode 100644 index 00000000..30973e5a --- /dev/null +++ b/src/mono_picture_mxf.cc @@ -0,0 +1,146 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "mono_picture_mxf.h" +#include "mono_picture_mxf_writer.h" +#include "AS_DCP.h" +#include "KM_fileio.h" +#include "exceptions.h" +#include "mono_picture_frame.h" + +using std::string; +using std::vector; +using boost::shared_ptr; +using boost::dynamic_pointer_cast; +using boost::lexical_cast; +using namespace dcp; + +MonoPictureMXF::MonoPictureMXF (boost::filesystem::path directory, boost::filesystem::path mxf_name) + : PictureMXF (directory, mxf_name) +{ + +} + +void +MonoPictureMXF::read () +{ + ASDCP::JP2K::MXFReader reader; + Kumu::Result_t r = reader.OpenRead (path().string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); + } + + ASDCP::JP2K::PictureDescriptor desc; + if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + + _size.width = desc.StoredWidth; + _size.height = desc.StoredHeight; + _edit_rate = desc.EditRate.Numerator; + assert (desc.EditRate.Denominator == 1); + _intrinsic_duration = desc.ContainerDuration; +} + +boost::filesystem::path +MonoPictureMXF::path_from_list (int f, vector const & files) const +{ + return files[f]; +} + +shared_ptr +MonoPictureMXF::get_frame (int n) const +{ + return shared_ptr (new MonoPictureFrame (path(), n, _decryption_context)); +} + +bool +MonoPictureMXF::equals (shared_ptr other, EqualityOptions opt, boost::function note) const +{ + if (!MXF::equals (other, opt, note)) { + return false; + } + + ASDCP::JP2K::MXFReader reader_A; + Kumu::Result_t r = reader_A.OpenRead (path().string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); + } + + ASDCP::JP2K::MXFReader reader_B; + r = reader_B.OpenRead (other->path().string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); + } + + ASDCP::JP2K::PictureDescriptor desc_A; + if (ASDCP_FAILURE (reader_A.FillPictureDescriptor (desc_A))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + ASDCP::JP2K::PictureDescriptor desc_B; + if (ASDCP_FAILURE (reader_B.FillPictureDescriptor (desc_B))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + + if (!descriptor_equals (desc_A, desc_B, note)) { + return false; + } + + shared_ptr other_picture = dynamic_pointer_cast (other); + assert (other_picture); + + for (int i = 0; i < _intrinsic_duration; ++i) { + if (i >= other_picture->intrinsic_duration()) { + return false; + } + + note (PROGRESS, "Comparing video frame " + lexical_cast (i) + " of " + lexical_cast (_intrinsic_duration)); + shared_ptr frame_A = get_frame (i); + shared_ptr frame_B = other_picture->get_frame (i); + + if (!frame_buffer_equals ( + i, opt, note, + frame_A->j2k_data(), frame_A->j2k_size(), + frame_B->j2k_data(), frame_B->j2k_size() + )) { + return false; + } + } + + return true; +} + +shared_ptr +MonoPictureMXF::start_write (bool overwrite) +{ + /* XXX: can't we use shared_ptr here? */ + return shared_ptr (new MonoPictureMXFWriter (this, overwrite)); +} + +string +MonoPictureMXF::cpl_node_name () const +{ + return "MainPicture"; +} + +int +MonoPictureMXF::edit_rate_factor () const +{ + return 1; +} diff --git a/src/mono_picture_mxf.h b/src/mono_picture_mxf.h new file mode 100644 index 00000000..029ccce5 --- /dev/null +++ b/src/mono_picture_mxf.h @@ -0,0 +1,50 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_MONO_PICTURE_MXF_H +#define LIBDCP_MONO_PICTURE_MXF_H + +#include "picture_mxf.h" + +namespace dcp { + +/** A 2D (monoscopic) picture asset */ +class MonoPictureMXF : public PictureMXF +{ +public: + MonoPictureMXF (boost::filesystem::path directory, boost::filesystem::path mxf_name); + + void read (); + + /** Start a progressive write to a MonoPictureMXF */ + boost::shared_ptr start_write (bool); + + boost::shared_ptr get_frame (int n) const; + bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; + +private: + boost::filesystem::path path_from_list (int f, std::vector const & files) const; + void construct (boost::function, bool, MXFMetadata const &); + std::string cpl_node_name () const; + int edit_rate_factor () const; +}; + +} + +#endif diff --git a/src/mono_picture_mxf_writer.cc b/src/mono_picture_mxf_writer.cc new file mode 100644 index 00000000..24ca2079 --- /dev/null +++ b/src/mono_picture_mxf_writer.cc @@ -0,0 +1,109 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "AS_DCP.h" +#include "KM_fileio.h" +#include "mono_picture_mxf_writer.h" +#include "exceptions.h" +#include "picture_mxf.h" + +#include "picture_mxf_writer_common.cc" + +using std::istream; +using std::ostream; +using std::string; +using boost::shared_ptr; +using boost::lexical_cast; +using namespace dcp; + +struct MonoPictureMXFWriter::ASDCPState : public ASDCPStateBase +{ + ASDCP::JP2K::MXFWriter mxf_writer; +}; + +/** @param a Asset to write to. `a' must not be deleted while + * this writer class still exists, or bad things will happen. + */ +MonoPictureMXFWriter::MonoPictureMXFWriter (PictureMXF* asset, bool overwrite) + : PictureMXFWriter (asset, overwrite) + , _state (new MonoPictureMXFWriter::ASDCPState) +{ + _state->encryption_context = asset->encryption_context (); +} + +void +MonoPictureMXFWriter::start (uint8_t* data, int size) +{ + dcp::start (this, _state, _asset, data, size); +} + +FrameInfo +MonoPictureMXFWriter::write (uint8_t* data, int size) +{ + assert (!_finalized); + + if (!_started) { + start (data, size); + } + + if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) { + boost::throw_exception (MiscError ("could not parse J2K frame")); + } + + uint64_t const before_offset = _state->mxf_writer.Tell (); + + string hash; + ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0, &hash); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r)); + } + + ++_frames_written; + return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash); +} + +void +MonoPictureMXFWriter::fake_write (int size) +{ + assert (_started); + assert (!_finalized); + + Kumu::Result_t r = _state->mxf_writer.FakeWriteFrame (size); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r)); + } + + ++_frames_written; +} + +void +MonoPictureMXFWriter::finalize () +{ + assert (!_finalized); + + Kumu::Result_t r = _state->mxf_writer.Finalize(); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string(), r)); + } + + _finalized = true; + _asset->set_intrinsic_duration (_frames_written); + _asset->set_duration (_frames_written); +} + diff --git a/src/mono_picture_mxf_writer.h b/src/mono_picture_mxf_writer.h new file mode 100644 index 00000000..4faa6b60 --- /dev/null +++ b/src/mono_picture_mxf_writer.h @@ -0,0 +1,60 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include "picture_mxf_writer.h" + +namespace dcp { + +/** A helper class for writing to MonoPictureAssets progressively (i.e. writing frame-by-frame, + * rather than giving libdcp all the frames in one go). + * + * Objects of this class can only be created with MonoPictureAsset::start_write(). + * + * Frames can be written to the MonoPictureAsset by calling write() with a JPEG2000 image + * (a verbatim .j2c file). finalize() must be called after the last frame has been written. + * The action of finalize() can't be done in MonoPictureAssetWriter's destructor as it may + * throw an exception. + */ +class MonoPictureMXFWriter : public PictureMXFWriter +{ +public: + FrameInfo write (uint8_t *, int); + void fake_write (int size); + void finalize (); + +private: + friend class MonoPictureMXF; + + MonoPictureMXFWriter (PictureMXF *, bool); + void start (uint8_t *, int); + + /* do this with an opaque pointer so we don't have to include + ASDCP headers + */ + + struct ASDCPState; + boost::shared_ptr _state; +}; + +} diff --git a/src/mxf.cc b/src/mxf.cc new file mode 100644 index 00000000..e37b2822 --- /dev/null +++ b/src/mxf.cc @@ -0,0 +1,155 @@ +/* + Copyright (C) 2012 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/asset.cc + * @brief Parent class for assets of DCPs made up of MXF files. + */ + +#include +#include +#include +#include +#include "AS_DCP.h" +#include "KM_prng.h" +#include "KM_util.h" +#include "mxf.h" +#include "util.h" +#include "metadata.h" +#include "exceptions.h" +#include "kdm.h" + +using std::string; +using std::list; +using std::pair; +using boost::shared_ptr; +using boost::lexical_cast; +using boost::dynamic_pointer_cast; +using namespace dcp; + +MXF::MXF (boost::filesystem::path directory, boost::filesystem::path file_name) + : ContentAsset (directory, file_name) + , _progress (0) + , _encryption_context (0) + , _decryption_context (0) + , _interop (false) +{ + +} + +MXF::~MXF () +{ + delete _encryption_context; + delete _decryption_context; +} + +void +MXF::fill_writer_info (ASDCP::WriterInfo* writer_info) +{ + writer_info->ProductVersion = _metadata.product_version; + writer_info->CompanyName = _metadata.company_name; + writer_info->ProductName = _metadata.product_name.c_str(); + + if (_interop) { + writer_info->LabelSetType = ASDCP::LS_MXF_INTEROP; + } else { + writer_info->LabelSetType = ASDCP::LS_MXF_SMPTE; + } + unsigned int c; + Kumu::hex2bin (_uuid.c_str(), writer_info->AssetUUID, Kumu::UUID_Length, &c); + assert (c == Kumu::UUID_Length); + + if (_key) { + Kumu::GenRandomUUID (writer_info->ContextID); + writer_info->EncryptedEssence = true; + + unsigned int c; + Kumu::hex2bin (_key_id.c_str(), writer_info->CryptographicKeyID, Kumu::UUID_Length, &c); + assert (c == Kumu::UUID_Length); + } +} + +bool +MXF::equals (shared_ptr other, EqualityOptions opt, boost::function note) const +{ + if (!ContentAsset::equals (other, opt, note)) { + return false; + } + + shared_ptr other_mxf = dynamic_pointer_cast (other); + if (!other_mxf) { + note (ERROR, "comparing an MXF asset with a non-MXF asset"); + return false; + } + + if (_file_name != other_mxf->_file_name) { + note (ERROR, "MXF names differ"); + if (!opt.mxf_names_can_differ) { + return false; + } + } + + return true; +} + +void +MXF::write_to_cpl (xmlpp::Element* node) const +{ + pair const attr = cpl_node_attribute (); + xmlpp::Element* a = node->add_child (cpl_node_name ()); + if (!attr.first.empty ()) { + a->set_attribute (attr.first, attr.second); + } + a->add_child ("Id")->add_child_text ("urn:uuid:" + _uuid); + a->add_child ("AnnotationText")->add_child_text (_file_name.string ()); + a->add_child ("EditRate")->add_child_text (lexical_cast (_edit_rate) + " 1"); + a->add_child ("IntrinsicDuration")->add_child_text (lexical_cast (_intrinsic_duration)); + a->add_child ("EntryPoint")->add_child_text (lexical_cast (_entry_point)); + a->add_child ("Duration")->add_child_text (lexical_cast (_duration)); + if (!_key_id.empty ()) { + a->add_child("KeyId")->add_child_text ("urn:uuid:" + _key_id); + } +} + +void +MXF::set_key (Key key) +{ + _key = key; + + if (_key_id.empty ()) { + /* No key ID so far; we now need one */ + _key_id = make_uuid (); + } + + _decryption_context = new ASDCP::AESDecContext; + if (ASDCP_FAILURE (_decryption_context->InitKey (_key->value ()))) { + throw MiscError ("could not set up decryption context"); + } + + _encryption_context = new ASDCP::AESEncContext; + if (ASDCP_FAILURE (_encryption_context->InitKey (_key->value ()))) { + throw MiscError ("could not set up encryption context"); + } + + uint8_t cbc_buffer[ASDCP::CBC_BLOCK_SIZE]; + + Kumu::FortunaRNG rng; + if (ASDCP_FAILURE (_encryption_context->SetIVec (rng.FillRandom (cbc_buffer, ASDCP::CBC_BLOCK_SIZE)))) { + throw MiscError ("could not set up CBC initialization vector"); + } +} diff --git a/src/mxf.h b/src/mxf.h new file mode 100644 index 00000000..3e9e6666 --- /dev/null +++ b/src/mxf.h @@ -0,0 +1,126 @@ +/* + Copyright (C) 2012 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_MXF_ASSET_H +#define LIBDCP_MXF_ASSET_H + +#include +#include "content_asset.h" +#include "key.h" +#include "metadata.h" + +namespace ASDCP { + class AESEncContext; + class AESDecContext; +} + +namespace dcp +{ + +class MXFMetadata; + +/** @class MXF + * @brief Parent class for classes which represent MXF files. + */ +class MXF : public ContentAsset +{ +public: + /** Construct an MXF. + * This class will not write anything to disk in this constructor, but subclasses may. + * + * @param directory Directory where MXF file is. + * @param file_name Name of MXF file. + */ + MXF (boost::filesystem::path directory, boost::filesystem::path file_name); + + ~MXF (); + + virtual bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; + virtual void write_to_cpl (xmlpp::Element *) const; + virtual std::string key_type () const = 0; + + /** Fill in a ADSCP::WriteInfo struct. + * @param w struct to fill in. + */ + void fill_writer_info (ASDCP::WriterInfo* w); + + void set_progress (boost::signals2::signal* progress) { + _progress = progress; + } + + bool encrypted () const { + return !_key_id.empty (); + } + + void set_key_id (std::string i) { + _key_id = i; + } + + std::string key_id () const { + return _key_id; + } + + void set_key (Key); + + boost::optional key () const { + return _key; + } + + ASDCP::AESEncContext* encryption_context () const { + return _encryption_context; + } + + void set_metadata (MXFMetadata m) { + _metadata = m; + } + + MXFMetadata metadata () const { + return _metadata; + } + + /** Set whether or not the asset should be written in Interop mode. + * @param i true to use interop. + */ + void set_interop (bool i) { + _interop = i; + } + + bool interop () const { + return _interop; + } + +protected: + virtual std::string cpl_node_name () const = 0; + virtual std::pair cpl_node_attribute () const { + return std::make_pair ("", ""); + } + + /** Signal to emit to report progress, or 0 */ + boost::signals2::signal* _progress; + ASDCP::AESEncContext* _encryption_context; + ASDCP::AESDecContext* _decryption_context; + std::string _key_id; + boost::optional _key; + MXFMetadata _metadata; + bool _interop; +}; + +} + +#endif diff --git a/src/mxf_asset.cc b/src/mxf_asset.cc deleted file mode 100644 index c2d19a39..00000000 --- a/src/mxf_asset.cc +++ /dev/null @@ -1,155 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/asset.cc - * @brief Parent class for assets of DCPs made up of MXF files. - */ - -#include -#include -#include -#include -#include "AS_DCP.h" -#include "KM_prng.h" -#include "KM_util.h" -#include "mxf_asset.h" -#include "util.h" -#include "metadata.h" -#include "exceptions.h" -#include "kdm.h" - -using std::string; -using std::list; -using std::pair; -using boost::shared_ptr; -using boost::lexical_cast; -using boost::dynamic_pointer_cast; -using namespace dcp; - -MXFAsset::MXFAsset (boost::filesystem::path directory, boost::filesystem::path file_name) - : ContentAsset (directory, file_name) - , _progress (0) - , _encryption_context (0) - , _decryption_context (0) - , _interop (false) -{ - -} - -MXFAsset::~MXFAsset () -{ - delete _encryption_context; - delete _decryption_context; -} - -void -MXFAsset::fill_writer_info (ASDCP::WriterInfo* writer_info) -{ - writer_info->ProductVersion = _metadata.product_version; - writer_info->CompanyName = _metadata.company_name; - writer_info->ProductName = _metadata.product_name.c_str(); - - if (_interop) { - writer_info->LabelSetType = ASDCP::LS_MXF_INTEROP; - } else { - writer_info->LabelSetType = ASDCP::LS_MXF_SMPTE; - } - unsigned int c; - Kumu::hex2bin (_uuid.c_str(), writer_info->AssetUUID, Kumu::UUID_Length, &c); - assert (c == Kumu::UUID_Length); - - if (_key) { - Kumu::GenRandomUUID (writer_info->ContextID); - writer_info->EncryptedEssence = true; - - unsigned int c; - Kumu::hex2bin (_key_id.c_str(), writer_info->CryptographicKeyID, Kumu::UUID_Length, &c); - assert (c == Kumu::UUID_Length); - } -} - -bool -MXFAsset::equals (shared_ptr other, EqualityOptions opt, boost::function note) const -{ - if (!ContentAsset::equals (other, opt, note)) { - return false; - } - - shared_ptr other_mxf = dynamic_pointer_cast (other); - if (!other_mxf) { - note (ERROR, "comparing an MXF asset with a non-MXF asset"); - return false; - } - - if (_file_name != other_mxf->_file_name) { - note (ERROR, "MXF names differ"); - if (!opt.mxf_names_can_differ) { - return false; - } - } - - return true; -} - -void -MXFAsset::write_to_cpl (xmlpp::Element* node) const -{ - pair const attr = cpl_node_attribute (); - xmlpp::Element* a = node->add_child (cpl_node_name ()); - if (!attr.first.empty ()) { - a->set_attribute (attr.first, attr.second); - } - a->add_child ("Id")->add_child_text ("urn:uuid:" + _uuid); - a->add_child ("AnnotationText")->add_child_text (_file_name.string ()); - a->add_child ("EditRate")->add_child_text (lexical_cast (_edit_rate) + " 1"); - a->add_child ("IntrinsicDuration")->add_child_text (lexical_cast (_intrinsic_duration)); - a->add_child ("EntryPoint")->add_child_text (lexical_cast (_entry_point)); - a->add_child ("Duration")->add_child_text (lexical_cast (_duration)); - if (!_key_id.empty ()) { - a->add_child("KeyId")->add_child_text ("urn:uuid:" + _key_id); - } -} - -void -MXFAsset::set_key (Key key) -{ - _key = key; - - if (_key_id.empty ()) { - /* No key ID so far; we now need one */ - _key_id = make_uuid (); - } - - _decryption_context = new ASDCP::AESDecContext; - if (ASDCP_FAILURE (_decryption_context->InitKey (_key->value ()))) { - throw MiscError ("could not set up decryption context"); - } - - _encryption_context = new ASDCP::AESEncContext; - if (ASDCP_FAILURE (_encryption_context->InitKey (_key->value ()))) { - throw MiscError ("could not set up encryption context"); - } - - uint8_t cbc_buffer[ASDCP::CBC_BLOCK_SIZE]; - - Kumu::FortunaRNG rng; - if (ASDCP_FAILURE (_encryption_context->SetIVec (rng.FillRandom (cbc_buffer, ASDCP::CBC_BLOCK_SIZE)))) { - throw MiscError ("could not set up CBC initialization vector"); - } -} diff --git a/src/mxf_asset.h b/src/mxf_asset.h deleted file mode 100644 index 4af7a38d..00000000 --- a/src/mxf_asset.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef LIBDCP_MXF_ASSET_H -#define LIBDCP_MXF_ASSET_H - -#include -#include "content_asset.h" -#include "key.h" -#include "metadata.h" - -namespace ASDCP { - class AESEncContext; - class AESDecContext; -} - -namespace dcp -{ - -class MXFMetadata; - -/** @brief Parent class for assets which have MXF files */ -class MXFAsset : public ContentAsset -{ -public: - /** Construct an MXFAsset. - * This class will not write anything to disk in this constructor, but subclasses may. - * - * @param directory Directory where MXF file is. - * @param file_name Name of MXF file. - */ - MXFAsset (boost::filesystem::path directory, boost::filesystem::path file_name); - - ~MXFAsset (); - - virtual bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; - virtual void write_to_cpl (xmlpp::Element *) const; - virtual std::string key_type () const = 0; - - /** Fill in a ADSCP::WriteInfo struct. - * @param w struct to fill in. - */ - void fill_writer_info (ASDCP::WriterInfo* w); - - void set_progress (boost::signals2::signal* progress) { - _progress = progress; - } - - bool encrypted () const { - return !_key_id.empty (); - } - - void set_key_id (std::string i) { - _key_id = i; - } - - std::string key_id () const { - return _key_id; - } - - void set_key (Key); - - boost::optional key () const { - return _key; - } - - ASDCP::AESEncContext* encryption_context () const { - return _encryption_context; - } - - void set_metadata (MXFMetadata m) { - _metadata = m; - } - - MXFMetadata metadata () const { - return _metadata; - } - - /** Set whether or not the asset should be written in Interop mode. - * @param i true to use interop. - */ - void set_interop (bool i) { - _interop = i; - } - - bool interop () const { - return _interop; - } - -protected: - virtual std::string cpl_node_name () const = 0; - virtual std::pair cpl_node_attribute () const { - return std::make_pair ("", ""); - } - - /** Signal to emit to report progress, or 0 */ - boost::signals2::signal* _progress; - ASDCP::AESEncContext* _encryption_context; - ASDCP::AESDecContext* _decryption_context; - std::string _key_id; - boost::optional _key; - MXFMetadata _metadata; - bool _interop; -}; - -} - -#endif diff --git a/src/picture_asset.cc b/src/picture_asset.cc deleted file mode 100644 index 94ebefaa..00000000 --- a/src/picture_asset.cc +++ /dev/null @@ -1,211 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/picture_asset.cc - * @brief An asset made up of JPEG2000 files - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "AS_DCP.h" -#include "KM_fileio.h" -#include "picture_asset.h" -#include "util.h" -#include "exceptions.h" -#include "xyz_frame.h" -#include "picture_asset_writer.h" - -using std::string; -using std::ostream; -using std::list; -using std::vector; -using std::max; -using std::stringstream; -using std::pair; -using std::make_pair; -using std::istream; -using std::cout; -using boost::shared_ptr; -using boost::dynamic_pointer_cast; -using boost::lexical_cast; -using namespace dcp; - -PictureAsset::PictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name) - : MXFAsset (directory, mxf_name) -{ - -} - -void -PictureAsset::write_to_cpl (xmlpp::Element* node) const -{ - MXFAsset::write_to_cpl (node); - - xmlpp::Node::NodeList c = node->get_children (); - xmlpp::Node::NodeList::iterator i = c.begin(); - while (i != c.end() && (*i)->get_name() != cpl_node_name ()) { - ++i; - } - - assert (i != c.end ()); - - (*i)->add_child ("FrameRate")->add_child_text (lexical_cast (_edit_rate * edit_rate_factor ()) + " 1"); - if (_interop) { - stringstream s; - s << std::fixed << std::setprecision (2) << (float (_size.width) / _size.height); - (*i)->add_child ("ScreenAspectRatio")->add_child_text (s.str ()); - } else { - (*i)->add_child ("ScreenAspectRatio")->add_child_text (lexical_cast (_size.width) + " " + lexical_cast (_size.height)); - } -} - -bool -PictureAsset::descriptor_equals ( - ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, boost::function note - ) const -{ - if ( - a.EditRate != b.EditRate || - a.SampleRate != b.SampleRate || - a.StoredWidth != b.StoredWidth || - a.StoredHeight != b.StoredHeight || - a.AspectRatio != b.AspectRatio || - a.Rsize != b.Rsize || - a.Xsize != b.Xsize || - a.Ysize != b.Ysize || - a.XOsize != b.XOsize || - a.YOsize != b.YOsize || - a.XTsize != b.XTsize || - a.YTsize != b.YTsize || - a.XTOsize != b.XTOsize || - a.YTOsize != b.YTOsize || - a.Csize != b.Csize -// a.CodingStyleDefault != b.CodingStyleDefault || -// a.QuantizationDefault != b.QuantizationDefault - ) { - - note (ERROR, "video MXF picture descriptors differ"); - return false; - } - - if (a.ContainerDuration != b.ContainerDuration) { - note (ERROR, "video container durations differ"); - } - -// for (unsigned int j = 0; j < ASDCP::JP2K::MaxComponents; ++j) { -// if (a.ImageComponents[j] != b.ImageComponents[j]) { -// notes.pack_start ("video MXF picture descriptors differ"); -// } -// } - - return true; -} - -bool -PictureAsset::frame_buffer_equals ( - int frame, EqualityOptions opt, boost::function note, - uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B - ) const -{ - if (size_A == size_B && memcmp (data_A, data_B, size_A) == 0) { - note (NOTE, "J2K identical"); - /* Easy result; the J2K data is identical */ - return true; - } - - /* Decompress the images to bitmaps */ - shared_ptr image_A = decompress_j2k (const_cast (data_A), size_A, 0); - shared_ptr image_B = decompress_j2k (const_cast (data_B), size_B, 0); - - /* Compare them */ - - vector abs_diffs (image_A->size().width * image_A->size().height * 3); - int d = 0; - int max_diff = 0; - - for (int c = 0; c < 3; ++c) { - - if (image_A->size() != image_B->size()) { - note (ERROR, "image sizes for frame " + lexical_cast(frame) + " differ"); - return false; - } - - int const pixels = image_A->size().width * image_A->size().height; - for (int j = 0; j < pixels; ++j) { - int const t = abs (image_A->data(c)[j] - image_B->data(c)[j]); - abs_diffs[d++] = t; - max_diff = max (max_diff, t); - } - } - - uint64_t total = 0; - for (vector::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) { - total += *j; - } - - double const mean = double (total) / abs_diffs.size (); - - uint64_t total_squared_deviation = 0; - for (vector::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) { - total_squared_deviation += pow (*j - mean, 2); - } - - double const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size()); - - note (NOTE, "mean difference " + lexical_cast (mean) + ", deviation " + lexical_cast (std_dev)); - - if (mean > opt.max_mean_pixel_error) { - note ( - ERROR, - "mean " + lexical_cast(mean) + - " out of range " + lexical_cast(opt.max_mean_pixel_error) + - " in frame " + lexical_cast(frame) - ); - - return false; - } - - if (std_dev > opt.max_std_dev_pixel_error) { - note ( - ERROR, - "standard deviation " + lexical_cast(std_dev) + - " out of range " + lexical_cast(opt.max_std_dev_pixel_error) + - " in frame " + lexical_cast(frame) - ); - - return false; - } - - return true; -} - -string -PictureAsset::key_type () const -{ - return "MDIK"; -} - - - diff --git a/src/picture_asset.h b/src/picture_asset.h deleted file mode 100644 index d9b9919b..00000000 --- a/src/picture_asset.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef LIBDCP_PICTURE_ASSET_H -#define LIBDCP_PICTURE_ASSET_H - -/** @file src/picture_asset.h - * @brief An asset made up of JPEG2000 data - */ - -#include -#include "mxf_asset.h" -#include "util.h" -#include "metadata.h" - -namespace ASDCP { - namespace JP2K { - class PictureDescriptor; - } -} - -namespace dcp -{ - -class MonoPictureFrame; -class StereoPictureFrame; -class PictureAssetWriter; - -/** @brief An asset made up of JPEG2000 data */ -class PictureAsset : public MXFAsset -{ -public: - /** Construct a PictureAsset. - * - * @param directory Directory where MXF file is. - * @param mxf_name Name of MXF file. - */ - PictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name); - - /** Start a progressive write to this asset. - * The following parameters must be set up (if required) before calling this: - * Interop mode (set_interop) - * Edit rate (set_edit_rate) - * MXF Metadata (set_metadata) - * - * @param overwrite true to overwrite an existing MXF file; in this mode, writing can be resumed to a partially-written MXF; false if the - * MXF file does not exist. - */ - virtual boost::shared_ptr start_write (bool overwrite) = 0; - - virtual void read () = 0; - virtual void create (std::vector const &) {} - virtual void create (boost::function) {} - - Size size () const { - return _size; - } - - void set_size (Size s) { - _size = s; - } - - void write_to_cpl (xmlpp::Element *) const; - -protected: - - bool frame_buffer_equals ( - int frame, EqualityOptions opt, boost::function note, - uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B - ) const; - - bool descriptor_equals ( - ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, boost::function - ) const; - - /** picture size in pixels */ - Size _size; - -private: - std::string key_type () const; - virtual int edit_rate_factor () const = 0; -}; - - -} - -#endif diff --git a/src/picture_asset_writer.cc b/src/picture_asset_writer.cc deleted file mode 100644 index c1c9bd85..00000000 --- a/src/picture_asset_writer.cc +++ /dev/null @@ -1,94 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include -#include "AS_DCP.h" -#include "KM_fileio.h" -#include "picture_asset_writer.h" -#include "exceptions.h" -#include "picture_asset.h" - -using std::istream; -using std::ostream; -using std::string; -using boost::shared_ptr; -using namespace dcp; - -FrameInfo::FrameInfo (istream& s) - : offset (0) - , size (0) -{ - s >> offset >> size; - - if (!s.good ()) { - /* Make sure we zero these if something bad happened, otherwise - the caller might try to alloc lots of RAM. - */ - offset = size = 0; - } - - s >> hash; -} - -FrameInfo::FrameInfo (FILE* f) -{ -#ifdef LIBDCP_WINDOWS - fscanf (f, "%I64u", &offset); - fscanf (f, "%I64u", &size); -#else - fscanf (f, "%" SCNu64, &offset); - fscanf (f, "%" SCNu64, &size); -#endif - - if (ferror (f)) { - offset = size = 0; - } - - char hash_buffer[128]; - fscanf (f, "%s", hash_buffer); - hash = hash_buffer; -} - -void -FrameInfo::write (ostream& s) const -{ - s << offset << " " << size << " " << hash; -} - -void -FrameInfo::write (FILE* f) const -{ -#ifdef LIBDCP_WINDOWS - fprintf (f, "%I64u %I64u %s", offset, size, hash.c_str ()); -#else - fprintf (f, "%" PRIu64 " %" PRIu64 " %s", offset, size, hash.c_str ()); -#endif -} - - -PictureAssetWriter::PictureAssetWriter (PictureAsset* asset, bool overwrite) - : _asset (asset) - , _frames_written (0) - , _started (false) - , _finalized (false) - , _overwrite (overwrite) -{ - -} diff --git a/src/picture_asset_writer.h b/src/picture_asset_writer.h deleted file mode 100644 index c6c00d87..00000000 --- a/src/picture_asset_writer.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include -#include -#include -#include -#include "metadata.h" -#include "types.h" - -namespace dcp { - -class PictureAsset; - -struct FrameInfo -{ - FrameInfo (uint64_t o, uint64_t s, std::string h) - : offset (o) - , size (s) - , hash (h) - {} - - FrameInfo (std::istream& s); - FrameInfo (FILE *); - - void write (std::ostream& s) const; - void write (FILE *) const; - - uint64_t offset; - uint64_t size; - std::string hash; -}; - -class PictureAssetWriter : public boost::noncopyable -{ -public: - virtual ~PictureAssetWriter () {} - virtual FrameInfo write (uint8_t *, int) = 0; - virtual void finalize () = 0; - virtual void fake_write (int) = 0; - -protected: - template - friend void start (PictureAssetWriter *, boost::shared_ptr

, Q *, uint8_t *, int); - - PictureAssetWriter (PictureAsset *, bool); - - PictureAsset* _asset; - - /** Number of picture frames written to the asset so far. For stereo assets - * this will be incremented for each eye (i.e. there will be twice the number - * of frames as in a mono asset). - */ - int _frames_written; - bool _started; - /** true if finalize() has been called */ - bool _finalized; - bool _overwrite; -}; - -} diff --git a/src/picture_asset_writer_common.cc b/src/picture_asset_writer_common.cc deleted file mode 100644 index bb1316c7..00000000 --- a/src/picture_asset_writer_common.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -using boost::shared_ptr; - -struct ASDCPStateBase -{ - ASDCPStateBase () - : frame_buffer (4 * Kumu::Megabyte) - {} - - ASDCP::JP2K::CodestreamParser j2k_parser; - ASDCP::JP2K::FrameBuffer frame_buffer; - ASDCP::WriterInfo writer_info; - ASDCP::JP2K::PictureDescriptor picture_descriptor; - ASDCP::AESEncContext* encryption_context; -}; - -template -void dcp::start (PictureAssetWriter* writer, shared_ptr

state, Q* asset, uint8_t* data, int size) -{ - if (ASDCP_FAILURE (state->j2k_parser.OpenReadFrame (data, size, state->frame_buffer))) { - boost::throw_exception (MiscError ("could not parse J2K frame")); - } - - state->j2k_parser.FillPictureDescriptor (state->picture_descriptor); - state->picture_descriptor.EditRate = ASDCP::Rational (asset->edit_rate(), 1); - - asset->fill_writer_info (&state->writer_info); - - Kumu::Result_t r = state->mxf_writer.OpenWrite ( - asset->path().string().c_str(), - state->writer_info, - state->picture_descriptor, - 16384, - writer->_overwrite - ); - - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for writing", asset->path().string(), r)); - } - - writer->_started = true; -} diff --git a/src/picture_mxf.cc b/src/picture_mxf.cc new file mode 100644 index 00000000..3221a88f --- /dev/null +++ b/src/picture_mxf.cc @@ -0,0 +1,211 @@ +/* + Copyright (C) 2012 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/picture_asset.cc + * @brief An asset made up of JPEG2000 files + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "AS_DCP.h" +#include "KM_fileio.h" +#include "picture_mxf.h" +#include "util.h" +#include "exceptions.h" +#include "xyz_frame.h" +#include "picture_mxf_writer.h" + +using std::string; +using std::ostream; +using std::list; +using std::vector; +using std::max; +using std::stringstream; +using std::pair; +using std::make_pair; +using std::istream; +using std::cout; +using boost::shared_ptr; +using boost::dynamic_pointer_cast; +using boost::lexical_cast; +using namespace dcp; + +PictureMXF::PictureMXF (boost::filesystem::path directory, boost::filesystem::path mxf_name) + : MXF (directory, mxf_name) +{ + +} + +void +PictureMXF::write_to_cpl (xmlpp::Element* node) const +{ + MXF::write_to_cpl (node); + + xmlpp::Node::NodeList c = node->get_children (); + xmlpp::Node::NodeList::iterator i = c.begin(); + while (i != c.end() && (*i)->get_name() != cpl_node_name ()) { + ++i; + } + + assert (i != c.end ()); + + (*i)->add_child ("FrameRate")->add_child_text (lexical_cast (_edit_rate * edit_rate_factor ()) + " 1"); + if (_interop) { + stringstream s; + s << std::fixed << std::setprecision (2) << (float (_size.width) / _size.height); + (*i)->add_child ("ScreenAspectRatio")->add_child_text (s.str ()); + } else { + (*i)->add_child ("ScreenAspectRatio")->add_child_text (lexical_cast (_size.width) + " " + lexical_cast (_size.height)); + } +} + +bool +PictureMXF::descriptor_equals ( + ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, boost::function note + ) const +{ + if ( + a.EditRate != b.EditRate || + a.SampleRate != b.SampleRate || + a.StoredWidth != b.StoredWidth || + a.StoredHeight != b.StoredHeight || + a.AspectRatio != b.AspectRatio || + a.Rsize != b.Rsize || + a.Xsize != b.Xsize || + a.Ysize != b.Ysize || + a.XOsize != b.XOsize || + a.YOsize != b.YOsize || + a.XTsize != b.XTsize || + a.YTsize != b.YTsize || + a.XTOsize != b.XTOsize || + a.YTOsize != b.YTOsize || + a.Csize != b.Csize +// a.CodingStyleDefault != b.CodingStyleDefault || +// a.QuantizationDefault != b.QuantizationDefault + ) { + + note (ERROR, "video MXF picture descriptors differ"); + return false; + } + + if (a.ContainerDuration != b.ContainerDuration) { + note (ERROR, "video container durations differ"); + } + +// for (unsigned int j = 0; j < ASDCP::JP2K::MaxComponents; ++j) { +// if (a.ImageComponents[j] != b.ImageComponents[j]) { +// notes.pack_start ("video MXF picture descriptors differ"); +// } +// } + + return true; +} + +bool +PictureMXF::frame_buffer_equals ( + int frame, EqualityOptions opt, boost::function note, + uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B + ) const +{ + if (size_A == size_B && memcmp (data_A, data_B, size_A) == 0) { + note (NOTE, "J2K identical"); + /* Easy result; the J2K data is identical */ + return true; + } + + /* Decompress the images to bitmaps */ + shared_ptr image_A = decompress_j2k (const_cast (data_A), size_A, 0); + shared_ptr image_B = decompress_j2k (const_cast (data_B), size_B, 0); + + /* Compare them */ + + vector abs_diffs (image_A->size().width * image_A->size().height * 3); + int d = 0; + int max_diff = 0; + + for (int c = 0; c < 3; ++c) { + + if (image_A->size() != image_B->size()) { + note (ERROR, "image sizes for frame " + lexical_cast(frame) + " differ"); + return false; + } + + int const pixels = image_A->size().width * image_A->size().height; + for (int j = 0; j < pixels; ++j) { + int const t = abs (image_A->data(c)[j] - image_B->data(c)[j]); + abs_diffs[d++] = t; + max_diff = max (max_diff, t); + } + } + + uint64_t total = 0; + for (vector::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) { + total += *j; + } + + double const mean = double (total) / abs_diffs.size (); + + uint64_t total_squared_deviation = 0; + for (vector::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) { + total_squared_deviation += pow (*j - mean, 2); + } + + double const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size()); + + note (NOTE, "mean difference " + lexical_cast (mean) + ", deviation " + lexical_cast (std_dev)); + + if (mean > opt.max_mean_pixel_error) { + note ( + ERROR, + "mean " + lexical_cast(mean) + + " out of range " + lexical_cast(opt.max_mean_pixel_error) + + " in frame " + lexical_cast(frame) + ); + + return false; + } + + if (std_dev > opt.max_std_dev_pixel_error) { + note ( + ERROR, + "standard deviation " + lexical_cast(std_dev) + + " out of range " + lexical_cast(opt.max_std_dev_pixel_error) + + " in frame " + lexical_cast(frame) + ); + + return false; + } + + return true; +} + +string +PictureMXF::key_type () const +{ + return "MDIK"; +} + + + diff --git a/src/picture_mxf.h b/src/picture_mxf.h new file mode 100644 index 00000000..1966ee8e --- /dev/null +++ b/src/picture_mxf.h @@ -0,0 +1,103 @@ +/* + Copyright (C) 2012 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_PICTURE_MXF_H +#define LIBDCP_PICTURE_MXF_H + +/** @file src/picture_asset.h + * @brief An asset made up of JPEG2000 data + */ + +#include +#include "mxf.h" +#include "util.h" +#include "metadata.h" + +namespace ASDCP { + namespace JP2K { + class PictureDescriptor; + } +} + +namespace dcp +{ + +class MonoPictureFrame; +class StereoPictureFrame; +class PictureMXFWriter; + +/** @brief An asset made up of JPEG2000 data */ +class PictureMXF : public MXF +{ +public: + /** Construct a PictureAsset. + * + * @param directory Directory where MXF file is. + * @param mxf_name Name of MXF file. + */ + PictureMXF (boost::filesystem::path directory, boost::filesystem::path mxf_name); + + /** Start a progressive write to this asset. + * The following parameters must be set up (if required) before calling this: + * Interop mode (set_interop) + * Edit rate (set_edit_rate) + * MXF Metadata (set_metadata) + * + * @param overwrite true to overwrite an existing MXF file; in this mode, writing can be resumed to a partially-written MXF; false if the + * MXF file does not exist. + */ + virtual boost::shared_ptr start_write (bool overwrite) = 0; + + virtual void read () = 0; + virtual void create (std::vector const &) {} + virtual void create (boost::function) {} + + Size size () const { + return _size; + } + + void set_size (Size s) { + _size = s; + } + + void write_to_cpl (xmlpp::Element *) const; + +protected: + + bool frame_buffer_equals ( + int frame, EqualityOptions opt, boost::function note, + uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B + ) const; + + bool descriptor_equals ( + ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, boost::function + ) const; + + /** picture size in pixels */ + Size _size; + +private: + std::string key_type () const; + virtual int edit_rate_factor () const = 0; +}; + + +} + +#endif diff --git a/src/picture_mxf_writer.cc b/src/picture_mxf_writer.cc new file mode 100644 index 00000000..61dd854b --- /dev/null +++ b/src/picture_mxf_writer.cc @@ -0,0 +1,94 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include "AS_DCP.h" +#include "KM_fileio.h" +#include "picture_mxf_writer.h" +#include "exceptions.h" +#include "picture_mxf.h" + +using std::istream; +using std::ostream; +using std::string; +using boost::shared_ptr; +using namespace dcp; + +FrameInfo::FrameInfo (istream& s) + : offset (0) + , size (0) +{ + s >> offset >> size; + + if (!s.good ()) { + /* Make sure we zero these if something bad happened, otherwise + the caller might try to alloc lots of RAM. + */ + offset = size = 0; + } + + s >> hash; +} + +FrameInfo::FrameInfo (FILE* f) +{ +#ifdef LIBDCP_WINDOWS + fscanf (f, "%I64u", &offset); + fscanf (f, "%I64u", &size); +#else + fscanf (f, "%" SCNu64, &offset); + fscanf (f, "%" SCNu64, &size); +#endif + + if (ferror (f)) { + offset = size = 0; + } + + char hash_buffer[128]; + fscanf (f, "%s", hash_buffer); + hash = hash_buffer; +} + +void +FrameInfo::write (ostream& s) const +{ + s << offset << " " << size << " " << hash; +} + +void +FrameInfo::write (FILE* f) const +{ +#ifdef LIBDCP_WINDOWS + fprintf (f, "%I64u %I64u %s", offset, size, hash.c_str ()); +#else + fprintf (f, "%" PRIu64 " %" PRIu64 " %s", offset, size, hash.c_str ()); +#endif +} + + +PictureMXFWriter::PictureMXFWriter (PictureMXF* asset, bool overwrite) + : _asset (asset) + , _frames_written (0) + , _started (false) + , _finalized (false) + , _overwrite (overwrite) +{ + +} diff --git a/src/picture_mxf_writer.h b/src/picture_mxf_writer.h new file mode 100644 index 00000000..3d10288f --- /dev/null +++ b/src/picture_mxf_writer.h @@ -0,0 +1,78 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include "metadata.h" +#include "types.h" + +namespace dcp { + +class PictureMXF; + +struct FrameInfo +{ + FrameInfo (uint64_t o, uint64_t s, std::string h) + : offset (o) + , size (s) + , hash (h) + {} + + FrameInfo (std::istream& s); + FrameInfo (FILE *); + + void write (std::ostream& s) const; + void write (FILE *) const; + + uint64_t offset; + uint64_t size; + std::string hash; +}; + +class PictureMXFWriter : public boost::noncopyable +{ +public: + virtual ~PictureMXFWriter () {} + virtual FrameInfo write (uint8_t *, int) = 0; + virtual void finalize () = 0; + virtual void fake_write (int) = 0; + +protected: + template + friend void start (PictureMXFWriter *, boost::shared_ptr

, Q *, uint8_t *, int); + + PictureMXFWriter (PictureMXF *, bool); + + PictureMXF* _asset; + + /** Number of picture frames written to the asset so far. For stereo assets + * this will be incremented for each eye (i.e. there will be twice the number + * of frames as in a mono asset). + */ + int _frames_written; + bool _started; + /** true if finalize() has been called */ + bool _finalized; + bool _overwrite; +}; + +} diff --git a/src/picture_mxf_writer_common.cc b/src/picture_mxf_writer_common.cc new file mode 100644 index 00000000..07a0964a --- /dev/null +++ b/src/picture_mxf_writer_common.cc @@ -0,0 +1,60 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +using boost::shared_ptr; + +struct ASDCPStateBase +{ + ASDCPStateBase () + : frame_buffer (4 * Kumu::Megabyte) + {} + + ASDCP::JP2K::CodestreamParser j2k_parser; + ASDCP::JP2K::FrameBuffer frame_buffer; + ASDCP::WriterInfo writer_info; + ASDCP::JP2K::PictureDescriptor picture_descriptor; + ASDCP::AESEncContext* encryption_context; +}; + +template +void dcp::start (PictureMXFWriter* writer, shared_ptr

state, Q* asset, uint8_t* data, int size) +{ + if (ASDCP_FAILURE (state->j2k_parser.OpenReadFrame (data, size, state->frame_buffer))) { + boost::throw_exception (MiscError ("could not parse J2K frame")); + } + + state->j2k_parser.FillPictureDescriptor (state->picture_descriptor); + state->picture_descriptor.EditRate = ASDCP::Rational (asset->edit_rate(), 1); + + asset->fill_writer_info (&state->writer_info); + + Kumu::Result_t r = state->mxf_writer.OpenWrite ( + asset->path().string().c_str(), + state->writer_info, + state->picture_descriptor, + 16384, + writer->_overwrite + ); + + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for writing", asset->path().string(), r)); + } + + writer->_started = true; +} diff --git a/src/reel.cc b/src/reel.cc index 7a54059e..9f514d88 100644 --- a/src/reel.cc +++ b/src/reel.cc @@ -20,10 +20,10 @@ #include #include "reel.h" #include "util.h" -#include "picture_asset.h" -#include "mono_picture_asset.h" -#include "stereo_picture_asset.h" -#include "sound_asset.h" +#include "picture_mxf.h" +#include "mono_picture_mxf.h" +#include "stereo_picture_mxf.h" +#include "sound_mxf.h" #include "subtitle_asset.h" #include "kdm.h" @@ -41,7 +41,7 @@ Reel::write_to_cpl (xmlpp::Element* node) const reel->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid()); xmlpp::Element* asset_list = reel->add_child ("AssetList"); - if (_main_picture && dynamic_pointer_cast (_main_picture)) { + if (_main_picture && dynamic_pointer_cast (_main_picture)) { /* Mono pictures come before other stuff... */ _main_picture->write_to_cpl (asset_list); } @@ -54,7 +54,7 @@ Reel::write_to_cpl (xmlpp::Element* node) const _main_subtitle->write_to_cpl (asset_list); } - if (_main_picture && dynamic_pointer_cast (_main_picture)) { + if (_main_picture && dynamic_pointer_cast (_main_picture)) { /* ... but stereo pictures must come after */ _main_picture->write_to_cpl (asset_list); } diff --git a/src/reel.h b/src/reel.h index 666e235e..41e811e4 100644 --- a/src/reel.h +++ b/src/reel.h @@ -33,8 +33,8 @@ namespace xmlpp { namespace dcp { -class PictureAsset; -class SoundAsset; +class PictureMXF; +class SoundMXF; class SubtitleAsset; class KDM; @@ -43,8 +43,8 @@ class Reel { public: Reel ( - boost::shared_ptr picture, - boost::shared_ptr sound, + boost::shared_ptr picture, + boost::shared_ptr sound, boost::shared_ptr subtitle ) : _main_picture (picture) @@ -52,11 +52,11 @@ public: , _main_subtitle (subtitle) {} - boost::shared_ptr main_picture () const { + boost::shared_ptr main_picture () const { return _main_picture; } - boost::shared_ptr main_sound () const { + boost::shared_ptr main_sound () const { return _main_sound; } @@ -75,8 +75,8 @@ public: void add_kdm (KDM const &); private: - boost::shared_ptr _main_picture; - boost::shared_ptr _main_sound; + boost::shared_ptr _main_picture; + boost::shared_ptr _main_sound; boost::shared_ptr _main_subtitle; }; diff --git a/src/sound_asset.cc b/src/sound_asset.cc deleted file mode 100644 index 5a1ce06b..00000000 --- a/src/sound_asset.cc +++ /dev/null @@ -1,269 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/sound_asset.cc - * @brief An asset made up of WAV files - */ - -#include -#include -#include -#include -#include -#include "KM_fileio.h" -#include "AS_DCP.h" -#include "sound_asset.h" -#include "util.h" -#include "exceptions.h" -#include "sound_frame.h" - -using std::string; -using std::stringstream; -using std::ostream; -using std::vector; -using std::list; -using boost::shared_ptr; -using boost::lexical_cast; -using namespace dcp; - -SoundAsset::SoundAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name) - : MXFAsset (directory, mxf_name) - , _channels (0) - , _sampling_rate (0) -{ - -} - -void -SoundAsset::read () -{ - ASDCP::PCM::MXFReader reader; - Kumu::Result_t r = reader.OpenRead (path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::PCM::AudioDescriptor desc; - if (ASDCP_FAILURE (reader.FillAudioDescriptor (desc))) { - boost::throw_exception (DCPReadError ("could not read audio MXF information")); - } - - _sampling_rate = desc.AudioSamplingRate.Numerator / desc.AudioSamplingRate.Denominator; - _channels = desc.ChannelCount; - _edit_rate = desc.EditRate.Numerator; - assert (desc.EditRate.Denominator == 1); - _intrinsic_duration = desc.ContainerDuration; -} - -string -SoundAsset::cpl_node_name () const -{ - return "MainSound"; -} - -bool -SoundAsset::equals (shared_ptr other, EqualityOptions opt, boost::function note) const -{ - if (!MXFAsset::equals (other, opt, note)) { - return false; - } - - ASDCP::PCM::MXFReader reader_A; - Kumu::Result_t r = reader_A.OpenRead (path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::PCM::MXFReader reader_B; - r = reader_B.OpenRead (other->path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::PCM::AudioDescriptor desc_A; - if (ASDCP_FAILURE (reader_A.FillAudioDescriptor (desc_A))) { - boost::throw_exception (DCPReadError ("could not read audio MXF information")); - } - ASDCP::PCM::AudioDescriptor desc_B; - if (ASDCP_FAILURE (reader_B.FillAudioDescriptor (desc_B))) { - boost::throw_exception (DCPReadError ("could not read audio MXF information")); - } - - if ( - desc_A.EditRate != desc_B.EditRate || - desc_A.AudioSamplingRate != desc_B.AudioSamplingRate || - desc_A.Locked != desc_B.Locked || - desc_A.ChannelCount != desc_B.ChannelCount || - desc_A.QuantizationBits != desc_B.QuantizationBits || - desc_A.BlockAlign != desc_B.BlockAlign || - desc_A.AvgBps != desc_B.AvgBps || - desc_A.LinkedTrackID != desc_B.LinkedTrackID || - desc_A.ContainerDuration != desc_B.ContainerDuration -// desc_A.ChannelFormat != desc_B.ChannelFormat || - ) { - - note (ERROR, "audio MXF picture descriptors differ"); - return false; - } - - ASDCP::PCM::FrameBuffer buffer_A (1 * Kumu::Megabyte); - ASDCP::PCM::FrameBuffer buffer_B (1 * Kumu::Megabyte); - - for (int i = 0; i < _intrinsic_duration; ++i) { - if (ASDCP_FAILURE (reader_A.ReadFrame (i, buffer_A))) { - boost::throw_exception (DCPReadError ("could not read audio frame")); - } - - if (ASDCP_FAILURE (reader_B.ReadFrame (i, buffer_B))) { - boost::throw_exception (DCPReadError ("could not read audio frame")); - } - - if (buffer_A.Size() != buffer_B.Size()) { - note (ERROR, "sizes of audio data for frame " + lexical_cast(i) + " differ"); - return false; - } - - if (memcmp (buffer_A.RoData(), buffer_B.RoData(), buffer_A.Size()) != 0) { - for (uint32_t i = 0; i < buffer_A.Size(); ++i) { - int const d = abs (buffer_A.RoData()[i] - buffer_B.RoData()[i]); - if (d > opt.max_audio_sample_error) { - note (ERROR, "PCM data difference of " + lexical_cast (d)); - return false; - } - } - } - } - - return true; -} - -shared_ptr -SoundAsset::get_frame (int n) const -{ - /* XXX: should add on entry point here? */ - return shared_ptr (new SoundFrame (path().string(), n, _decryption_context)); -} - -shared_ptr -SoundAsset::start_write () -{ - /* XXX: can't we use a shared_ptr here? */ - return shared_ptr (new SoundAssetWriter (this)); -} - -struct SoundAssetWriter::ASDCPState -{ - ASDCP::PCM::MXFWriter mxf_writer; - ASDCP::PCM::FrameBuffer frame_buffer; - ASDCP::WriterInfo writer_info; - ASDCP::PCM::AudioDescriptor audio_desc; - ASDCP::AESEncContext* encryption_context; -}; - -SoundAssetWriter::SoundAssetWriter (SoundAsset* a) - : _state (new SoundAssetWriter::ASDCPState) - , _asset (a) - , _finalized (false) - , _frames_written (0) - , _frame_buffer_offset (0) -{ - _state->encryption_context = a->encryption_context (); - - /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */ - _state->audio_desc.EditRate = ASDCP::Rational (_asset->edit_rate(), 1); - _state->audio_desc.AudioSamplingRate = ASDCP::Rational (_asset->sampling_rate(), 1); - _state->audio_desc.Locked = 0; - _state->audio_desc.ChannelCount = _asset->channels (); - _state->audio_desc.QuantizationBits = 24; - _state->audio_desc.BlockAlign = 3 * _asset->channels(); - _state->audio_desc.AvgBps = _asset->sampling_rate() * _state->audio_desc.BlockAlign; - _state->audio_desc.LinkedTrackID = 0; - _state->audio_desc.ChannelFormat = ASDCP::PCM::CF_NONE; - - _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc)); - _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc)); - memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity()); - - _asset->fill_writer_info (&_state->writer_info); - - Kumu::Result_t r = _state->mxf_writer.OpenWrite (_asset->path().string().c_str(), _state->writer_info, _state->audio_desc); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (FileError ("could not open audio MXF for writing", _asset->path().string(), r)); - } -} - -void -SoundAssetWriter::write (float const * const * data, int frames) -{ - for (int i = 0; i < frames; ++i) { - - byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset; - - /* Write one sample per channel */ - for (int j = 0; j < _asset->channels(); ++j) { - int32_t const s = data[j][i] * (1 << 23); - *out++ = (s & 0xff); - *out++ = (s & 0xff00) >> 8; - *out++ = (s & 0xff0000) >> 16; - } - _frame_buffer_offset += 3 * _asset->channels(); - - assert (_frame_buffer_offset <= int (_state->frame_buffer.Capacity())); - - /* Finish the MXF frame if required */ - if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) { - write_current_frame (); - _frame_buffer_offset = 0; - memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity()); - } - } -} - -void -SoundAssetWriter::write_current_frame () -{ - ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MiscError ("could not write audio MXF frame (" + lexical_cast (int (r)) + ")")); - } - - ++_frames_written; -} - -void -SoundAssetWriter::finalize () -{ - if (_frame_buffer_offset > 0) { - write_current_frame (); - } - - if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) { - boost::throw_exception (MiscError ("could not finalise audio MXF")); - } - - _finalized = true; - _asset->set_intrinsic_duration (_frames_written); - _asset->set_duration (_frames_written); -} - -string -SoundAsset::key_type () const -{ - return "MDAK"; -} diff --git a/src/sound_asset.h b/src/sound_asset.h deleted file mode 100644 index ce08c512..00000000 --- a/src/sound_asset.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef LIBDCP_SOUND_ASSET_H -#define LIBDCP_SOUND_ASSET_H - -/** @file src/sound_asset.h - * @brief An asset made up of PCM audio data files - */ - -#include "mxf_asset.h" -#include "types.h" -#include "metadata.h" - -namespace dcp -{ - -class SoundFrame; -class SoundAsset; - -class SoundAssetWriter -{ -public: - void write (float const * const *, int); - void finalize (); - -private: - friend class SoundAsset; - - SoundAssetWriter (SoundAsset *); - - /* no copy construction */ - SoundAssetWriter (SoundAssetWriter const &); - SoundAssetWriter& operator= (SoundAssetWriter const &); - - void write_current_frame (); - - /* do this with an opaque pointer so we don't have to include - ASDCP headers - */ - - struct ASDCPState; - boost::shared_ptr _state; - - SoundAsset* _asset; - bool _finalized; - int _frames_written; - int _frame_buffer_offset; -}; - -/** @brief An asset made up of WAV files */ -class SoundAsset : public MXFAsset -{ -public: - SoundAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name); - - void read (); - - boost::shared_ptr start_write (); - - bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; - - boost::shared_ptr get_frame (int n) const; - - void set_channels (int c) { - _channels = c; - } - - int channels () const { - return _channels; - } - - void set_sampling_rate (int s) { - _sampling_rate = s; - } - - int sampling_rate () const { - return _sampling_rate; - } - -private: - std::string key_type () const; - void construct (boost::function get_path); - std::string cpl_node_name () const; - - /** Number of channels in the asset */ - int _channels; - int _sampling_rate; -}; - -} - -#endif diff --git a/src/sound_mxf.cc b/src/sound_mxf.cc new file mode 100644 index 00000000..b569c57a --- /dev/null +++ b/src/sound_mxf.cc @@ -0,0 +1,269 @@ +/* + Copyright (C) 2012 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/sound_asset.cc + * @brief An asset made up of WAV files + */ + +#include +#include +#include +#include +#include +#include "KM_fileio.h" +#include "AS_DCP.h" +#include "sound_mxf.h" +#include "util.h" +#include "exceptions.h" +#include "sound_frame.h" + +using std::string; +using std::stringstream; +using std::ostream; +using std::vector; +using std::list; +using boost::shared_ptr; +using boost::lexical_cast; +using namespace dcp; + +SoundMXF::SoundMXF (boost::filesystem::path directory, boost::filesystem::path mxf_name) + : MXF (directory, mxf_name) + , _channels (0) + , _sampling_rate (0) +{ + +} + +void +SoundMXF::read () +{ + ASDCP::PCM::MXFReader reader; + Kumu::Result_t r = reader.OpenRead (path().string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); + } + + ASDCP::PCM::AudioDescriptor desc; + if (ASDCP_FAILURE (reader.FillAudioDescriptor (desc))) { + boost::throw_exception (DCPReadError ("could not read audio MXF information")); + } + + _sampling_rate = desc.AudioSamplingRate.Numerator / desc.AudioSamplingRate.Denominator; + _channels = desc.ChannelCount; + _edit_rate = desc.EditRate.Numerator; + assert (desc.EditRate.Denominator == 1); + _intrinsic_duration = desc.ContainerDuration; +} + +string +SoundMXF::cpl_node_name () const +{ + return "MainSound"; +} + +bool +SoundMXF::equals (shared_ptr other, EqualityOptions opt, boost::function note) const +{ + if (!MXF::equals (other, opt, note)) { + return false; + } + + ASDCP::PCM::MXFReader reader_A; + Kumu::Result_t r = reader_A.OpenRead (path().string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); + } + + ASDCP::PCM::MXFReader reader_B; + r = reader_B.OpenRead (other->path().string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); + } + + ASDCP::PCM::AudioDescriptor desc_A; + if (ASDCP_FAILURE (reader_A.FillAudioDescriptor (desc_A))) { + boost::throw_exception (DCPReadError ("could not read audio MXF information")); + } + ASDCP::PCM::AudioDescriptor desc_B; + if (ASDCP_FAILURE (reader_B.FillAudioDescriptor (desc_B))) { + boost::throw_exception (DCPReadError ("could not read audio MXF information")); + } + + if ( + desc_A.EditRate != desc_B.EditRate || + desc_A.AudioSamplingRate != desc_B.AudioSamplingRate || + desc_A.Locked != desc_B.Locked || + desc_A.ChannelCount != desc_B.ChannelCount || + desc_A.QuantizationBits != desc_B.QuantizationBits || + desc_A.BlockAlign != desc_B.BlockAlign || + desc_A.AvgBps != desc_B.AvgBps || + desc_A.LinkedTrackID != desc_B.LinkedTrackID || + desc_A.ContainerDuration != desc_B.ContainerDuration +// desc_A.ChannelFormat != desc_B.ChannelFormat || + ) { + + note (ERROR, "audio MXF picture descriptors differ"); + return false; + } + + ASDCP::PCM::FrameBuffer buffer_A (1 * Kumu::Megabyte); + ASDCP::PCM::FrameBuffer buffer_B (1 * Kumu::Megabyte); + + for (int i = 0; i < _intrinsic_duration; ++i) { + if (ASDCP_FAILURE (reader_A.ReadFrame (i, buffer_A))) { + boost::throw_exception (DCPReadError ("could not read audio frame")); + } + + if (ASDCP_FAILURE (reader_B.ReadFrame (i, buffer_B))) { + boost::throw_exception (DCPReadError ("could not read audio frame")); + } + + if (buffer_A.Size() != buffer_B.Size()) { + note (ERROR, "sizes of audio data for frame " + lexical_cast(i) + " differ"); + return false; + } + + if (memcmp (buffer_A.RoData(), buffer_B.RoData(), buffer_A.Size()) != 0) { + for (uint32_t i = 0; i < buffer_A.Size(); ++i) { + int const d = abs (buffer_A.RoData()[i] - buffer_B.RoData()[i]); + if (d > opt.max_audio_sample_error) { + note (ERROR, "PCM data difference of " + lexical_cast (d)); + return false; + } + } + } + } + + return true; +} + +shared_ptr +SoundMXF::get_frame (int n) const +{ + /* XXX: should add on entry point here? */ + return shared_ptr (new SoundFrame (path().string(), n, _decryption_context)); +} + +shared_ptr +SoundMXF::start_write () +{ + /* XXX: can't we use a shared_ptr here? */ + return shared_ptr (new SoundMXFWriter (this)); +} + +struct SoundMXFWriter::ASDCPState +{ + ASDCP::PCM::MXFWriter mxf_writer; + ASDCP::PCM::FrameBuffer frame_buffer; + ASDCP::WriterInfo writer_info; + ASDCP::PCM::AudioDescriptor audio_desc; + ASDCP::AESEncContext* encryption_context; +}; + +SoundMXFWriter::SoundMXFWriter (SoundMXF* a) + : _state (new SoundMXFWriter::ASDCPState) + , _asset (a) + , _finalized (false) + , _frames_written (0) + , _frame_buffer_offset (0) +{ + _state->encryption_context = a->encryption_context (); + + /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */ + _state->audio_desc.EditRate = ASDCP::Rational (_asset->edit_rate(), 1); + _state->audio_desc.AudioSamplingRate = ASDCP::Rational (_asset->sampling_rate(), 1); + _state->audio_desc.Locked = 0; + _state->audio_desc.ChannelCount = _asset->channels (); + _state->audio_desc.QuantizationBits = 24; + _state->audio_desc.BlockAlign = 3 * _asset->channels(); + _state->audio_desc.AvgBps = _asset->sampling_rate() * _state->audio_desc.BlockAlign; + _state->audio_desc.LinkedTrackID = 0; + _state->audio_desc.ChannelFormat = ASDCP::PCM::CF_NONE; + + _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc)); + _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc)); + memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity()); + + _asset->fill_writer_info (&_state->writer_info); + + Kumu::Result_t r = _state->mxf_writer.OpenWrite (_asset->path().string().c_str(), _state->writer_info, _state->audio_desc); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (FileError ("could not open audio MXF for writing", _asset->path().string(), r)); + } +} + +void +SoundMXFWriter::write (float const * const * data, int frames) +{ + for (int i = 0; i < frames; ++i) { + + byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset; + + /* Write one sample per channel */ + for (int j = 0; j < _asset->channels(); ++j) { + int32_t const s = data[j][i] * (1 << 23); + *out++ = (s & 0xff); + *out++ = (s & 0xff00) >> 8; + *out++ = (s & 0xff0000) >> 16; + } + _frame_buffer_offset += 3 * _asset->channels(); + + assert (_frame_buffer_offset <= int (_state->frame_buffer.Capacity())); + + /* Finish the MXF frame if required */ + if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) { + write_current_frame (); + _frame_buffer_offset = 0; + memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity()); + } + } +} + +void +SoundMXFWriter::write_current_frame () +{ + ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MiscError ("could not write audio MXF frame (" + lexical_cast (int (r)) + ")")); + } + + ++_frames_written; +} + +void +SoundMXFWriter::finalize () +{ + if (_frame_buffer_offset > 0) { + write_current_frame (); + } + + if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) { + boost::throw_exception (MiscError ("could not finalise audio MXF")); + } + + _finalized = true; + _asset->set_intrinsic_duration (_frames_written); + _asset->set_duration (_frames_written); +} + +string +SoundMXF::key_type () const +{ + return "MDAK"; +} diff --git a/src/sound_mxf.h b/src/sound_mxf.h new file mode 100644 index 00000000..eec3fab6 --- /dev/null +++ b/src/sound_mxf.h @@ -0,0 +1,109 @@ +/* + Copyright (C) 2012 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_SOUND_ASSET_H +#define LIBDCP_SOUND_ASSET_H + +/** @file src/sound_asset.h + * @brief An asset made up of PCM audio data files + */ + +#include "mxf.h" +#include "types.h" +#include "metadata.h" + +namespace dcp +{ + +class SoundFrame; +class SoundMXF; + +class SoundMXFWriter +{ +public: + void write (float const * const *, int); + void finalize (); + +private: + friend class SoundMXF; + + SoundMXFWriter (SoundMXF *); + + /* no copy construction */ + SoundMXFWriter (SoundMXFWriter const &); + SoundMXFWriter& operator= (SoundMXFWriter const &); + + void write_current_frame (); + + /* do this with an opaque pointer so we don't have to include + ASDCP headers + */ + + struct ASDCPState; + boost::shared_ptr _state; + + SoundMXF* _asset; + bool _finalized; + int _frames_written; + int _frame_buffer_offset; +}; + +/** @brief An asset made up of WAV files */ +class SoundMXF : public MXF +{ +public: + SoundMXF (boost::filesystem::path directory, boost::filesystem::path mxf_name); + + void read (); + + boost::shared_ptr start_write (); + + bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; + + boost::shared_ptr get_frame (int n) const; + + void set_channels (int c) { + _channels = c; + } + + int channels () const { + return _channels; + } + + void set_sampling_rate (int s) { + _sampling_rate = s; + } + + int sampling_rate () const { + return _sampling_rate; + } + +private: + std::string key_type () const; + void construct (boost::function get_path); + std::string cpl_node_name () const; + + /** Number of channels in the asset */ + int _channels; + int _sampling_rate; +}; + +} + +#endif diff --git a/src/stereo_picture_asset.cc b/src/stereo_picture_asset.cc deleted file mode 100644 index 711a8090..00000000 --- a/src/stereo_picture_asset.cc +++ /dev/null @@ -1,150 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "AS_DCP.h" -#include "stereo_picture_asset.h" -#include "stereo_picture_frame.h" -#include "exceptions.h" -#include "stereo_picture_asset_writer.h" - -using std::string; -using std::pair; -using std::make_pair; -using boost::shared_ptr; -using boost::dynamic_pointer_cast; -using namespace dcp; - -bool -StereoPictureAsset::equals (shared_ptr other, EqualityOptions opt, boost::function note) const -{ - if (!MXFAsset::equals (other, opt, note)) { - return false; - } - - ASDCP::JP2K::MXFSReader reader_A; - Kumu::Result_t r = reader_A.OpenRead (path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::JP2K::MXFSReader reader_B; - r = reader_B.OpenRead (other->path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::JP2K::PictureDescriptor desc_A; - if (ASDCP_FAILURE (reader_A.FillPictureDescriptor (desc_A))) { - boost::throw_exception (DCPReadError ("could not read video MXF information")); - } - ASDCP::JP2K::PictureDescriptor desc_B; - if (ASDCP_FAILURE (reader_B.FillPictureDescriptor (desc_B))) { - boost::throw_exception (DCPReadError ("could not read video MXF information")); - } - - if (!descriptor_equals (desc_A, desc_B, note)) { - return false; - } - - shared_ptr other_picture = dynamic_pointer_cast (other); - assert (other_picture); - - for (int i = 0; i < _intrinsic_duration; ++i) { - shared_ptr frame_A = get_frame (i); - shared_ptr frame_B = other_picture->get_frame (i); - - if (!frame_buffer_equals ( - i, opt, note, - frame_A->left_j2k_data(), frame_A->left_j2k_size(), - frame_B->left_j2k_data(), frame_B->left_j2k_size() - )) { - return false; - } - - if (!frame_buffer_equals ( - i, opt, note, - frame_A->right_j2k_data(), frame_A->right_j2k_size(), - frame_B->right_j2k_data(), frame_B->right_j2k_size() - )) { - return false; - } - } - - return true; -} - -StereoPictureAsset::StereoPictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name) - : PictureAsset (directory, mxf_name) -{ - -} - -void -StereoPictureAsset::read () -{ - ASDCP::JP2K::MXFSReader reader; - Kumu::Result_t r = reader.OpenRead (path().string().c_str()); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); - } - - ASDCP::JP2K::PictureDescriptor desc; - if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) { - boost::throw_exception (DCPReadError ("could not read video MXF information")); - } - - _size.width = desc.StoredWidth; - _size.height = desc.StoredHeight; -} - -shared_ptr -StereoPictureAsset::get_frame (int n) const -{ - return shared_ptr (new StereoPictureFrame (path().string(), n)); -} - -shared_ptr -StereoPictureAsset::start_write (bool overwrite) -{ - return shared_ptr (new StereoPictureAssetWriter (this, overwrite)); -} - -string -StereoPictureAsset::cpl_node_name () const -{ - return "msp-cpl:MainStereoscopicPicture"; -} - -pair -StereoPictureAsset::cpl_node_attribute () const -{ - if (_interop) { - return make_pair ("xmlns:msp-cpl", "http://www.digicine.com/schemas/437-Y/2007/Main-Stereo-Picture-CPL"); - } else { - return make_pair ("xmlns:msp-cpl", "http://www.smpte-ra.org/schemas/429-10/2008/Main-Stereo-Picture-CPL"); - } - - return make_pair ("", ""); -} - -int -StereoPictureAsset::edit_rate_factor () const -{ - return 2; -} diff --git a/src/stereo_picture_asset.h b/src/stereo_picture_asset.h deleted file mode 100644 index a6620dcf..00000000 --- a/src/stereo_picture_asset.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef LIBDCP_STEREO_PICTURE_ASSET_H -#define LIBDCP_STEREO_PICTURE_ASSET_H - -#include "picture_asset.h" - -namespace dcp { - -/** A 3D (stereoscopic) picture asset */ -class StereoPictureAsset : public PictureAsset -{ -public: - StereoPictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name); - - void read (); - - /** Start a progressive write to a StereoPictureAsset */ - boost::shared_ptr start_write (bool); - - boost::shared_ptr get_frame (int n) const; - bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; - -private: - std::string cpl_node_name () const; - std::pair cpl_node_attribute () const; - int edit_rate_factor () const; -}; - -} - -#endif diff --git a/src/stereo_picture_asset_writer.cc b/src/stereo_picture_asset_writer.cc deleted file mode 100644 index 04ca0590..00000000 --- a/src/stereo_picture_asset_writer.cc +++ /dev/null @@ -1,119 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include "AS_DCP.h" -#include "KM_fileio.h" -#include "stereo_picture_asset_writer.h" -#include "exceptions.h" -#include "picture_asset.h" - -#include "picture_asset_writer_common.cc" - -using std::istream; -using std::ostream; -using std::string; -using boost::shared_ptr; -using namespace dcp; - -struct StereoPictureAssetWriter::ASDCPState : public ASDCPStateBase -{ - ASDCP::JP2K::MXFSWriter mxf_writer; -}; - -StereoPictureAssetWriter::StereoPictureAssetWriter (PictureAsset* asset, bool overwrite) - : PictureAssetWriter (asset, overwrite) - , _state (new StereoPictureAssetWriter::ASDCPState) - , _next_eye (EYE_LEFT) -{ - _state->encryption_context = asset->encryption_context (); -} - -void -StereoPictureAssetWriter::start (uint8_t* data, int size) -{ - dcp::start (this, _state, _asset, data, size); -} - -/** Write a frame for one eye. Frames must be written left, then right, then left etc. - * @param data JPEG2000 data. - * @param size Size of data. - */ -FrameInfo -StereoPictureAssetWriter::write (uint8_t* data, int size) -{ - assert (!_finalized); - - if (!_started) { - start (data, size); - } - - if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) { - boost::throw_exception (MiscError ("could not parse J2K frame")); - } - - uint64_t const before_offset = _state->mxf_writer.Tell (); - - string hash; - Kumu::Result_t r = _state->mxf_writer.WriteFrame ( - _state->frame_buffer, - _next_eye == EYE_LEFT ? ASDCP::JP2K::SP_LEFT : ASDCP::JP2K::SP_RIGHT, - _state->encryption_context, - 0, - &hash - ); - - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r)); - } - - _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT; - - ++_frames_written; - return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash); -} - -void -StereoPictureAssetWriter::fake_write (int size) -{ - assert (_started); - assert (!_finalized); - - Kumu::Result_t r = _state->mxf_writer.FakeWriteFrame (size); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r)); - } - - _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT; - ++_frames_written; -} - -void -StereoPictureAssetWriter::finalize () -{ - assert (!_finalized); - - Kumu::Result_t r = _state->mxf_writer.Finalize(); - if (ASDCP_FAILURE (r)) { - boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string(), r)); - } - - _finalized = true; - _asset->set_intrinsic_duration (_frames_written / 2); - _asset->set_duration (_frames_written / 2); -} diff --git a/src/stereo_picture_asset_writer.h b/src/stereo_picture_asset_writer.h deleted file mode 100644 index 96572989..00000000 --- a/src/stereo_picture_asset_writer.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright (C) 2012-2013 Carl Hetherington - - This program 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. - - This program 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 this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include -#include -#include -#include -#include "picture_asset_writer.h" - -namespace dcp { - -/** A helper class for writing to StereoPictureAssets progressively (i.e. writing frame-by-frame, - * rather than giving libdcp all the frames in one go). - * - * Objects of this class can only be created with StereoPictureAsset::start_write(). - * - * Frames can be written to the MonoPictureAsset by calling write() with a JPEG2000 image - * (a verbatim .j2c file). finalize() must be called after the last frame has been written. - * The action of finalize() can't be done in MonoPictureAssetWriter's destructor as it may - * throw an exception. - */ -class StereoPictureAssetWriter : public PictureAssetWriter -{ -public: - FrameInfo write (uint8_t *, int); - void fake_write (int size); - void finalize (); - -private: - friend class StereoPictureAsset; - - StereoPictureAssetWriter (PictureAsset *, bool); - void start (uint8_t *, int); - - /* do this with an opaque pointer so we don't have to include - ASDCP headers - */ - - struct ASDCPState; - boost::shared_ptr _state; - - dcp::Eye _next_eye; -}; - -} diff --git a/src/stereo_picture_mxf.cc b/src/stereo_picture_mxf.cc new file mode 100644 index 00000000..29283746 --- /dev/null +++ b/src/stereo_picture_mxf.cc @@ -0,0 +1,150 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "AS_DCP.h" +#include "stereo_picture_mxf.h" +#include "stereo_picture_frame.h" +#include "exceptions.h" +#include "stereo_picture_mxf_writer.h" + +using std::string; +using std::pair; +using std::make_pair; +using boost::shared_ptr; +using boost::dynamic_pointer_cast; +using namespace dcp; + +bool +StereoPictureMXF::equals (shared_ptr other, EqualityOptions opt, boost::function note) const +{ + if (!MXF::equals (other, opt, note)) { + return false; + } + + ASDCP::JP2K::MXFSReader reader_A; + Kumu::Result_t r = reader_A.OpenRead (path().string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); + } + + ASDCP::JP2K::MXFSReader reader_B; + r = reader_B.OpenRead (other->path().string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); + } + + ASDCP::JP2K::PictureDescriptor desc_A; + if (ASDCP_FAILURE (reader_A.FillPictureDescriptor (desc_A))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + ASDCP::JP2K::PictureDescriptor desc_B; + if (ASDCP_FAILURE (reader_B.FillPictureDescriptor (desc_B))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + + if (!descriptor_equals (desc_A, desc_B, note)) { + return false; + } + + shared_ptr other_picture = dynamic_pointer_cast (other); + assert (other_picture); + + for (int i = 0; i < _intrinsic_duration; ++i) { + shared_ptr frame_A = get_frame (i); + shared_ptr frame_B = other_picture->get_frame (i); + + if (!frame_buffer_equals ( + i, opt, note, + frame_A->left_j2k_data(), frame_A->left_j2k_size(), + frame_B->left_j2k_data(), frame_B->left_j2k_size() + )) { + return false; + } + + if (!frame_buffer_equals ( + i, opt, note, + frame_A->right_j2k_data(), frame_A->right_j2k_size(), + frame_B->right_j2k_data(), frame_B->right_j2k_size() + )) { + return false; + } + } + + return true; +} + +StereoPictureMXF::StereoPictureMXF (boost::filesystem::path directory, boost::filesystem::path mxf_name) + : PictureMXF (directory, mxf_name) +{ + +} + +void +StereoPictureMXF::read () +{ + ASDCP::JP2K::MXFSReader reader; + Kumu::Result_t r = reader.OpenRead (path().string().c_str()); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r)); + } + + ASDCP::JP2K::PictureDescriptor desc; + if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) { + boost::throw_exception (DCPReadError ("could not read video MXF information")); + } + + _size.width = desc.StoredWidth; + _size.height = desc.StoredHeight; +} + +shared_ptr +StereoPictureMXF::get_frame (int n) const +{ + return shared_ptr (new StereoPictureFrame (path().string(), n)); +} + +shared_ptr +StereoPictureMXF::start_write (bool overwrite) +{ + return shared_ptr (new StereoPictureMXFWriter (this, overwrite)); +} + +string +StereoPictureMXF::cpl_node_name () const +{ + return "msp-cpl:MainStereoscopicPicture"; +} + +pair +StereoPictureMXF::cpl_node_attribute () const +{ + if (_interop) { + return make_pair ("xmlns:msp-cpl", "http://www.digicine.com/schemas/437-Y/2007/Main-Stereo-Picture-CPL"); + } else { + return make_pair ("xmlns:msp-cpl", "http://www.smpte-ra.org/schemas/429-10/2008/Main-Stereo-Picture-CPL"); + } + + return make_pair ("", ""); +} + +int +StereoPictureMXF::edit_rate_factor () const +{ + return 2; +} diff --git a/src/stereo_picture_mxf.h b/src/stereo_picture_mxf.h new file mode 100644 index 00000000..2e16b22c --- /dev/null +++ b/src/stereo_picture_mxf.h @@ -0,0 +1,49 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef LIBDCP_STEREO_PICTURE_MXF_H +#define LIBDCP_STEREO_PICTURE_MXF_H + +#include "picture_mxf.h" + +namespace dcp { + +/** A 3D (stereoscopic) picture asset */ +class StereoPictureMXF : public PictureMXF +{ +public: + StereoPictureMXF (boost::filesystem::path directory, boost::filesystem::path mxf_name); + + void read (); + + /** Start a progressive write to a StereoPictureMXF */ + boost::shared_ptr start_write (bool); + + boost::shared_ptr get_frame (int n) const; + bool equals (boost::shared_ptr other, EqualityOptions opt, boost::function note) const; + +private: + std::string cpl_node_name () const; + std::pair cpl_node_attribute () const; + int edit_rate_factor () const; +}; + +} + +#endif diff --git a/src/stereo_picture_mxf_writer.cc b/src/stereo_picture_mxf_writer.cc new file mode 100644 index 00000000..b988d75a --- /dev/null +++ b/src/stereo_picture_mxf_writer.cc @@ -0,0 +1,119 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "AS_DCP.h" +#include "KM_fileio.h" +#include "stereo_picture_mxf_writer.h" +#include "exceptions.h" +#include "picture_mxf.h" + +#include "picture_mxf_writer_common.cc" + +using std::istream; +using std::ostream; +using std::string; +using boost::shared_ptr; +using namespace dcp; + +struct StereoPictureMXFWriter::ASDCPState : public ASDCPStateBase +{ + ASDCP::JP2K::MXFSWriter mxf_writer; +}; + +StereoPictureMXFWriter::StereoPictureMXFWriter (PictureMXF* asset, bool overwrite) + : PictureMXFWriter (asset, overwrite) + , _state (new StereoPictureMXFWriter::ASDCPState) + , _next_eye (EYE_LEFT) +{ + _state->encryption_context = asset->encryption_context (); +} + +void +StereoPictureMXFWriter::start (uint8_t* data, int size) +{ + dcp::start (this, _state, _asset, data, size); +} + +/** Write a frame for one eye. Frames must be written left, then right, then left etc. + * @param data JPEG2000 data. + * @param size Size of data. + */ +FrameInfo +StereoPictureMXFWriter::write (uint8_t* data, int size) +{ + assert (!_finalized); + + if (!_started) { + start (data, size); + } + + if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) { + boost::throw_exception (MiscError ("could not parse J2K frame")); + } + + uint64_t const before_offset = _state->mxf_writer.Tell (); + + string hash; + Kumu::Result_t r = _state->mxf_writer.WriteFrame ( + _state->frame_buffer, + _next_eye == EYE_LEFT ? ASDCP::JP2K::SP_LEFT : ASDCP::JP2K::SP_RIGHT, + _state->encryption_context, + 0, + &hash + ); + + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r)); + } + + _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT; + + ++_frames_written; + return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash); +} + +void +StereoPictureMXFWriter::fake_write (int size) +{ + assert (_started); + assert (!_finalized); + + Kumu::Result_t r = _state->mxf_writer.FakeWriteFrame (size); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r)); + } + + _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT; + ++_frames_written; +} + +void +StereoPictureMXFWriter::finalize () +{ + assert (!_finalized); + + Kumu::Result_t r = _state->mxf_writer.Finalize(); + if (ASDCP_FAILURE (r)) { + boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string(), r)); + } + + _finalized = true; + _asset->set_intrinsic_duration (_frames_written / 2); + _asset->set_duration (_frames_written / 2); +} diff --git a/src/stereo_picture_mxf_writer.h b/src/stereo_picture_mxf_writer.h new file mode 100644 index 00000000..384250db --- /dev/null +++ b/src/stereo_picture_mxf_writer.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2012-2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include "picture_mxf_writer.h" + +namespace dcp { + +/** A helper class for writing to StereoPictureAssets progressively (i.e. writing frame-by-frame, + * rather than giving libdcp all the frames in one go). + * + * Objects of this class can only be created with StereoPictureAsset::start_write(). + * + * Frames can be written to the MonoPictureAsset by calling write() with a JPEG2000 image + * (a verbatim .j2c file). finalize() must be called after the last frame has been written. + * The action of finalize() can't be done in MonoPictureAssetWriter's destructor as it may + * throw an exception. + */ +class StereoPictureMXFWriter : public PictureMXFWriter +{ +public: + FrameInfo write (uint8_t *, int); + void fake_write (int size); + void finalize (); + +private: + friend class StereoPictureMXF; + + StereoPictureMXFWriter (PictureMXF *, bool); + void start (uint8_t *, int); + + /* do this with an opaque pointer so we don't have to include + ASDCP headers + */ + + struct ASDCPState; + boost::shared_ptr _state; + + dcp::Eye _next_eye; +}; + +} diff --git a/src/wscript b/src/wscript index db0cc628..bcdc149c 100644 --- a/src/wscript +++ b/src/wscript @@ -26,23 +26,23 @@ def build(bld): kdm.cc key.cc metadata.cc - mono_picture_asset.cc - mono_picture_asset_writer.cc + mono_picture_mxf.cc + mono_picture_mxf_writer.cc mono_picture_frame.cc - mxf_asset.cc + mxf.cc object.cc - picture_asset.cc - picture_asset_writer.cc + picture_mxf.cc + picture_mxf_writer.cc rec709_linearised_gamma_lut.cc reel.cc rgb_xyz.cc signer.cc signer_chain.cc - sound_asset.cc + sound_mxf.cc sound_frame.cc srgb_linearised_gamma_lut.cc - stereo_picture_asset.cc - stereo_picture_asset_writer.cc + stereo_picture_mxf.cc + stereo_picture_mxf_writer.cc stereo_picture_frame.cc subtitle_asset.cc types.cc @@ -70,22 +70,22 @@ def build(bld): lut.h lut_cache.h metadata.h - mono_picture_asset.h + mono_picture_mxf.h mono_picture_frame.h - mxf_asset.h + mxf.h object.h - picture_asset.h - picture_asset_writer.h + picture_mxf.h + picture_mxf_writer.h rgb_xyz.h rec709_linearised_gamma_lut.h reel.h argb_frame.h signer.h signer_chain.h - sound_asset.h + sound_mxf.h sound_frame.h srgb_linearised_gamma_lut.h - stereo_picture_asset.h + stereo_picture_mxf.h stereo_picture_frame.h subtitle_asset.h types.h diff --git a/test/cpl_sar.cc b/test/cpl_sar.cc index 5628363a..e280dbd3 100644 --- a/test/cpl_sar.cc +++ b/test/cpl_sar.cc @@ -20,7 +20,7 @@ #include #include #include "cpl.h" -#include "mono_picture_asset.h" +#include "mono_picture_mxf.h" using boost::shared_ptr; @@ -29,7 +29,7 @@ using boost::shared_ptr; */ BOOST_AUTO_TEST_CASE (cpl_sar) { - shared_ptr mp (new dcp::MonoPictureAsset ("build/test/foo", "video.mxf")); + shared_ptr mp (new dcp::MonoPictureMXF ("build/test/foo", "video.mxf")); mp->set_interop (true); { diff --git a/test/decryption_test.cc b/test/decryption_test.cc index 5fd29c6c..b1cb5265 100644 --- a/test/decryption_test.cc +++ b/test/decryption_test.cc @@ -23,7 +23,7 @@ #include "mono_picture_frame.h" #include "cpl.h" #include "argb_frame.h" -#include "mono_picture_asset.h" +#include "mono_picture_mxf.h" #include "reel.h" #include "test.h" @@ -34,10 +34,10 @@ static shared_ptr get_frame (dcp::DCP const & dcp) { shared_ptr reel = dcp.cpls().front()->reels().front (); - shared_ptr picture = reel->main_picture (); + shared_ptr picture = reel->main_picture (); BOOST_CHECK (picture); - shared_ptr mono_picture = dynamic_pointer_cast (picture); + shared_ptr mono_picture = dynamic_pointer_cast (picture); shared_ptr j2k_frame = mono_picture->get_frame (0); return j2k_frame->argb_frame (); } diff --git a/test/frame_info_test.cc b/test/frame_info_test.cc index bf4181a0..02ff83aa 100644 --- a/test/frame_info_test.cc +++ b/test/frame_info_test.cc @@ -19,7 +19,8 @@ #include #include -#include "picture_asset_writer.h" +#include "picture_mxf.h" +#include "picture_mxf_writer.h" using namespace std; diff --git a/test/recovery_test.cc b/test/recovery_test.cc index 27a4c150..4cde723f 100644 --- a/test/recovery_test.cc +++ b/test/recovery_test.cc @@ -19,8 +19,8 @@ #include #include -#include "mono_picture_asset_writer.h" -#include "mono_picture_asset.h" +#include "mono_picture_mxf_writer.h" +#include "mono_picture_mxf.h" #include "KM_util.h" using std::string; @@ -48,10 +48,10 @@ BOOST_AUTO_TEST_CASE (recovery) boost::filesystem::remove_all ("build/test/baz"); boost::filesystem::create_directories ("build/test/baz"); - shared_ptr mp (new dcp::MonoPictureAsset ("build/test/baz", "video1.mxf")); + shared_ptr mp (new dcp::MonoPictureMXF ("build/test/baz", "video1.mxf")); mp->set_edit_rate (24); mp->set_size (dcp::Size (32, 32)); - shared_ptr writer = mp->start_write (false); + shared_ptr writer = mp->start_write (false); int written_size = 0; for (int i = 0; i < 24; ++i) { @@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE (recovery) Kumu::ResetTestRNG (); #endif - mp.reset (new dcp::MonoPictureAsset ("build/test/baz", "video2.mxf")); + mp.reset (new dcp::MonoPictureMXF ("build/test/baz", "video2.mxf")); mp->set_edit_rate (24); mp->set_size (dcp::Size (32, 32)); writer = mp->start_write (true); diff --git a/test/round_trip_test.cc b/test/round_trip_test.cc index 51edf111..0e6fe5f5 100644 --- a/test/round_trip_test.cc +++ b/test/round_trip_test.cc @@ -22,8 +22,8 @@ #include "certificates.h" #include "kdm.h" #include "signer.h" -#include "mono_picture_asset.h" -#include "sound_asset.h" +#include "mono_picture_mxf.h" +#include "sound_mxf.h" #include "reel.h" #include "test.h" #include "cpl.h" @@ -56,7 +56,7 @@ BOOST_AUTO_TEST_CASE (round_trip_test) boost::filesystem::path work_dir = "build/test/round_trip_test"; boost::filesystem::create_directory (work_dir); - shared_ptr asset_A (new dcp::MonoPictureAsset (work_dir, "video.mxf")); + shared_ptr asset_A (new dcp::MonoPictureMXF (work_dir, "video.mxf")); asset_A->set_edit_rate (24); asset_A->set_intrinsic_duration (24); asset_A->set_size (dcp::Size (32, 32)); @@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE (round_trip_test) asset_A->set_key (key); shared_ptr cpl (new dcp::CPL (work_dir, "A Test DCP", dcp::FEATURE, 24, 24)); - cpl->add_reel (shared_ptr (new dcp::Reel (asset_A, shared_ptr (), shared_ptr ()))); + cpl->add_reel (shared_ptr (new dcp::Reel (asset_A, shared_ptr (), shared_ptr ()))); /* A KDM using our certificate chain's leaf key pair */ dcp::KDM kdm_A ( @@ -100,8 +100,8 @@ BOOST_AUTO_TEST_CASE (round_trip_test) } /* Reload the picture MXF */ - shared_ptr asset_B ( - new dcp::MonoPictureAsset (work_dir, "video.mxf") + shared_ptr asset_B ( + new dcp::MonoPictureMXF (work_dir, "video.mxf") ); asset_B->set_key (kdm_B.keys().front().key()); diff --git a/tools/dcpinfo.cc b/tools/dcpinfo.cc index 48926c01..70c94d8e 100644 --- a/tools/dcpinfo.cc +++ b/tools/dcpinfo.cc @@ -5,8 +5,8 @@ #include "dcp.h" #include "exceptions.h" #include "reel.h" -#include "sound_asset.h" -#include "picture_asset.h" +#include "sound_mxf.h" +#include "picture_mxf.h" #include "subtitle_asset.h" #include "cpl.h" -- cgit v1.2.3