summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2012-07-16 19:24:44 +0100
committerCarl Hetherington <cth@carlh.net>2012-07-16 19:24:44 +0100
commit7d48446b5efdf795df1ce22d6d9ed3ebe85d3381 (patch)
treef492aebd71fae087e7903dafc097d3899cff8481 /src
Import.
Diffstat (limited to 'src')
-rw-r--r--src/asset.cc81
-rw-r--r--src/asset.h56
-rw-r--r--src/dcp.cc270
-rw-r--r--src/dcp.h72
-rw-r--r--src/picture_asset.cc92
-rw-r--r--src/picture_asset.h37
-rw-r--r--src/sound_asset.cc131
-rw-r--r--src/sound_asset.h33
-rw-r--r--src/tags.cc46
-rw-r--r--src/tags.h42
-rw-r--r--src/util.cc95
-rw-r--r--src/util.h28
-rw-r--r--src/wscript14
13 files changed, 997 insertions, 0 deletions
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'