diff options
| author | Carl Hetherington <cth@carlh.net> | 2012-07-16 19:24:44 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2012-07-16 19:24:44 +0100 |
| commit | 7d48446b5efdf795df1ce22d6d9ed3ebe85d3381 (patch) | |
| tree | f492aebd71fae087e7903dafc097d3899cff8481 | |
Import.
56 files changed, 1592 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..62449d43 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +core +*~ +build +.waf* +.lock*
\ No newline at end of file diff --git a/run-tests.sh b/run-tests.sh new file mode 100755 index 00000000..08db5a85 --- /dev/null +++ b/run-tests.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +LD_LIBRARY_PATH=build/src +build/test/tests +diff -ur build/test/foo test/ref/DCP +
\ No newline at end of file diff --git a/src/asset.cc b/src/asset.cc new file mode 100644 index 00000000..2101e4bb --- /dev/null +++ b/src/asset.cc @@ -0,0 +1,81 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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 <boost/filesystem.hpp> +#include <AS_DCP.h> +#include <KM_util.h> +#include "asset.h" +#include "util.h" +#include "tags.h" + +using namespace std; +using namespace boost; +using namespace libdcp; + +/** Construct an Asset. + * @param p Pathname of MXF file. + * @param fps Frames per second. + * @param len Length in frames. + */ + +Asset::Asset (string p, int fps, int len) + : _mxf_path (p) + , _fps (fps) + , _length (len) + , _uuid (make_uuid ()) +{ + +} + +void +Asset::write_to_pkl (ostream& s) const +{ + s << " <Id>urn:uuid:" << _uuid << "</Id>\n" + << " <AnnotationText>" << filesystem::path(_mxf_path).filename() << "</AnnotationText>\n" + << " <Hash>" << _digest << "</Hash>\n" + << " <Size>" << filesystem::file_size(_mxf_path) << "</Size>\n" + << " <Type>application/mxf</Type>\n"; +} + +void +Asset::write_to_assetmap (ostream& s) const +{ + s << " <Asset>\n" + << " <Id>urn:uuid:" << _uuid << "</Id>\n" + << " <ChunkList>\n" + << " <Chunk>\n" + << " <Path>" << filesystem::path(_mxf_path).filename() << "</Path>\n" + << " <VolumeIndex>1</VolumeIndex>\n" + << " <Offset>0</Offset>\n" + << " <Length>" << filesystem::file_size(_mxf_path) << "</Length>\n" + << " </Chunk>\n" + << " </ChunkList>\n" + << " </Asset>\n"; +} + +void +Asset::fill_writer_info (ASDCP::WriterInfo* writer_info) const +{ + writer_info->ProductVersion = Tags::instance()->product_version; + writer_info->CompanyName = Tags::instance()->company_name; + writer_info->ProductName = Tags::instance()->product_name.c_str(); + + writer_info->LabelSetType = ASDCP::LS_MXF_SMPTE; + Kumu::GenRandomUUID (writer_info->AssetUUID); +} diff --git a/src/asset.h b/src/asset.h new file mode 100644 index 00000000..ce742d11 --- /dev/null +++ b/src/asset.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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_ASSET_H +#define LIBDCP_ASSET_H + +#include <string> +#include <sigc++/sigc++.h> + +namespace ASDCP { + class WriterInfo; +} + +namespace libdcp +{ + +class Asset +{ +public: + Asset (std::string, int, int); + + virtual void write_to_cpl (std::ostream &) const = 0; + void write_to_pkl (std::ostream &) const; + void write_to_assetmap (std::ostream &) const; + + sigc::signal1<void, float> Progress; + +protected: + void fill_writer_info (ASDCP::WriterInfo *) const; + + std::string _mxf_path; + int _fps; + int _length; + std::string _uuid; + std::string _digest; +}; + +} + +#endif diff --git a/src/dcp.cc b/src/dcp.cc new file mode 100644 index 00000000..0ba4ed72 --- /dev/null +++ b/src/dcp.cc @@ -0,0 +1,270 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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 <sstream> +#include <fstream> +#include <iomanip> +#include <cassert> +#include <boost/filesystem.hpp> +#include "dcp.h" +#include "asset.h" +#include "sound_asset.h" +#include "picture_asset.h" +#include "util.h" +#include "tags.h" + +using namespace std; +using namespace boost; +using namespace libdcp; + +/** Construct a DCP. + * @param d Directory to write files to. + */ +DCP::DCP (string d, string n, ContentType c, int fps, int length) + : _directory (d) + , _name (n) + , _content_type (c) + , _fps (fps) + , _length (length) +{ + char buffer[64]; + time_t now; + time (&now); + struct tm* tm = localtime (&now); + strftime (buffer, 64, "%Y-%m-%dT%I:%M:%S+00:00", tm); + _date = string (buffer); +} + +void +DCP::add_sound_asset (list<string> const & files) +{ + filesystem::path p; + p /= _directory; + p /= "audio.mxf"; + _assets.push_back (shared_ptr<SoundAsset> (new SoundAsset (files, p.string(), _fps, _length))); +} + +void +DCP::add_picture_asset (list<string> const & files, int w, int h) +{ + filesystem::path p; + p /= _directory; + p /= "video.mxf"; + _assets.push_back (shared_ptr<PictureAsset> (new PictureAsset (files, p.string(), _fps, _length, w, h))); +} + +/** Write the required XML files to the directory that was + * passed into the constructor. + */ +void +DCP::write_xml () const +{ + string cpl_uuid = make_uuid (); + string cpl_path = write_cpl (cpl_uuid); + int cpl_length = filesystem::file_size (cpl_path); + string cpl_digest = make_digest (cpl_path); + + string pkl_uuid = make_uuid (); + string pkl_path = write_pkl (pkl_uuid, cpl_uuid, cpl_digest, cpl_length); + + write_volindex (); + write_assetmap (cpl_uuid, cpl_length, pkl_uuid, filesystem::file_size (pkl_path)); +} + +/** Write the CPL file. + * @param cpl_uuid UUID to use. + * @return CPL pathname. + */ +string +DCP::write_cpl (string cpl_uuid) const +{ + filesystem::path p; + p /= _directory; + stringstream s; + s << cpl_uuid << "_cpl.xml"; + p /= s.str(); + ofstream cpl (p.string().c_str()); + + cpl << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + << "<CompositionPlaylist xmlns=\"http://www.smpte-ra.org/schemas/429-7/2006/CPL\">\n" + << " <Id>urn:uuid:" << cpl_uuid << "</Id>\n" + << " <AnnotationText>" << _name << "</AnnotationText>\n" + << " <IssueDate>" << _date << "</IssueDate>\n" + << " <Creator>libdcp " << Tags::instance()->creator << "</Creator>\n" + << " <ContentTitleText>" << _name << "</ContentTitleText>\n" + << " <ContentKind>" << _content_type << "</ContentKind>\n" + << " <ContentVersion>\n" + << " <Id>urn:uri:" << cpl_uuid << "_" << _date << "</Id>\n" + << " <LabelText>" << cpl_uuid << "_" << _date << "</LabelText>\n" + << " </ContentVersion>\n" + << " <RatingList/>\n" + << " <ReelList>\n"; + + cpl << " <Reel>\n" + << " <Id>urn:uuid:" << make_uuid() << "</Id>\n" + << " <AssetList>\n"; + + for (list<shared_ptr<Asset> >::const_iterator i = _assets.begin(); i != _assets.end(); ++i) { + (*i)->write_to_cpl (cpl); + } + + cpl << " </AssetList>\n" + << " </Reel>\n" + << " </ReelList>\n" + << "</CompositionPlaylist>\n"; + + return p.string (); +} + +/** Write the PKL file. + * @param pkl_uuid UUID to use. + * @param cpl_uuid UUID of the CPL file. + * @param cpl_digest SHA digest of the CPL file. + * @param cpl_length Length of the CPL file in bytes. + */ +std::string +DCP::write_pkl (string pkl_uuid, string cpl_uuid, string cpl_digest, int cpl_length) const +{ + filesystem::path p; + p /= _directory; + stringstream s; + s << pkl_uuid << "_pkl.xml"; + p /= s.str(); + ofstream pkl (p.string().c_str()); + + pkl << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + << "<PackingList xmlns=\"http://www.smpte-ra.org/schemas/429-8/2007/PKL\">\n" + << " <Id>urn:uuid:" << pkl_uuid << "</Id>\n" + << " <AnnotationText>" << _name << "</AnnotationText>\n" + << " <IssueDate>" << _date << "</IssueDate>\n" + << " <Issuer>" << Tags::instance()->issuer << "</Issuer>\n" + << " <Creator>" << Tags::instance()->creator << "</Creator>\n" + << " <AssetList>\n"; + + for (list<shared_ptr<Asset> >::const_iterator i = _assets.begin(); i != _assets.end(); ++i) { + (*i)->write_to_pkl (pkl); + } + + pkl << " <Asset>\n" + << " <Id>urn:uuid" << cpl_uuid << "</Id>\n" + << " <Hash>" << cpl_digest << "</Hash>\n" + << " <Size>" << cpl_length << "</Size>\n" + << " <Type>text/xml</Type>\n" + << " </Asset>\n"; + + pkl << " </AssetList>\n" + << "</PackingList>\n"; + + return p.string (); +} + +void +DCP::write_volindex () const +{ + filesystem::path p; + p /= _directory; + p /= "VOLINDEX.xml"; + ofstream vi (p.string().c_str()); + + vi << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + << "<VolumeIndex xmlns=\"http://www.smpte-ra.org/schemas/429-9/2007/AM\">\n" + << " <Index>1</Index>\n" + << "</VolumeIndex>\n"; +} + +void +DCP::write_assetmap (string cpl_uuid, int cpl_length, string pkl_uuid, int pkl_length) const +{ + filesystem::path p; + p /= _directory; + p /= "ASSETMAP.xml"; + ofstream am (p.string().c_str()); + + am << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + << "<AssetMap xmlns=\"http://www.smpte-ra.org/schemas/429-9/2007/AM\">\n" + << " <Id>urn:uuid:" << make_uuid() << "</Id>\n" + << " <Creator>" << Tags::instance()->creator << "</Creator>\n" + << " <VolumeCount>1</VolumeCount>\n" + << " <IssueDate>" << _date << "</IssueDate>\n" + << " <Issuer>" << Tags::instance()->issuer << "</Issuer>\n" + << " <AssetList>\n"; + + am << " <Asset>\n" + << " <Id>urn:uuid:" << pkl_uuid << "</Id>\n" + << " <PackingList>true</PackingList>\n" + << " <ChunkList>\n" + << " <Chunk>\n" + << " <Path>" << pkl_uuid << "_pkl.xml</Path>\n" + << " <VolumeIndex>1</VolumeIndex>\n" + << " <Offset>0</Offset>\n" + << " <Length>" << pkl_length << "</Length>\n" + << " </Chunk>\n" + << " </ChunkList>\n" + << " </Asset>\n"; + + am << " <Asset>\n" + << " <Id>urn:uuid:" << cpl_uuid << "</Id>\n" + << " <ChunkList>\n" + << " <Chunk>\n" + << " <Path>" << cpl_uuid << "_cpl.xml</Path>\n" + << " <VolumeIndex>1</VolumeIndex>\n" + << " <Offset>0</Offset>\n" + << " <Length>" << cpl_length << "</Length>\n" + << " </Chunk>\n" + << " </ChunkList>\n" + << " </Asset>\n"; + + for (list<shared_ptr<Asset> >::const_iterator i = _assets.begin(); i != _assets.end(); ++i) { + (*i)->write_to_assetmap (am); + } + + am << " </AssetList>\n" + << "</AssetMap>\n"; +} + + +string +DCP::content_type_string (ContentType t) +{ + switch (t) { + case FEATURE: + return "feature"; + case SHORT: + return "short"; + case TRAILER: + return "trailer"; + case TEST: + return "test"; + case TRANSITIONAL: + return "transitional"; + case RATING: + return "rating"; + case TEASER: + return "teaser"; + case POLICY: + return "policy"; + case PUBLIC_SERVICE_ANNOUNCEMENT: + return "psa"; + case ADVERTISEMENT: + return "advertisement"; + } + + assert (false); +} + diff --git a/src/dcp.h b/src/dcp.h new file mode 100644 index 00000000..27be9ec6 --- /dev/null +++ b/src/dcp.h @@ -0,0 +1,72 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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 <string> +#include <list> +#include <boost/shared_ptr.hpp> + +namespace libdcp +{ + +class Asset; + +class DCP +{ +public: + enum ContentType + { + FEATURE, + SHORT, + TRAILER, + TEST, + TRANSITIONAL, + RATING, + TEASER, + POLICY, + PUBLIC_SERVICE_ANNOUNCEMENT, + ADVERTISEMENT + }; + + DCP (std::string, std::string, ContentType, int, int); + + void add_sound_asset (std::list<std::string> const &); + void add_picture_asset (std::list<std::string> const &, int, int); + + void write_xml () const; + +private: + + std::string write_cpl (std::string) const; + std::string write_pkl (std::string, std::string, std::string, int) const; + void write_volindex () const; + void write_assetmap (std::string, int, std::string, int) const; + + static std::string content_type_string (ContentType); + + std::string _directory; + std::string _name; + ContentType _content_type; + int _fps; + int _length; + std::list<boost::shared_ptr<Asset> > _assets; + + std::string _date; +}; + +} diff --git a/src/picture_asset.cc b/src/picture_asset.cc new file mode 100644 index 00000000..4e3b0227 --- /dev/null +++ b/src/picture_asset.cc @@ -0,0 +1,92 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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 <list> +#include <stdexcept> +#include <iostream> +#include <boost/filesystem.hpp> +#include <AS_DCP.h> +#include <KM_fileio.h> +#include "picture_asset.h" +#include "util.h" + +using namespace std; +using namespace boost; +using namespace libdcp; + +PictureAsset::PictureAsset (list<string> const & files, string p, int fps, int len, int w, int h) + : Asset (p, fps, len) + , _width (w) + , _height (h) +{ + ASDCP::JP2K::CodestreamParser j2k_parser; + ASDCP::JP2K::FrameBuffer frame_buffer (4 * Kumu::Megabyte); + if (ASDCP_FAILURE (j2k_parser.OpenReadFrame (files.front().c_str(), frame_buffer))) { + throw runtime_error ("could not open J2K file for reading"); + } + + ASDCP::JP2K::PictureDescriptor picture_desc; + j2k_parser.FillPictureDescriptor (picture_desc); + /* XXX: we round for DCP: not sure if this is right */ + picture_desc.EditRate = ASDCP::Rational (_fps, 1); + + ASDCP::WriterInfo writer_info; + fill_writer_info (&writer_info); + + ASDCP::JP2K::MXFWriter mxf_writer; + if (ASDCP_FAILURE (mxf_writer.OpenWrite (_mxf_path.c_str(), writer_info, picture_desc))) { + throw runtime_error ("could not open MXF for writing"); + } + + int j = 0; + for (list<string>::const_iterator i = files.begin(); i != files.end(); ++i) { + if (ASDCP_FAILURE (j2k_parser.OpenReadFrame (i->c_str(), frame_buffer))) { + throw runtime_error ("could not open J2K file for reading"); + } + + /* XXX: passing 0 to WriteFrame ok? */ + if (ASDCP_FAILURE (mxf_writer.WriteFrame (frame_buffer, 0, 0))) { + throw runtime_error ("error in writing video MXF"); + } + + ++j; + Progress (float (j) / files.size ()); + } + + if (ASDCP_FAILURE (mxf_writer.Finalize())) { + throw runtime_error ("error in finalising video MXF"); + } + + _digest = make_digest (_mxf_path); +} + +void +PictureAsset::write_to_cpl (ostream& s) const +{ + s << " <MainPicture>\n" + << " <Id>" << _uuid << "</Id>\n" + << " <AnnotationText>" << filesystem::path(_mxf_path).filename() << "</AnnotationText>\n" + << " <EditRate>" << _fps << "</EditRate>\n" + << " <IntrinsicDuration>" << _length << "</IntrinsicDuration>\n" + << " <EntryPoint>0</EntryPoint>\n" + << " <Duration>" << _length << "</Duration>\n" + << " <FrameRate>" << _fps << "</FrameRate>\n" + << " <ScreenAspectRatio>" << _width << " " << _height << "</ScreenAspectRatio>\n" + << " </MainPicture>\n"; +} diff --git a/src/picture_asset.h b/src/picture_asset.h new file mode 100644 index 00000000..cef5367b --- /dev/null +++ b/src/picture_asset.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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 "asset.h" + +namespace libdcp +{ + +class PictureAsset : public Asset +{ +public: + PictureAsset (std::list<std::string> const &, std::string, int, int, int, int); + + void write_to_cpl (std::ostream &) const; + +private: + int _width; + int _height; +}; + +} diff --git a/src/sound_asset.cc b/src/sound_asset.cc new file mode 100644 index 00000000..30083719 --- /dev/null +++ b/src/sound_asset.cc @@ -0,0 +1,131 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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 <iostream> +#include <stdexcept> +#include <boost/filesystem.hpp> +#include <AS_DCP.h> +#include "sound_asset.h" +#include "util.h" + +using namespace std; +using namespace boost; +using namespace libdcp; + +SoundAsset::SoundAsset (list<string> const & files, string p, int fps, int len) + : Asset (p, fps, len) +{ + ASDCP::Rational asdcp_fps (_fps, 1); + + ASDCP::PCM::WAVParser pcm_parser_channel[files.size()]; + if (pcm_parser_channel[0].OpenRead (files.front().c_str(), asdcp_fps)) { + throw runtime_error ("could not open WAV file for reading"); + } + + ASDCP::PCM::AudioDescriptor audio_desc; + pcm_parser_channel[0].FillAudioDescriptor (audio_desc); + audio_desc.ChannelCount = 0; + audio_desc.BlockAlign = 0; + audio_desc.EditRate = asdcp_fps; + audio_desc.AvgBps = audio_desc.AvgBps * files.size (); + + ASDCP::PCM::FrameBuffer frame_buffer_channel[files.size()]; + ASDCP::PCM::AudioDescriptor audio_desc_channel[files.size()]; + + int j = 0; + for (list<string>::const_iterator i = files.begin(); i != files.end(); ++i) { + + if (ASDCP_FAILURE (pcm_parser_channel[j].OpenRead (i->c_str(), asdcp_fps))) { + throw runtime_error ("could not open WAV file for reading"); + } + + pcm_parser_channel[j].FillAudioDescriptor (audio_desc_channel[j]); + frame_buffer_channel[j].Capacity (ASDCP::PCM::CalcFrameBufferSize (audio_desc_channel[j])); + + audio_desc.ChannelCount += audio_desc_channel[j].ChannelCount; + audio_desc.BlockAlign += audio_desc_channel[j].BlockAlign; + ++j; + } + + ASDCP::PCM::FrameBuffer frame_buffer; + frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (audio_desc)); + frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (audio_desc)); + + ASDCP::WriterInfo writer_info; + fill_writer_info (&writer_info); + + ASDCP::PCM::MXFWriter mxf_writer; + if (ASDCP_FAILURE (mxf_writer.OpenWrite (_mxf_path.c_str(), writer_info, audio_desc))) { + throw runtime_error ("could not open audio MXF for writing"); + } + + for (int i = 0; i < _length; ++i) { + + byte_t *data_s = frame_buffer.Data(); + byte_t *data_e = data_s + frame_buffer.Capacity(); + byte_t sample_size = ASDCP::PCM::CalcSampleSize (audio_desc_channel[0]); + int offset = 0; + + for (list<string>::size_type j = 0; j < files.size(); ++j) { + memset (frame_buffer_channel[j].Data(), 0, frame_buffer_channel[j].Capacity()); + if (ASDCP_FAILURE (pcm_parser_channel[j].ReadFrame (frame_buffer_channel[j]))) { + throw runtime_error ("could not read audio frame"); + } + + if (frame_buffer_channel[j].Size() != frame_buffer_channel[j].Capacity()) { + throw runtime_error ("short audio frame"); + } + } + + while (data_s < data_e) { + for (list<string>::size_type j = 0; j < files.size(); ++j) { + byte_t* frame = frame_buffer_channel[j].Data() + offset; + memcpy (data_s, frame, sample_size); + data_s += sample_size; + } + offset += sample_size; + } + + if (ASDCP_FAILURE (mxf_writer.WriteFrame (frame_buffer, 0, 0))) { + throw runtime_error ("could not write audio MXF frame"); + } + + Progress (float (i) / _length); + } + + if (ASDCP_FAILURE (mxf_writer.Finalize())) { + throw runtime_error ("could not finalise audio MXF"); + } + + _digest = make_digest (_mxf_path); +} + +void +SoundAsset::write_to_cpl (ostream& s) const +{ + s << " <MainSound>\n" + << " <Id>" << _uuid << "</Id>\n" + << " <AnnotationText>" << filesystem::path(_mxf_path).filename() << "</AnnotationText>\n" + << " <EditRate>" << _fps << "</EditRate>\n" + << " <IntrinsicDuration>" << _length << "</IntrinsicDuration>\n" + << " <EntryPoint>0</EntryPoint>\n" + << " <Duration>" << _length << "</Duration>\n" + << " </MainSound>\n"; +} + diff --git a/src/sound_asset.h b/src/sound_asset.h new file mode 100644 index 00000000..8c63b530 --- /dev/null +++ b/src/sound_asset.h @@ -0,0 +1,33 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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 "asset.h" + +namespace libdcp +{ + +class SoundAsset : public Asset +{ +public: + SoundAsset (std::list<std::string> const &, std::string, int, int); + + void write_to_cpl (std::ostream &) const; +}; + +} diff --git a/src/tags.cc b/src/tags.cc new file mode 100644 index 00000000..40aeeea3 --- /dev/null +++ b/src/tags.cc @@ -0,0 +1,46 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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 "tags.h" + +using namespace std; +using namespace libdcp; + +Tags* Tags::_instance = 0; + +Tags::Tags () + : company_name ("libdcp") + , product_name ("libdcp") + , product_version (LIBDCP_VERSION) + , issuer ("libdcp" LIBDCP_VERSION) + , creator ("libdcp" LIBDCP_VERSION) +{ + +} + +Tags * +Tags::instance () +{ + if (_instance == 0) { + _instance = new Tags; + } + + return _instance; +} + diff --git a/src/tags.h b/src/tags.h new file mode 100644 index 00000000..036bd4e6 --- /dev/null +++ b/src/tags.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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 <string> + +namespace libdcp +{ + +class Tags +{ +public: + static Tags* instance (); + + std::string company_name; + std::string product_name; + std::string product_version; + std::string issuer; + std::string creator; + +private: + Tags (); + + static Tags* _instance; +}; + +} diff --git a/src/util.cc b/src/util.cc new file mode 100644 index 00000000..aab2e184 --- /dev/null +++ b/src/util.cc @@ -0,0 +1,95 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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 <stdexcept> +#include <sstream> +#include <iomanip> +#include <openssl/sha.h> +#include "KM_util.h" +#include "KM_fileio.h" +#include "AS_DCP.h" +#include "util.h" + +using namespace std; + +bool libdcp::libdcp_test = false; + +/** Create a UUID. + * @return UUID. + */ +string +libdcp::make_uuid () +{ + char buffer[64]; + Kumu::UUID id; + + if (libdcp_test) { + static int N = 0; + byte_t t[16]; + for (int i = 0; i < 16; ++i) { + t[i] = N; + } + ++N; + + id = Kumu::UUID (t); + } else { + Kumu::GenRandomValue (id); + } + + id.EncodeHex (buffer, 64); + return string (buffer); +} + +/** Create a digest for a file. + * @param filename File name. + * @return Digest. + */ +string +libdcp::make_digest (string filename) +{ + Kumu::FileReader reader; + if (ASDCP_FAILURE (reader.OpenRead (filename.c_str ()))) { + throw runtime_error ("could not open file to compute digest"); + } + + SHA_CTX sha; + SHA1_Init (&sha); + + Kumu::ByteString read_buffer (65536); + while (1) { + ui32_t read = 0; + Kumu::Result_t r = reader.Read (read_buffer.Data(), read_buffer.Capacity(), &read); + + if (r == Kumu::RESULT_ENDOFFILE) { + break; + } else if (ASDCP_FAILURE (r)) { + throw runtime_error ("could not read file to compute digest"); + } + + SHA1_Update (&sha, read_buffer.Data(), read); + } + + byte_t byte_buffer[20]; + SHA1_Final (byte_buffer, &sha); + + stringstream s; + char digest[64]; + s << setfill('0') << setw(36) << Kumu::base64encode (byte_buffer, 20, digest, 64); + return s.str (); +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 00000000..fcb6d739 --- /dev/null +++ b/src/util.h @@ -0,0 +1,28 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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 <string> + +namespace libdcp { + +extern std::string make_uuid (); +extern std::string make_digest (std::string); +extern bool libdcp_test; + +} diff --git a/src/wscript b/src/wscript new file mode 100644 index 00000000..22b75b97 --- /dev/null +++ b/src/wscript @@ -0,0 +1,14 @@ +def build(bld): + obj = bld(features = 'cxx cxxshlib') + obj.name = 'libdcp' + obj.export_includes = ['.'] + obj.uselib = 'ASDCP KUMU BOOST_FILESYSTEM OPENSSL SIGC++' + obj.source = """ + dcp.cc + asset.cc + sound_asset.cc + picture_asset.cc + util.cc + tags.cc + """ + obj.target = 'libdcp' diff --git a/test/data/1s_24-bit_48k_silence.wav b/test/data/1s_24-bit_48k_silence.wav Binary files differnew file mode 100644 index 00000000..5bfc14dc --- /dev/null +++ b/test/data/1s_24-bit_48k_silence.wav diff --git a/test/data/32x32_red_square.j2c b/test/data/32x32_red_square.j2c Binary files differnew file mode 100644 index 00000000..027929be --- /dev/null +++ b/test/data/32x32_red_square.j2c diff --git a/test/data/32x32_red_square.png b/test/data/32x32_red_square.png Binary files differnew file mode 100644 index 00000000..49cd2bef --- /dev/null +++ b/test/data/32x32_red_square.png diff --git a/test/ref/DCP/02020202-0202-0202-0202-020202020202_cpl.xml b/test/ref/DCP/02020202-0202-0202-0202-020202020202_cpl.xml new file mode 100644 index 00000000..cc5e933e --- /dev/null +++ b/test/ref/DCP/02020202-0202-0202-0202-020202020202_cpl.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CompositionPlaylist xmlns="http://www.smpte-ra.org/schemas/429-7/2006/CPL"> + <Id>urn:uuid:488d58ab-474e-4b01-a2cb-a6ce1e5e70b3</Id> + <AnnotationText>A Test DCP</AnnotationText> + <IssueDate>2012-07-16T06:28:27+00:00</IssueDate> + <Creator>OpenDCP 0.0.25</Creator> + <ContentTitleText>A Test DCP</ContentTitleText> + <ContentKind>feature</ContentKind> + <ContentVersion> + <Id>urn:uri:488d58ab-474e-4b01-a2cb-a6ce1e5e70b3_2012-07-16T06:28:27+00:00</Id> + <LabelText>488d58ab-474e-4b01-a2cb-a6ce1e5e70b3_2012-07-16T06:28:27+00:00</LabelText> + </ContentVersion> + <RatingList/> + <ReelList> + <Reel> + <Id>urn:uuid:b4f8f14e-63d9-4d34-9b8d-11d158d77115</Id> + <AssetList> + <MainPicture> + <Id>urn:uuid:0a2335fe-09a3-4a19-b5ea-2e0308e727b3</Id> + <AnnotationText>video.mxf</AnnotationText> + <EditRate>24 1</EditRate> + <IntrinsicDuration>24</IntrinsicDuration> + <EntryPoint>0</EntryPoint> + <Duration>24</Duration> + <FrameRate>24 1</FrameRate> + <ScreenAspectRatio>32 32</ScreenAspectRatio> + </MainPicture> + <MainSound> + <Id>urn:uuid:ac884d5a-1d72-425b-a45b-d0c0bce4f6f4</Id> + <AnnotationText>audio.mxf</AnnotationText> + <EditRate>24 1</EditRate> + <IntrinsicDuration>24</IntrinsicDuration> + <EntryPoint>0</EntryPoint> + <Duration>24</Duration> + </MainSound> + </AssetList> + </Reel> + </ReelList> +</CompositionPlaylist> diff --git a/test/ref/DCP/04040404-0404-0404-0404-040404040404_pkl.xml b/test/ref/DCP/04040404-0404-0404-0404-040404040404_pkl.xml new file mode 100644 index 00000000..76f1292a --- /dev/null +++ b/test/ref/DCP/04040404-0404-0404-0404-040404040404_pkl.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<PackingList xmlns="http://www.smpte-ra.org/schemas/429-8/2007/PKL"> + <Id>urn:uuid:caaf9bfe-2a00-4b95-b6d3-d6c0b181e17b</Id> + <AnnotationText>A Test DCP</AnnotationText> + <IssueDate>2012-07-16T06:28:27+00:00</IssueDate> + <Issuer>OpenDCP 0.0.25</Issuer> + <Creator>OpenDCP 0.0.25</Creator> + <AssetList> + <Asset> + <Id>urn:uuid:0a2335fe-09a3-4a19-b5ea-2e0308e727b3</Id> + <AnnotationText>video.mxf</AnnotationText> + <Hash>ETDlJzvJT98gAc2Y84/4AoXQuhs=</Hash> + <Size>26080</Size> + <Type>application/mxf</Type> + </Asset> + <Asset> + <Id>urn:uuid:ac884d5a-1d72-425b-a45b-d0c0bce4f6f4</Id> + <AnnotationText>audio.mxf</AnnotationText> + <Hash>duYsdzeBVwuv5EfxvLmshyBQdo0=</Hash> + <Size>305326</Size> + <Type>application/mxf</Type> + </Asset> + <Asset> + <Id>urn:uuid:488d58ab-474e-4b01-a2cb-a6ce1e5e70b3</Id> + <Hash>RanewioDBtecLpvB7W2v/g9/vos=</Hash> + <Size>1526</Size> + <Type>text/xml</Type> + </Asset> + </AssetList> +</PackingList> diff --git a/test/ref/DCP/ASSETMAP.xml b/test/ref/DCP/ASSETMAP.xml new file mode 100644 index 00000000..dff86855 --- /dev/null +++ b/test/ref/DCP/ASSETMAP.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<AssetMap xmlns="http://www.smpte-ra.org/schemas/429-9/2007/AM"> + <Id>urn:uuid:7193f3d3-4083-4e4d-9a35-b108dad4e305</Id> + <Creator>OpenDCP 0.0.25</Creator> + <VolumeCount>1</VolumeCount> + <IssueDate>2012-07-16T06:28:27+00:00</IssueDate> + <Issuer>OpenDCP 0.0.25</Issuer> + <AssetList> + <Asset> + <Id>urn:uuid:caaf9bfe-2a00-4b95-b6d3-d6c0b181e17b</Id> + <PackingList>true</PackingList> + <ChunkList> + <Chunk> + <Path>caaf9bfe-2a00-4b95-b6d3-d6c0b181e17b_pkl.xml</Path> + <VolumeIndex>1</VolumeIndex> + <Offset>0</Offset> + <Length>1049</Length> + </Chunk> + </ChunkList> + </Asset> + <Asset> + <Id>urn:uuid:488d58ab-474e-4b01-a2cb-a6ce1e5e70b3</Id> + <ChunkList> + <Chunk> + <Path>488d58ab-474e-4b01-a2cb-a6ce1e5e70b3_cpl.xml</Path> + <VolumeIndex>1</VolumeIndex> + <Offset>0</Offset> + <Length>1526</Length> + </Chunk> + </ChunkList> + </Asset> + <Asset> + <Id>urn:uuid:0a2335fe-09a3-4a19-b5ea-2e0308e727b3</Id> + <ChunkList> + <Chunk> + <Path>video.mxf</Path> + <VolumeIndex>1</VolumeIndex> + <Offset>0</Offset> + <Length>26080</Length> + </Chunk> + </ChunkList> + </Asset> + <Asset> + <Id>urn:uuid:ac884d5a-1d72-425b-a45b-d0c0bce4f6f4</Id> + <ChunkList> + <Chunk> + <Path>audio.mxf</Path> + <VolumeIndex>1</VolumeIndex> + <Offset>0</Offset> + <Length>305326</Length> + </Chunk> + </ChunkList> + </Asset> + </AssetList> +</AssetMap> diff --git a/test/ref/DCP/VOLINDEX.xml b/test/ref/DCP/VOLINDEX.xml new file mode 100644 index 00000000..f66c004a --- /dev/null +++ b/test/ref/DCP/VOLINDEX.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<VolumeIndex xmlns="http://www.smpte-ra.org/schemas/429-9/2007/AM"> + <Index>1</Index> +</VolumeIndex> diff --git a/test/ref/DCP/audio.mxf b/test/ref/DCP/audio.mxf Binary files differnew file mode 100644 index 00000000..06e9438d --- /dev/null +++ b/test/ref/DCP/audio.mxf diff --git a/test/ref/DCP/video.dump b/test/ref/DCP/video.dump new file mode 100644 index 00000000..c01dfd73 --- /dev/null +++ b/test/ref/DCP/video.dump @@ -0,0 +1,302 @@ +Dump an MXF file using MXFLib +- using dictionary "dict.xml" + +Partition at 0x00000000 is for BodySID 0x0000 +ClosedCompleteHeader + MajorVersion = 1 + MinorVersion = 2 + KAGSize = 1 + ThisPartition = 0 + PreviousPartition = 0 + FooterPartition = 25476 + HeaderByteCount = 16244 + IndexByteCount = 0 + IndexSID = 0 + BodyOffset = 0 + BodySID = 0 + OperationalPattern = MXF Specialized OP Atom + EssenceContainers + EssenceContainer = MXF-GC Generic Essence Multiple Mappings + EssenceContainer = MXF-GC JPEG-2000 Picture Mappings + +Header Metadata: + Preface + InstanceUID = {772726eb-422b-478c-869c-679e550ee103} + LastModifiedDate = 2012-07-16 17:28:27.000 + Version = 258 + ObjectModelVersion = 0 + PrimaryPackage -> Weak Reference to SourcePackage + Identifications + Identification = {7079b446-6e8c-4bfb-91d6-8e2053a0b4c2} + Identification -> Strong Reference to Identification + Identification + InstanceUID = {7079b446-6e8c-4bfb-91d6-8e2053a0b4c2} + ThisGenerationUID = {4f2bd67b-f257-4ed9-ac9d-6956bbe61c36} + CompanyName = OpenDCP + ProductName = OpenDCP + ProductVersion = Major="0", Minor="0", Patch="0", Build="0", Release="VersionUnknown" + VersionString = 0.0.25 + ProductUID = {43059a1d-0432-4101-b83f-736815acf31d} + ModificationDate = 2012-07-16 17:28:27.000 + ToolkitVersion = Major="1", Minor="8", Patch="44", Build="27240", Release="VersionReleased" + Platform = unix + ContentStorage = {f11189d2-31a5-4c7b-8608-59ff5d340245} + ContentStorage -> Strong Reference to ContentStorage + ContentStorage + InstanceUID = {f11189d2-31a5-4c7b-8608-59ff5d340245} + Packages + Package = {80a78644-dda6-43d7-a55b-77c4fec29654} + Package -> Strong Reference to MaterialPackage + MaterialPackage + InstanceUID = {80a78644-dda6-43d7-a55b-77c4fec29654} + PackageUID = [060a2b34.0101.0105.01010f20],13,00,00,00,{2542c57c-5a87-4834-918b-9f09153e10ca} + Name = AS-DCP Material Package + PackageCreationDate = 2012-07-16 17:28:27.000 + PackageModifiedDate = 2012-07-16 17:28:27.000 + Tracks + Tracks_Item = {79f6d86f-3f08-40b3-943b-e7e3b1819209} + Tracks_Item -> Strong Reference to Track + Track + InstanceUID = {79f6d86f-3f08-40b3-943b-e7e3b1819209} + TrackID = 1 + TrackNumber = 0 + TrackName = Timecode Track + Sequence = {6964beaf-d7fc-4f74-8d58-6a095850df00} + Sequence -> Strong Reference to Sequence + Sequence + InstanceUID = {6964beaf-d7fc-4f74-8d58-6a095850df00} + DataDefinition = SMPTE 12M Timecode Track + Duration = 24 + StructuralComponents + StructuralComponent = {9c28304b-6293-4651-8e20-ba11db057222} + StructuralComponent -> Strong Reference to TimecodeComponent + TimecodeComponent + InstanceUID = {9c28304b-6293-4651-8e20-ba11db057222} + DataDefinition = SMPTE 12M Timecode Track + Duration = 24 + RoundedTimecodeBase = 24 + StartTimecode = 0 + DropFrame = 0 + EditRate = 24/1 + Origin = 0 + Tracks_Item = {4cc1735a-c2e5-40c5-a3d8-4699d9d8f28a} + Tracks_Item -> Strong Reference to Track + Track + InstanceUID = {4cc1735a-c2e5-40c5-a3d8-4699d9d8f28a} + TrackID = 2 + TrackNumber = 0 + TrackName = Picture Track + Sequence = {85182d60-83d9-490d-9a6e-9939ea88593b} + Sequence -> Strong Reference to Sequence + Sequence + InstanceUID = {85182d60-83d9-490d-9a6e-9939ea88593b} + DataDefinition = Picture Essence Track + Duration = 24 + StructuralComponents + StructuralComponent = {8d1a4bab-3854-4110-a6c6-a8658947859e} + StructuralComponent -> Strong Reference to SourceClip + SourceClip + InstanceUID = {8d1a4bab-3854-4110-a6c6-a8658947859e} + DataDefinition = Picture Essence Track + Duration = 24 + StartPosition = 0 + SourcePackageID = [060a2b34.0101.0105.01010f20],13,00,00,00,{0a2335fe-09a3-4a19-b5ea-2e0308e727b3} + SourceTrackID = 2 + EditRate = 24/1 + Origin = 0 + Package = {24569e0a-10e9-42b5-bbb6-c81b76ec6c7e} + Package -> Strong Reference to SourcePackage + SourcePackage + InstanceUID = {24569e0a-10e9-42b5-bbb6-c81b76ec6c7e} + PackageUID = [060a2b34.0101.0105.01010f20],13,00,00,00,{0a2335fe-09a3-4a19-b5ea-2e0308e727b3} + Name = File Package: SMPTE 429-4 frame wrapping of JPEG 2000 codestreams + PackageCreationDate = 2012-07-16 17:28:27.000 + PackageModifiedDate = 2012-07-16 17:28:27.000 + Tracks + Tracks_Item = {072c2857-9116-4802-98bb-1c585580baae} + Tracks_Item -> Strong Reference to Track + Track + InstanceUID = {072c2857-9116-4802-98bb-1c585580baae} + TrackID = 1 + TrackNumber = 0 + TrackName = Timecode Track + Sequence = {82f075be-eb53-47c2-b6d6-5aa053756248} + Sequence -> Strong Reference to Sequence + Sequence + InstanceUID = {82f075be-eb53-47c2-b6d6-5aa053756248} + DataDefinition = SMPTE 12M Timecode Track + Duration = 24 + StructuralComponents + StructuralComponent = {29dc1968-3487-4ef2-a156-949afe99fdab} + StructuralComponent -> Strong Reference to TimecodeComponent + TimecodeComponent + InstanceUID = {29dc1968-3487-4ef2-a156-949afe99fdab} + DataDefinition = SMPTE 12M Timecode Track + Duration = 24 + RoundedTimecodeBase = 24 + StartTimecode = 86400 + DropFrame = 0 + EditRate = 24/1 + Origin = 0 + Tracks_Item = {f2f685f0-25f5-4d65-9592-7f511c730706} + Tracks_Item -> Strong Reference to Track + Track + InstanceUID = {f2f685f0-25f5-4d65-9592-7f511c730706} + TrackID = 2 + TrackNumber = 352389121 + TrackName = Picture Track + Sequence = {90bbbbca-9042-4244-9db2-939c83cd1a84} + Sequence -> Strong Reference to Sequence + Sequence + InstanceUID = {90bbbbca-9042-4244-9db2-939c83cd1a84} + DataDefinition = Picture Essence Track + Duration = 24 + StructuralComponents + StructuralComponent = {f1f17679-2c07-458c-af0d-cfdadde6a705} + StructuralComponent -> Strong Reference to SourceClip + SourceClip + InstanceUID = {f1f17679-2c07-458c-af0d-cfdadde6a705} + DataDefinition = Picture Essence Track + Duration = 24 + StartPosition = 0 + SourcePackageID = [00000000.0000.0000.00000000],00,00,00,00,[00000000.0000.0000.00000000.00000000] + SourceTrackID = 0 + EditRate = 24/1 + Origin = 0 + Descriptor = {d33fd7d1-a6ce-4f9f-ade5-db60bf89e34b} + Descriptor -> Strong Reference to RGBAEssenceDescriptor + RGBAEssenceDescriptor + InstanceUID = {d33fd7d1-a6ce-4f9f-ade5-db60bf89e34b} + SubDescriptors + SubDescriptor = {d4700ab9-4c69-44d1-a849-8920b60b017e} + SubDescriptor -> Strong Reference to JPEG2000PictureSubDescriptor + JPEG2000PictureSubDescriptor + InstanceUID = {d4700ab9-4c69-44d1-a849-8920b60b017e} + Rsiz = 3 + Xsiz = 32 + Ysiz = 32 + XOsiz = 0 + YOsiz = 0 + XTsiz = 32 + YTsiz = 32 + XTOsiz = 0 + YTOsiz = 0 + Csiz = 3 + PictureComponentSizing + PictureComponentSize = Ssiz="7", XRsiz="1", YRsiz="1" + PictureComponentSize = Ssiz="7", XRsiz="1", YRsiz="1" + PictureComponentSize = Ssiz="7", XRsiz="1", YRsiz="1" + CodingStyleDefault = Scod="1", SGcod="ProgressionOrder="4", NumberOfLayers="1", MultipleComponentTransformation="1"", SPcod="DecompositionLevels="5", CodeblockWidth="3", CodeblockHeight="3", CodeblockStyle="0", Transformation="0"", PrecinctSize="119, 136, 136, 136, 136, 136" + QuantizationDefault = Sqcd="66", SPqcd="119, 32, 118, 240, 118, 240, 118, 192, 111, 0, 111, 0, 110, 224, 103, 80, 103, 80, 103, 104, 80, 5, 80, 5, 80, 71, 87, 211, 87, 211, 87, 98" + LinkedTrackID = 2 + SampleRate = 24/1 + ContainerDuration = 24 + EssenceContainer = MXF-GC JPEG-2000 Picture Mappings + FrameLayout = 0 + StoredWidth = 32 + StoredHeight = 32 + AspectRatio = 32/32 + PictureEssenceCoding = [060e2b34.0401.0109.04010202.03010103] + ComponentMaxRef = 4095 + ComponentMinRef = 0 + EssenceContainerData + EssenceContainer = {7d384edf-2ff0-4b50-ab06-9165947b289b} + EssenceContainer -> Strong Reference to EssenceContainerData + EssenceContainerData + InstanceUID = {7d384edf-2ff0-4b50-ab06-9165947b289b} + LinkedPackageUID = [060a2b34.0101.0105.01010f20],13,00,00,00,{0a2335fe-09a3-4a19-b5ea-2e0308e727b3} + IndexSID = 129 + BodySID = 1 + OperationalPattern = MXF Specialized OP Atom + EssenceContainers + EssenceContainer = MXF-GC Generic Essence Multiple Mappings + EssenceContainer = MXF-GC JPEG-2000 Picture Mappings + DMSchemes + +No index table in this partition + +Partition at 0x00004000 is for BodySID 0x0001 + +Partition at 0x00006384 is for BodySID 0x0000 +CompleteFooter + MajorVersion = 1 + MinorVersion = 2 + KAGSize = 1 + ThisPartition = 25476 + PreviousPartition = 16384 + FooterPartition = 25476 + HeaderByteCount = 0 + IndexByteCount = 404 + IndexSID = 129 + BodyOffset = 0 + BodySID = 0 + OperationalPattern = MXF Specialized OP Atom + EssenceContainers + EssenceContainer = MXF-GC Generic Essence Multiple Mappings + EssenceContainer = MXF-GC JPEG-2000 Picture Mappings +No header metadata in this partition + +Index Table Segment (first edit unit = 0, duration = 24) : + Indexing BodySID 0x0001 from IndexSID 0x0081 + + Bytestream Order: + EditUnit 0 for stream 0 is at 0x00000000, Flags=00 *Exact* + EditUnit 1 for stream 0 is at 0x00000175, Flags=00 *Exact* + EditUnit 2 for stream 0 is at 0x000002ea, Flags=00 *Exact* + EditUnit 3 for stream 0 is at 0x0000045f, Flags=00 *Exact* + EditUnit 4 for stream 0 is at 0x000005d4, Flags=00 *Exact* + EditUnit 5 for stream 0 is at 0x00000749, Flags=00 *Exact* + EditUnit 6 for stream 0 is at 0x000008be, Flags=00 *Exact* + EditUnit 7 for stream 0 is at 0x00000a33, Flags=00 *Exact* + EditUnit 8 for stream 0 is at 0x00000ba8, Flags=00 *Exact* + EditUnit 9 for stream 0 is at 0x00000d1d, Flags=00 *Exact* + EditUnit 10 for stream 0 is at 0x00000e92, Flags=00 *Exact* + EditUnit 11 for stream 0 is at 0x00001007, Flags=00 *Exact* + EditUnit 12 for stream 0 is at 0x0000117c, Flags=00 *Exact* + EditUnit 13 for stream 0 is at 0x000012f1, Flags=00 *Exact* + EditUnit 14 for stream 0 is at 0x00001466, Flags=00 *Exact* + EditUnit 15 for stream 0 is at 0x000015db, Flags=00 *Exact* + EditUnit 16 for stream 0 is at 0x00001750, Flags=00 *Exact* + EditUnit 17 for stream 0 is at 0x000018c5, Flags=00 *Exact* + EditUnit 18 for stream 0 is at 0x00001a3a, Flags=00 *Exact* + EditUnit 19 for stream 0 is at 0x00001baf, Flags=00 *Exact* + EditUnit 20 for stream 0 is at 0x00001d24, Flags=00 *Exact* + EditUnit 21 for stream 0 is at 0x00001e99, Flags=00 *Exact* + EditUnit 22 for stream 0 is at 0x0000200e, Flags=00 *Exact* + EditUnit 23 for stream 0 is at 0x00002183, Flags=00 *Exact* + + Presentation Order: + EditUnit 0 for stream 0 is at 0x00000000, Flags=00 *Exact* + EditUnit 1 for stream 0 is at 0x00000175, Flags=00 *Exact* + EditUnit 2 for stream 0 is at 0x000002ea, Flags=00 *Exact* + EditUnit 3 for stream 0 is at 0x0000045f, Flags=00 *Exact* + EditUnit 4 for stream 0 is at 0x000005d4, Flags=00 *Exact* + EditUnit 5 for stream 0 is at 0x00000749, Flags=00 *Exact* + EditUnit 6 for stream 0 is at 0x000008be, Flags=00 *Exact* + EditUnit 7 for stream 0 is at 0x00000a33, Flags=00 *Exact* + EditUnit 8 for stream 0 is at 0x00000ba8, Flags=00 *Exact* + EditUnit 9 for stream 0 is at 0x00000d1d, Flags=00 *Exact* + EditUnit 10 for stream 0 is at 0x00000e92, Flags=00 *Exact* + EditUnit 11 for stream 0 is at 0x00001007, Flags=00 *Exact* + EditUnit 12 for stream 0 is at 0x0000117c, Flags=00 *Exact* + EditUnit 13 for stream 0 is at 0x000012f1, Flags=00 *Exact* + EditUnit 14 for stream 0 is at 0x00001466, Flags=00 *Exact* + EditUnit 15 for stream 0 is at 0x000015db, Flags=00 *Exact* + EditUnit 16 for stream 0 is at 0x00001750, Flags=00 *Exact* + EditUnit 17 for stream 0 is at 0x000018c5, Flags=00 *Exact* + EditUnit 18 for stream 0 is at 0x00001a3a, Flags=00 *Exact* + EditUnit 19 for stream 0 is at 0x00001baf, Flags=00 *Exact* + EditUnit 20 for stream 0 is at 0x00001d24, Flags=00 *Exact* + EditUnit 21 for stream 0 is at 0x00001e99, Flags=00 *Exact* + EditUnit 22 for stream 0 is at 0x0000200e, Flags=00 *Exact* + EditUnit 23 for stream 0 is at 0x00002183, Flags=00 *Exact* + +Read RIP + BodySID 0x0000 is at 0x00000000 and is not loaded + BodySID 0x0001 is at 0x00004000 and is not loaded + BodySID 0x0000 is at 0x00006384 and is not loaded + +Scanned RIP + BodySID 0x0000 is at 0x00000000 type ClosedCompleteHeader + BodySID 0x0001 is at 0x00004000 type ClosedCompleteBodyPartition + BodySID 0x0000 is at 0x00006384 type CompleteFooter diff --git a/test/ref/DCP/video.mxf b/test/ref/DCP/video.mxf Binary files differnew file mode 100644 index 00000000..9b11f99e --- /dev/null +++ b/test/ref/DCP/video.mxf diff --git a/test/ref/j2c/1.j2c b/test/ref/j2c/1.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/1.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/10.j2c b/test/ref/j2c/10.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/10.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/11.j2c b/test/ref/j2c/11.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/11.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/12.j2c b/test/ref/j2c/12.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/12.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/13.j2c b/test/ref/j2c/13.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/13.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/14.j2c b/test/ref/j2c/14.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/14.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/15.j2c b/test/ref/j2c/15.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/15.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/16.j2c b/test/ref/j2c/16.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/16.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/17.j2c b/test/ref/j2c/17.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/17.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/18.j2c b/test/ref/j2c/18.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/18.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/19.j2c b/test/ref/j2c/19.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/19.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/2.j2c b/test/ref/j2c/2.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/2.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/20.j2c b/test/ref/j2c/20.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/20.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/21.j2c b/test/ref/j2c/21.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/21.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/22.j2c b/test/ref/j2c/22.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/22.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/23.j2c b/test/ref/j2c/23.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/23.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/24.j2c b/test/ref/j2c/24.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/24.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/3.j2c b/test/ref/j2c/3.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/3.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/4.j2c b/test/ref/j2c/4.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/4.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/5.j2c b/test/ref/j2c/5.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/5.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/6.j2c b/test/ref/j2c/6.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/6.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/7.j2c b/test/ref/j2c/7.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/7.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/8.j2c b/test/ref/j2c/8.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/8.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/j2c/9.j2c b/test/ref/j2c/9.j2c new file mode 120000 index 00000000..7e4f07ef --- /dev/null +++ b/test/ref/j2c/9.j2c @@ -0,0 +1 @@ +../../data/32x32_red_square.j2c
\ No newline at end of file diff --git a/test/ref/make.sh b/test/ref/make.sh new file mode 100644 index 00000000..1d04e244 --- /dev/null +++ b/test/ref/make.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +rm -rf DCP +mkdir DCP +opendcp_mxf -i j2c -o DCP/video.mxf -r 24 +opendcp_mxf -i wav -o DCP/audio.mxf -r 24 +opendcp_xml --reel DCP/video.mxf DCP/audio.mxf -k feature -t "A Test DCP" -a "A Test DCP" +mv *.xml DCP +mv DCP/*_pkl.xml DCP/04040404-0404-0404-0404-040404040404_pkl.xml +mv DCP/*_cpl.xml DCP/02020202-0202-0202-0202-020202020202_cpl.xml diff --git a/test/ref/wav/1.wav b/test/ref/wav/1.wav new file mode 120000 index 00000000..6163f166 --- /dev/null +++ b/test/ref/wav/1.wav @@ -0,0 +1 @@ +../../data/1s_24-bit_48k_silence.wav
\ No newline at end of file diff --git a/test/ref/wav/2.wav b/test/ref/wav/2.wav new file mode 120000 index 00000000..6163f166 --- /dev/null +++ b/test/ref/wav/2.wav @@ -0,0 +1 @@ +../../data/1s_24-bit_48k_silence.wav
\ No newline at end of file diff --git a/test/tests.cc b/test/tests.cc new file mode 100644 index 00000000..dd9ed07d --- /dev/null +++ b/test/tests.cc @@ -0,0 +1,60 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + 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 <boost/filesystem.hpp> +#include "dcp.h" +#include "util.h" +#include "tags.h" + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE libdcp_test +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace boost; + +BOOST_AUTO_TEST_CASE (dcp_test) +{ + libdcp::libdcp_test = true; + + libdcp::Tags* t = libdcp::Tags::instance (); + t->issuer = "OpenDCP 0.0.25"; + t->creator = "OpenDCP 0.0.25"; + t->company_name = "OpenDCP"; + t->product_name = "OpenDCP"; + t->product_version = "0.0.25"; + + filesystem::remove_all ("build/test/foo"); + filesystem::create_directories ("build/test/foo"); + libdcp::DCP d ("build/test/foo", "A Test DCP", libdcp::DCP::FEATURE, 24, 24); + + list<string> j2cs; + for (int i = 0; i < 24; ++i) { + j2cs.push_back ("test/data/32x32_red_square.j2c"); + } + d.add_picture_asset (j2cs, 32, 32); + + list<string> wavs; + for (int i = 0; i < 2; ++i) { + wavs.push_back ("test/data/1s_24-bit_48k_silence.wav"); + } + d.add_sound_asset (wavs); + + d.write_xml (); +} diff --git a/test/wscript b/test/wscript new file mode 100644 index 00000000..cd10983a --- /dev/null +++ b/test/wscript @@ -0,0 +1,15 @@ +def configure(conf): + conf.check_cxx(fragment = """ + #define BOOST_TEST_MODULE Config test\n + #include <boost/test/unit_test.hpp>\n + int main() {} + """, msg = 'Checking for boost unit testing library', lib = 'boost_unit_test_framework', uselib_store = 'BOOST_TEST') + +def build(bld): + obj = bld(features = 'cxx cxxprogram') + obj.name = 'tests' + obj.uselib = 'BOOST_TEST' + obj.use = 'libdcp' + obj.source = 'tests.cc' + obj.target = 'tests' + obj.install_path = '' Binary files differdiff --git a/wscript b/wscript new file mode 100644 index 00000000..b28c366b --- /dev/null +++ b/wscript @@ -0,0 +1,43 @@ +APPNAME = 'libdcp' +VERSION = '0.01pre' + +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('compiler_cxx') + conf.env.append_value('CXXFLAGS', ['-Wall', '-Werror', '-Wextra', '-O2', '-D_FILE_OFFSET_BITS=64']) + conf.env.append_value('CXXFLAGS', ['-DLIBDCP_VERSION="%s"' % VERSION]) + + conf.check_cc(msg = 'Checking for libkumu', + function_name = 'Kumu::Version', + header_name = 'KM_util.h', + lib = 'kumu', + uselib_store = 'KUMU', + mandatory = True) + + conf.check_cc(msg = 'Checking for asdcplib', + function_name = 'ASDCP::Version', + header_name = 'AS_DCP.h', + lib = 'asdcp', + uselib_store = 'ASDCP', + mandatory = True) + + conf.check_cfg(package = 'openssl', args = '--cflags --libs', uselib_store = 'OPENSSL', mandatory = True) + conf.check_cfg(package = 'sigc++-2.0', args = '--cflags --libs', uselib_store = 'SIGC++', mandatory = True) + + conf.check_cxx(fragment = """ + #include <boost/filesystem.hpp>\n + int main() { boost::filesystem::copy_file ("a", "b"); }\n + """, + msg = 'Checking for boost filesystem library', + libpath = '/usr/local/lib', + lib = ['boost_filesystem', 'boost_system'], + uselib_store = 'BOOST_FILESYSTEM') + + conf.recurse('test') + +def build(bld): + bld.recurse('src') + bld.recurse('test') + |
