fed5068639e23ba350b6433f1c58e1f9d5d22ae2
[dcpomatic.git] / film_state.cc
1 /*
2     Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
3
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.
8
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.
13
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.
17
18 */
19
20 /** @file src/film_state.cc
21  *  @brief The state of a Film.  This is separate from Film so that
22  *  state can easily be copied and kept around for reference
23  *  by long-running jobs.  This avoids the jobs getting confused
24  *  by the user changing Film settings during their run.
25  */
26
27 #include <fstream>
28 #include <string>
29 #include <iomanip>
30 #include <sstream>
31 #include <boost/filesystem.hpp>
32 #include <boost/date_time.hpp>
33 #include "film_state.h"
34 #include "scaler.h"
35 #include "filter.h"
36 #include "format.h"
37 #include "dcp_content_type.h"
38 #include "util.h"
39 #include "exceptions.h"
40
41 using namespace std;
42 using namespace boost;
43
44 /** Write state to a stream.
45  *  @param f Stream to write to.
46  */
47 void
48 FilmState::write_metadata (ofstream& f) const
49 {
50         /* User stuff */
51         f << "name " << name << "\n";
52         f << "content " << content << "\n";
53         if (dcp_content_type) {
54                 f << "dcp_content_type " << dcp_content_type->pretty_name () << "\n";
55         }
56         f << "frames_per_second " << frames_per_second << "\n";
57         if (format) {
58                 f << "format " << format->as_metadata () << "\n";
59         }
60         f << "left_crop " << crop.left << "\n";
61         f << "right_crop " << crop.right << "\n";
62         f << "top_crop " << crop.top << "\n";
63         f << "bottom_crop " << crop.bottom << "\n";
64         for (vector<Filter const *>::const_iterator i = filters.begin(); i != filters.end(); ++i) {
65                 f << "filter " << (*i)->id () << "\n";
66         }
67         f << "scaler " << scaler->id () << "\n";
68         f << "dcp_frames " << dcp_frames << "\n";
69
70         f << "dcp_trim_action ";
71         switch (dcp_trim_action) {
72         case CUT:
73                 f << "cut\n";
74                 break;
75         case BLACK_OUT:
76                 f << "black_out\n";
77                 break;
78         }
79         
80         f << "dcp_ab " << (dcp_ab ? "1" : "0") << "\n";
81         f << "audio_gain " << audio_gain << "\n";
82         f << "audio_delay " << audio_delay << "\n";
83         f << "still_duration " << still_duration << "\n";
84         f << "with_subtitles " << with_subtitles << "\n";
85         f << "subtitle_offset " << subtitle_offset << "\n";
86         f << "subtitle_scale " << subtitle_scale << "\n";
87
88         /* Cached stuff; this is information about our content; we could
89            look it up each time, but that's slow.
90         */
91         for (vector<int>::const_iterator i = thumbs.begin(); i != thumbs.end(); ++i) {
92                 f << "thumb " << *i << "\n";
93         }
94         f << "width " << size.width << "\n";
95         f << "height " << size.height << "\n";
96         f << "length " << length << "\n";
97         f << "audio_channels " << audio_channels << "\n";
98         f << "audio_sample_rate " << audio_sample_rate << "\n";
99         f << "audio_sample_format " << audio_sample_format_to_string (audio_sample_format) << "\n";
100         f << "content_digest " << content_digest << "\n";
101         f << "has_subtitles " << has_subtitles << "\n";
102 }
103
104 /** Read state from a key / value pair.
105  *  @param k Key.
106  *  @param v Value.
107  */
108 void
109 FilmState::read_metadata (string k, string v)
110 {
111         /* User-specified stuff */
112         if (k == "name") {
113                 name = v;
114         } else if (k == "content") {
115                 content = v;
116         } else if (k == "dcp_content_type") {
117                 dcp_content_type = DCPContentType::from_pretty_name (v);
118         } else if (k == "frames_per_second") {
119                 frames_per_second = atof (v.c_str ());
120         } else if (k == "format") {
121                 format = Format::from_metadata (v);
122         } else if (k == "left_crop") {
123                 crop.left = atoi (v.c_str ());
124         } else if (k == "right_crop") {
125                 crop.right = atoi (v.c_str ());
126         } else if (k == "top_crop") {
127                 crop.top = atoi (v.c_str ());
128         } else if (k == "bottom_crop") {
129                 crop.bottom = atoi (v.c_str ());
130         } else if (k == "filter") {
131                 filters.push_back (Filter::from_id (v));
132         } else if (k == "scaler") {
133                 scaler = Scaler::from_id (v);
134         } else if (k == "dcp_frames") {
135                 dcp_frames = atoi (v.c_str ());
136         } else if (k == "dcp_trim_action") {
137                 if (v == "cut") {
138                         dcp_trim_action = CUT;
139                 } else if (v == "black_out") {
140                         dcp_trim_action = BLACK_OUT;
141                 }
142         } else if (k == "dcp_ab") {
143                 dcp_ab = (v == "1");
144         } else if (k == "audio_gain") {
145                 audio_gain = atof (v.c_str ());
146         } else if (k == "audio_delay") {
147                 audio_delay = atoi (v.c_str ());
148         } else if (k == "still_duration") {
149                 still_duration = atoi (v.c_str ());
150         } else if (k == "with_subtitles") {
151                 with_subtitles = (v == "1");
152         } else if (k == "subtitle_offset") {
153                 subtitle_offset = atoi (v.c_str ());
154         } else if (k == "subtitle_scale") {
155                 subtitle_scale = atof (v.c_str ());
156         }
157         
158         /* Cached stuff */
159         if (k == "thumb") {
160                 int const n = atoi (v.c_str ());
161                 /* Only add it to the list if it still exists */
162                 if (filesystem::exists (thumb_file_for_frame (n))) {
163                         thumbs.push_back (n);
164                 }
165         } else if (k == "width") {
166                 size.width = atoi (v.c_str ());
167         } else if (k == "height") {
168                 size.height = atoi (v.c_str ());
169         } else if (k == "length") {
170                 length = atof (v.c_str ());
171         } else if (k == "audio_channels") {
172                 audio_channels = atoi (v.c_str ());
173         } else if (k == "audio_sample_rate") {
174                 audio_sample_rate = atoi (v.c_str ());
175         } else if (k == "audio_sample_format") {
176                 audio_sample_format = audio_sample_format_from_string (v);
177         } else if (k == "content_digest") {
178                 content_digest = v;
179         } else if (k == "has_subtitles") {
180                 has_subtitles = (v == "1");
181         }
182 }
183
184
185 /** @param n A thumb index.
186  *  @return The path to the thumb's image file.
187  */
188 string
189 FilmState::thumb_file (int n) const
190 {
191         return thumb_file_for_frame (thumb_frame (n));
192 }
193
194 /** @param n A frame index within the Film.
195  *  @return The path to the thumb's image file for this frame;
196  *  we assume that it exists.
197  */
198 string
199 FilmState::thumb_file_for_frame (int n) const
200 {
201         return thumb_base_for_frame(n) + ".png";
202 }
203
204 string
205 FilmState::thumb_base (int n) const
206 {
207         return thumb_base_for_frame (thumb_frame (n));
208 }
209
210 string
211 FilmState::thumb_base_for_frame (int n) const
212 {
213         stringstream s;
214         s.width (8);
215         s << setfill('0') << n;
216         
217         filesystem::path p;
218         p /= dir ("thumbs");
219         p /= s.str ();
220                 
221         return p.string ();
222 }
223
224
225 /** @param n A thumb index.
226  *  @return The frame within the Film that it is for.
227  */
228 int
229 FilmState::thumb_frame (int n) const
230 {
231         assert (n < int (thumbs.size ()));
232         return thumbs[n];
233 }
234
235 Size
236 FilmState::cropped_size (Size s) const
237 {
238         s.width -= crop.left + crop.right;
239         s.height -= crop.top + crop.bottom;
240         return s;
241 }
242
243 /** Given a directory name, return its full path within the Film's directory.
244  *  The directory (and its parents) will be created if they do not exist.
245  */
246 string
247 FilmState::dir (string d) const
248 {
249         filesystem::path p;
250         p /= directory;
251         p /= d;
252         filesystem::create_directories (p);
253         return p.string ();
254 }
255
256 /** Given a file or directory name, return its full path within the Film's directory */
257 string
258 FilmState::file (string f) const
259 {
260         filesystem::path p;
261         p /= directory;
262         p /= f;
263         return p.string ();
264 }
265
266 string
267 FilmState::content_path () const
268 {
269         if (filesystem::path(content).has_root_directory ()) {
270                 return content;
271         }
272
273         return file (content);
274 }
275
276 ContentType
277 FilmState::content_type () const
278 {
279 #if BOOST_FILESYSTEM_VERSION == 3
280         string ext = filesystem::path(content).extension().string();
281 #else
282         string ext = filesystem::path(content).extension();
283 #endif
284
285         transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
286         
287         if (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png") {
288                 return STILL;
289         }
290
291         return VIDEO;
292 }
293
294 /** @return Number of bytes per sample of a single channel */
295 int
296 FilmState::bytes_per_sample () const
297 {
298         switch (audio_sample_format) {
299         case AV_SAMPLE_FMT_S16:
300                 return 2;
301         default:
302                 return 0;
303         }
304
305         return 0;
306 }
307
308 int
309 FilmState::target_sample_rate () const
310 {
311         /* Resample to a DCI-approved sample rate */
312         double t = dcp_audio_sample_rate (audio_sample_rate);
313
314         /* Compensate for the fact that video will be rounded to the
315            nearest integer number of frames per second.
316         */
317         if (rint (frames_per_second) != frames_per_second) {
318                 t *= frames_per_second / rint (frames_per_second);
319         }
320
321         return rint (t);
322 }
323
324 int
325 FilmState::dcp_length () const
326 {
327         if (dcp_frames) {
328                 return dcp_frames;
329         }
330
331         return length;
332 }
333
334 string
335 FilmState::dci_name () const
336 {
337         stringstream d;
338         d << dci_name_prefix << "_";
339
340         if (dcp_content_type) {
341                 d << dcp_content_type->dci_name() << "_";
342         }
343
344         if (format) {
345                 d << format->dci_name() << "_";
346         }
347
348         if (!audio_language.empty ()) {
349                 d << audio_language;
350                 if (!subtitle_language.empty ()) {
351                         d << "-" << subtitle_language;
352                 }
353                 d << "_";
354         }
355
356         if (!territory.empty ()) {
357                 d << territory;
358                 if (!rating.empty ()) {
359                         d << "-" << rating;
360                 }
361                 d << "_";
362         }
363
364         switch (audio_channels) {
365         case 1:
366                 d << "1_";
367                 break;
368         case 2:
369                 d << "2_";
370                 break;
371         case 6:
372                 d << "51_";
373                 break;
374         }
375
376         d << "2K_";
377
378         if (!studio.empty ()) {
379                 d << studio << "_";
380         }
381
382         gregorian::date today = gregorian::day_clock::local_day ();
383         d << gregorian::to_iso_extended_string (today) << "_";
384
385         if (!facility.empty ()) {
386                 d << facility << "_";
387         }
388
389         if (!package_type.empty ()) {
390                 d << package_type;
391         }
392
393         return d.str ();
394 }