Fix incorrect container size when loading a VF/OV combination into the player.
[dcpomatic.git] / src / lib / dcp_examiner.cc
1 /*
2     Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 #include "dcp_examiner.h"
22 #include "dcp_content.h"
23 #include "exceptions.h"
24 #include "image.h"
25 #include "config.h"
26 #include <dcp/dcp.h>
27 #include <dcp/decrypted_kdm.h>
28 #include <dcp/cpl.h>
29 #include <dcp/reel.h>
30 #include <dcp/reel_picture_asset.h>
31 #include <dcp/reel_sound_asset.h>
32 #include <dcp/mono_picture_asset.h>
33 #include <dcp/mono_picture_asset_reader.h>
34 #include <dcp/mono_picture_frame.h>
35 #include <dcp/stereo_picture_asset.h>
36 #include <dcp/stereo_picture_asset_reader.h>
37 #include <dcp/stereo_picture_frame.h>
38 #include <dcp/sound_asset.h>
39 #include <dcp/sound_asset_reader.h>
40 #include <dcp/subtitle_asset.h>
41 #include <dcp/reel_subtitle_asset.h>
42 #include <dcp/sound_asset.h>
43 #include <boost/foreach.hpp>
44 #include <iostream>
45
46 #include "i18n.h"
47
48 using std::list;
49 using std::cout;
50 using std::runtime_error;
51 using boost::shared_ptr;
52 using boost::dynamic_pointer_cast;
53
54 DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content)
55         : DCP (content)
56         , _video_length (0)
57         , _audio_length (0)
58         , _has_video (false)
59         , _has_audio (false)
60         , _has_subtitles (false)
61         , _encrypted (false)
62         , _needs_assets (false)
63         , _kdm_valid (false)
64         , _three_d (false)
65 {
66         shared_ptr<dcp::CPL> cpl;
67
68         if (content->cpl ()) {
69                 /* Use the CPL that the content was using before */
70                 BOOST_FOREACH (shared_ptr<dcp::CPL> i, cpls()) {
71                         if (i->id() == content->cpl().get()) {
72                                 cpl = i;
73                         }
74                 }
75         } else {
76                 /* Choose the CPL with the fewest unsatisfied references */
77
78                 int least_unsatisfied = INT_MAX;
79
80                 BOOST_FOREACH (shared_ptr<dcp::CPL> i, cpls()) {
81                         int unsatisfied = 0;
82                         BOOST_FOREACH (shared_ptr<dcp::Reel> j, i->reels()) {
83                                 if (j->main_picture() && !j->main_picture()->asset_ref().resolved()) {
84                                         ++unsatisfied;
85                                 }
86                                 if (j->main_sound() && !j->main_sound()->asset_ref().resolved()) {
87                                         ++unsatisfied;
88                                 }
89                                 if (j->main_subtitle() && !j->main_subtitle()->asset_ref().resolved()) {
90                                         ++unsatisfied;
91                                 }
92                         }
93
94                         if (unsatisfied < least_unsatisfied) {
95                                 least_unsatisfied = unsatisfied;
96                                 cpl = i;
97                         }
98                 }
99         }
100
101         if (!cpl) {
102                 throw DCPError ("No CPLs found in DCP");
103         }
104
105         _cpl = cpl->id ();
106         _name = cpl->content_title_text ();
107
108         BOOST_FOREACH (shared_ptr<dcp::Reel> i, cpl->reels()) {
109
110                 if (i->main_picture ()) {
111                         if (!i->main_picture()->asset_ref().resolved()) {
112                                 /* We are missing this asset so we can't continue; examination will be repeated later */
113                                 _needs_assets = true;
114                                 return;
115                         }
116
117                         dcp::Fraction const frac = i->main_picture()->edit_rate ();
118                         float const fr = float(frac.numerator) / frac.denominator;
119                         if (!_video_frame_rate) {
120                                 _video_frame_rate = fr;
121                         } else if (_video_frame_rate.get() != fr) {
122                                 throw DCPError (_("Mismatched frame rates in DCP"));
123                         }
124
125                         _has_video = true;
126                         shared_ptr<dcp::PictureAsset> asset = i->main_picture()->asset ();
127                         if (!_video_size) {
128                                 _video_size = asset->size ();
129                         } else if (_video_size.get() != asset->size ()) {
130                                 throw DCPError (_("Mismatched video sizes in DCP"));
131                         }
132
133                         _video_length += i->main_picture()->duration();
134                 }
135
136                 if (i->main_sound ()) {
137                         if (!i->main_sound()->asset_ref().resolved()) {
138                                 /* We are missing this asset so we can't continue; examination will be repeated later */
139                                 _needs_assets = true;
140                                 return;
141                         }
142
143                         _has_audio = true;
144                         shared_ptr<dcp::SoundAsset> asset = i->main_sound()->asset ();
145
146                         if (!_audio_channels) {
147                                 _audio_channels = asset->channels ();
148                         } else if (_audio_channels.get() != asset->channels ()) {
149                                 throw DCPError (_("Mismatched audio channel counts in DCP"));
150                         }
151
152                         if (!_audio_frame_rate) {
153                                 _audio_frame_rate = asset->sampling_rate ();
154                         } else if (_audio_frame_rate.get() != asset->sampling_rate ()) {
155                                 throw DCPError (_("Mismatched audio sample rates in DCP"));
156                         }
157
158                         _audio_length += i->main_sound()->duration();
159                 }
160
161                 if (i->main_subtitle ()) {
162                         if (!i->main_subtitle()->asset_ref().resolved()) {
163                                 /* We are missing this asset so we can't continue; examination will be repeated later */
164                                 _needs_assets = true;
165                                 return;
166                         }
167
168                         _has_subtitles = true;
169                 }
170
171                 if (i->main_picture()) {
172                         _reel_lengths.push_back (i->main_picture()->duration());
173                 } else if (i->main_sound()) {
174                         _reel_lengths.push_back (i->main_sound()->duration());
175                 } else if (i->main_subtitle()) {
176                         _reel_lengths.push_back (i->main_subtitle()->duration());
177                 }
178         }
179
180         _encrypted = cpl->encrypted ();
181         _kdm_valid = true;
182
183         /* Check that we can read the first picture, sound and subtitle frames of each reel */
184         try {
185                 BOOST_FOREACH (shared_ptr<dcp::Reel> i, cpl->reels()) {
186                         shared_ptr<dcp::PictureAsset> pic = i->main_picture()->asset ();
187                         shared_ptr<dcp::MonoPictureAsset> mono = dynamic_pointer_cast<dcp::MonoPictureAsset> (pic);
188                         shared_ptr<dcp::StereoPictureAsset> stereo = dynamic_pointer_cast<dcp::StereoPictureAsset> (pic);
189
190                         if (mono) {
191                                 mono->start_read()->get_frame(0)->xyz_image ();
192                         } else {
193                                 stereo->start_read()->get_frame(0)->xyz_image (dcp::EYE_LEFT);
194                         }
195
196                         if (i->main_sound()) {
197                                 shared_ptr<dcp::SoundAsset> sound = i->main_sound()->asset ();
198                                 i->main_sound()->asset()->start_read()->get_frame(0);
199                         }
200
201                         if (i->main_subtitle()) {
202                                 i->main_subtitle()->asset()->subtitles ();
203                         }
204                 }
205         } catch (dcp::DCPReadError& e) {
206                 _kdm_valid = false;
207         } catch (dcp::MiscError& e) {
208                 _kdm_valid = false;
209         }
210
211         DCPOMATIC_ASSERT (cpl->standard ());
212         _standard = cpl->standard().get();
213         _three_d = !cpl->reels().empty() && cpl->reels().front()->main_picture() &&
214                 dynamic_pointer_cast<dcp::StereoPictureAsset> (cpl->reels().front()->main_picture()->asset());
215
216         _cpl = cpl->id ();
217 }