Basic sub offset support.
[dcpomatic.git] / src / lib / 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 "film_state.h"
33 #include "scaler.h"
34 #include "filter.h"
35 #include "format.h"
36 #include "dcp_content_type.h"
37 #include "util.h"
38 #include "exceptions.h"
39
40 using namespace std;
41 using namespace boost;
42
43 /** Write state to a stream.
44  *  @param f Stream to write to.
45  */
46 void
47 FilmState::write_metadata (ofstream& f) const
48 {
49         /* User stuff */
50         f << "name " << name << "\n";
51         f << "content " << content << "\n";
52         if (dcp_content_type) {
53                 f << "dcp_content_type " << dcp_content_type->pretty_name () << "\n";
54         }
55         f << "frames_per_second " << frames_per_second << "\n";
56         if (format) {
57                 f << "format " << format->as_metadata () << "\n";
58         }
59         f << "left_crop " << crop.left << "\n";
60         f << "right_crop " << crop.right << "\n";
61         f << "top_crop " << crop.top << "\n";
62         f << "bottom_crop " << crop.bottom << "\n";
63         for (vector<Filter const *>::const_iterator i = filters.begin(); i != filters.end(); ++i) {
64                 f << "filter " << (*i)->id () << "\n";
65         }
66         f << "scaler " << scaler->id () << "\n";
67         f << "dcp_frames " << dcp_frames << "\n";
68
69         f << "dcp_trim_action ";
70         switch (dcp_trim_action) {
71         case CUT:
72                 f << "cut\n";
73                 break;
74         case BLACK_OUT:
75                 f << "black_out\n";
76                 break;
77         }
78         
79         f << "dcp_ab " << (dcp_ab ? "1" : "0") << "\n";
80         f << "audio_gain " << audio_gain << "\n";
81         f << "audio_delay " << audio_delay << "\n";
82         f << "still_duration " << still_duration << "\n";
83         f << "with_subtitles " << with_subtitles << "\n";
84         f << "subtitle_offset " << subtitle_offset << "\n";
85         f << "subtitle_scale " << subtitle_scale << "\n";
86
87         /* Cached stuff; this is information about our content; we could
88            look it up each time, but that's slow.
89         */
90         for (vector<int>::const_iterator i = thumbs.begin(); i != thumbs.end(); ++i) {
91                 f << "thumb " << *i << "\n";
92         }
93         f << "width " << size.width << "\n";
94         f << "height " << size.height << "\n";
95         f << "length " << length << "\n";
96         f << "audio_channels " << audio_channels << "\n";
97         f << "audio_sample_rate " << audio_sample_rate << "\n";
98         f << "audio_sample_format " << audio_sample_format_to_string (audio_sample_format) << "\n";
99         f << "content_digest " << content_digest << "\n";
100         f << "has_subtitles " << has_subtitles << "\n";
101 }
102
103 /** Read state from a key / value pair.
104  *  @param k Key.
105  *  @param v Value.
106  */
107 void
108 FilmState::read_metadata (string k, string v)
109 {
110         /* User-specified stuff */
111         if (k == "name") {
112                 name = v;
113         } else if (k == "content") {
114                 content = v;
115         } else if (k == "dcp_content_type") {
116                 dcp_content_type = DCPContentType::from_pretty_name (v);
117         } else if (k == "frames_per_second") {
118                 frames_per_second = atof (v.c_str ());
119         } else if (k == "format") {
120                 format = Format::from_metadata (v);
121         } else if (k == "left_crop") {
122                 crop.left = atoi (v.c_str ());
123         } else if (k == "right_crop") {
124                 crop.right = atoi (v.c_str ());
125         } else if (k == "top_crop") {
126                 crop.top = atoi (v.c_str ());
127         } else if (k == "bottom_crop") {
128                 crop.bottom = atoi (v.c_str ());
129         } else if (k == "filter") {
130                 filters.push_back (Filter::from_id (v));
131         } else if (k == "scaler") {
132                 scaler = Scaler::from_id (v);
133         } else if (k == "dcp_frames") {
134                 dcp_frames = atoi (v.c_str ());
135         } else if (k == "dcp_trim_action") {
136                 if (v == "cut") {
137                         dcp_trim_action = CUT;
138                 } else if (v == "black_out") {
139                         dcp_trim_action = BLACK_OUT;
140                 }
141         } else if (k == "dcp_ab") {
142                 dcp_ab = (v == "1");
143         } else if (k == "audio_gain") {
144                 audio_gain = atof (v.c_str ());
145         } else if (k == "audio_delay") {
146                 audio_delay = atoi (v.c_str ());
147         } else if (k == "still_duration") {
148                 still_duration = atoi (v.c_str ());
149         } else if (k == "with_subtitles") {
150                 with_subtitles = (v == "1");
151         } else if (k == "subtitle_offset") {
152                 subtitle_offset = atoi (v.c_str ());
153         } else if (k == "subtitle_scale") {
154                 subtitle_scale = atof (v.c_str ());
155         }
156         
157         /* Cached stuff */
158         if (k == "thumb") {
159                 int const n = atoi (v.c_str ());
160                 /* Only add it to the list if it still exists */
161                 if (filesystem::exists (thumb_file_for_frame (n))) {
162                         thumbs.push_back (n);
163                 }
164         } else if (k == "width") {
165                 size.width = atoi (v.c_str ());
166         } else if (k == "height") {
167                 size.height = atoi (v.c_str ());
168         } else if (k == "length") {
169                 length = atof (v.c_str ());
170         } else if (k == "audio_channels") {
171                 audio_channels = atoi (v.c_str ());
172         } else if (k == "audio_sample_rate") {
173                 audio_sample_rate = atoi (v.c_str ());
174         } else if (k == "audio_sample_format") {
175                 audio_sample_format = audio_sample_format_from_string (v);
176         } else if (k == "content_digest") {
177                 content_digest = v;
178         } else if (k == "has_subtitles") {
179                 has_subtitles = (v == "1");
180         }
181 }
182
183
184 /** @param n A thumb index.
185  *  @return The path to the thumb's image file.
186  */
187 string
188 FilmState::thumb_file (int n) const
189 {
190         return thumb_file_for_frame (thumb_frame (n));
191 }
192
193 /** @param n A frame index within the Film.
194  *  @return The path to the thumb's image file for this frame;
195  *  we assume that it exists.
196  */
197 string
198 FilmState::thumb_file_for_frame (int n) const
199 {
200         return thumb_base_for_frame(n) + ".png";
201 }
202
203 string
204 FilmState::thumb_base (int n) const
205 {
206         return thumb_base_for_frame (thumb_frame (n));
207 }
208
209 string
210 FilmState::thumb_base_for_frame (int n) const
211 {
212         stringstream s;
213         s.width (8);
214         s << setfill('0') << n;
215         
216         filesystem::path p;
217         p /= dir ("thumbs");
218         p /= s.str ();
219                 
220         return p.string ();
221 }
222
223
224 /** @param n A thumb index.
225  *  @return The frame within the Film that it is for.
226  */
227 int
228 FilmState::thumb_frame (int n) const
229 {
230         assert (n < int (thumbs.size ()));
231         return thumbs[n];
232 }
233
234 Size
235 FilmState::cropped_size (Size s) const
236 {
237         s.width -= crop.left + crop.right;
238         s.height -= crop.top + crop.bottom;
239         return s;
240 }
241
242 /** Given a directory name, return its full path within the Film's directory.
243  *  The directory (and its parents) will be created if they do not exist.
244  */
245 string
246 FilmState::dir (string d) const
247 {
248         filesystem::path p;
249         p /= directory;
250         p /= d;
251         filesystem::create_directories (p);
252         return p.string ();
253 }
254
255 /** Given a file or directory name, return its full path within the Film's directory */
256 string
257 FilmState::file (string f) const
258 {
259         filesystem::path p;
260         p /= directory;
261         p /= f;
262         return p.string ();
263 }
264
265 string
266 FilmState::content_path () const
267 {
268         if (filesystem::path(content).has_root_directory ()) {
269                 return content;
270         }
271
272         return file (content);
273 }
274
275 ContentType
276 FilmState::content_type () const
277 {
278 #if BOOST_FILESYSTEM_VERSION == 3
279         string ext = filesystem::path(content).extension().string();
280 #else
281         string ext = filesystem::path(content).extension();
282 #endif
283
284         transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
285         
286         if (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png") {
287                 return STILL;
288         }
289
290         return VIDEO;
291 }
292
293 /** @return Number of bytes per sample of a single channel */
294 int
295 FilmState::bytes_per_sample () const
296 {
297         switch (audio_sample_format) {
298         case AV_SAMPLE_FMT_S16:
299                 return 2;
300         default:
301                 return 0;
302         }
303
304         return 0;
305 }
306
307 int
308 FilmState::target_sample_rate () const
309 {
310         /* Resample to a DCI-approved sample rate */
311         double t = dcp_audio_sample_rate (audio_sample_rate);
312
313         /* Compensate for the fact that video will be rounded to the
314            nearest integer number of frames per second.
315         */
316         if (rint (frames_per_second) != frames_per_second) {
317                 t *= frames_per_second / rint (frames_per_second);
318         }
319
320         return rint (t);
321 }
322
323 int
324 FilmState::dcp_length () const
325 {
326         if (dcp_frames) {
327                 return dcp_frames;
328         }
329
330         return length;
331 }
332
333