2 Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "parse/cpl.h"
24 #include "picture_asset.h"
25 #include "sound_asset.h"
26 #include "subtitle_asset.h"
27 #include "parse/asset_map.h"
32 using std::stringstream;
36 using boost::shared_ptr;
37 using namespace libdcp;
39 CPL::CPL (string directory, string name, ContentKind content_kind, int length, int frames_per_second)
40 : _directory (directory)
42 , _content_kind (content_kind)
44 , _fps (frames_per_second)
49 /** Construct a CPL object from a XML file.
50 * @param directory The directory containing this CPL's DCP.
51 * @param file The CPL XML filename.
52 * @param asset_map The corresponding asset map.
53 * @param require_mxfs true to throw an exception if a required MXF file does not exist.
55 CPL::CPL (string directory, string file, shared_ptr<const libdcp::parse::AssetMap> asset_map, bool require_mxfs)
56 : _directory (directory)
57 , _content_kind (FEATURE)
62 shared_ptr<parse::CPL> cpl;
64 cpl.reset (new parse::CPL (file));
65 } catch (FileError& e) {
66 boost::throw_exception (FileError ("could not load CPL file", file));
69 /* Now cherry-pick the required bits into our own data structure */
71 _name = cpl->annotation_text;
72 _content_kind = cpl->content_kind;
74 for (list<shared_ptr<libdcp::parse::Reel> >::iterator i = cpl->reels.begin(); i != cpl->reels.end(); ++i) {
76 shared_ptr<parse::Picture> p;
78 if ((*i)->asset_list->main_picture) {
79 p = (*i)->asset_list->main_picture;
81 p = (*i)->asset_list->main_stereoscopic_picture;
84 _fps = p->edit_rate.numerator;
85 _length += p->duration;
87 shared_ptr<PictureAsset> picture;
88 shared_ptr<SoundAsset> sound;
89 shared_ptr<SubtitleAsset> subtitle;
91 /* Some rather twisted logic to decide if we are 3D or not;
92 some DCPs give a MainStereoscopicPicture to indicate 3D, others
93 just have a FrameRate twice the EditRate and apparently
94 expect you to divine the fact that they are hence 3D.
97 if (!(*i)->asset_list->main_stereoscopic_picture && p->edit_rate == p->frame_rate) {
100 picture.reset (new MonoPictureAsset (
102 asset_map->asset_from_id (p->id)->chunks.front()->path
106 picture->set_entry_point (p->entry_point);
107 picture->set_duration (p->duration);
108 } catch (MXFFileError) {
116 picture.reset (new StereoPictureAsset (
118 asset_map->asset_from_id (p->id)->chunks.front()->path,
124 picture->set_entry_point (p->entry_point);
125 picture->set_duration (p->duration);
127 } catch (MXFFileError) {
135 if ((*i)->asset_list->main_sound) {
138 sound.reset (new SoundAsset (
140 asset_map->asset_from_id ((*i)->asset_list->main_sound->id)->chunks.front()->path
144 sound->set_entry_point ((*i)->asset_list->main_sound->entry_point);
145 sound->set_duration ((*i)->asset_list->main_sound->duration);
146 } catch (MXFFileError) {
153 if ((*i)->asset_list->main_subtitle) {
155 subtitle.reset (new SubtitleAsset (
157 asset_map->asset_from_id ((*i)->asset_list->main_subtitle->id)->chunks.front()->path
161 subtitle->set_entry_point ((*i)->asset_list->main_subtitle->entry_point);
162 subtitle->set_duration ((*i)->asset_list->main_subtitle->duration);
165 _reels.push_back (shared_ptr<Reel> (new Reel (picture, sound, subtitle)));
170 CPL::add_reel (shared_ptr<const Reel> reel)
172 _reels.push_back (reel);
176 CPL::write_xml (XMLMetadata const & metadata) const
178 boost::filesystem::path p;
181 s << _uuid << "_cpl.xml";
183 ofstream os (p.string().c_str());
185 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
186 << "<CompositionPlaylist xmlns=\"http://www.smpte-ra.org/schemas/429-7/2006/CPL\">\n"
187 << " <Id>urn:uuid:" << _uuid << "</Id>\n"
188 << " <AnnotationText>" << _name << "</AnnotationText>\n"
189 << " <IssueDate>" << metadata.issue_date << "</IssueDate>\n"
190 << " <Creator>" << metadata.creator << "</Creator>\n"
191 << " <ContentTitleText>" << _name << "</ContentTitleText>\n"
192 << " <ContentKind>" << content_kind_to_string (_content_kind) << "</ContentKind>\n"
193 << " <ContentVersion>\n"
194 << " <Id>urn:uri:" << _uuid << "_" << metadata.issue_date << "</Id>\n"
195 << " <LabelText>" << _uuid << "_" << metadata.issue_date << "</LabelText>\n"
196 << " </ContentVersion>\n"
197 << " <RatingList/>\n"
200 for (list<shared_ptr<const Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) {
201 (*i)->write_to_cpl (os);
204 os << " </ReelList>\n"
205 << "</CompositionPlaylist>\n";
209 _digest = make_digest (p.string ());
210 _length = boost::filesystem::file_size (p.string ());
214 CPL::write_to_pkl (ostream& s) const
217 << " <Id>urn:uuid:" << _uuid << "</Id>\n"
218 << " <Hash>" << _digest << "</Hash>\n"
219 << " <Size>" << _length << "</Size>\n"
220 << " <Type>text/xml</Type>\n"
224 list<shared_ptr<const Asset> >
227 list<shared_ptr<const Asset> > a;
228 for (list<shared_ptr<const Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) {
229 if ((*i)->main_picture ()) {
230 a.push_back ((*i)->main_picture ());
232 if ((*i)->main_sound ()) {
233 a.push_back ((*i)->main_sound ());
235 if ((*i)->main_subtitle ()) {
236 a.push_back ((*i)->main_subtitle ());
244 CPL::write_to_assetmap (ostream& s) const
247 << " <Id>urn:uuid:" << _uuid << "</Id>\n"
250 << " <Path>" << _uuid << "_cpl.xml</Path>\n"
251 << " <VolumeIndex>1</VolumeIndex>\n"
252 << " <Offset>0</Offset>\n"
253 << " <Length>" << _length << "</Length>\n"
262 CPL::equals (CPL const & other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
264 if (_name != other._name) {
265 note (ERROR, "names differ");
269 if (_content_kind != other._content_kind) {
270 note (ERROR, "content kinds differ");
274 if (_fps != other._fps) {
275 note (ERROR, "frames per second differ");
279 if (_length != other._length) {
280 note (ERROR, "lengths differ");
284 if (_reels.size() != other._reels.size()) {
285 note (ERROR, "reel counts differ");
289 list<shared_ptr<const Reel> >::const_iterator a = _reels.begin ();
290 list<shared_ptr<const Reel> >::const_iterator b = other._reels.begin ();
292 while (a != _reels.end ()) {
293 if (!(*a)->equals (*b, opt, note)) {