8dd8309a8f0358a2d35c556623f6e569fe487d55
[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 <boost/date_time.hpp>
33 #include <boost/algorithm/string.hpp>
34 #include "film_state.h"
35 #include "scaler.h"
36 #include "filter.h"
37 #include "format.h"
38 #include "dcp_content_type.h"
39 #include "util.h"
40 #include "exceptions.h"
41 #include "options.h"
42 #include "decoder.h"
43 #include "decoder_factory.h"
44
45 using namespace std;
46 using namespace boost;
47
48 /** Write state to our `metadata' file */
49 void
50 FilmState::write_metadata () const
51 {
52         filesystem::create_directories (directory());
53
54         string const m = file ("metadata");
55         ofstream f (m.c_str ());
56         if (!f.good ()) {
57                 throw CreateFileError (m);
58         }
59
60         /* User stuff */
61         f << "name " << _name << "\n";
62         f << "use_dci_name " << _use_dci_name << "\n";
63         f << "content " << _content << "\n";
64         if (_dcp_content_type) {
65                 f << "dcp_content_type " << _dcp_content_type->pretty_name () << "\n";
66         }
67         if (_format) {
68                 f << "format " << _format->as_metadata () << "\n";
69         }
70         f << "left_crop " << _crop.left << "\n";
71         f << "right_crop " << _crop.right << "\n";
72         f << "top_crop " << _crop.top << "\n";
73         f << "bottom_crop " << _crop.bottom << "\n";
74         for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
75                 f << "filter " << (*i)->id () << "\n";
76         }
77         f << "scaler " << _scaler->id () << "\n";
78         f << "dcp_frames " << _dcp_frames << "\n";
79
80         f << "dcp_trim_action ";
81         switch (_dcp_trim_action) {
82         case CUT:
83                 f << "cut\n";
84                 break;
85         case BLACK_OUT:
86                 f << "black_out\n";
87                 break;
88         }
89         
90         f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n";
91         f << "selected_audio_stream " << _audio_stream << "\n";
92         f << "audio_gain " << _audio_gain << "\n";
93         f << "audio_delay " << _audio_delay << "\n";
94         f << "still_duration " << _still_duration << "\n";
95         f << "selected_subtitle_stream " << _subtitle_stream << "\n";
96         f << "with_subtitles " << _with_subtitles << "\n";
97         f << "subtitle_offset " << _subtitle_offset << "\n";
98         f << "subtitle_scale " << _subtitle_scale << "\n";
99         f << "audio_language " << _audio_language << "\n";
100         f << "subtitle_language " << _subtitle_language << "\n";
101         f << "territory " << _territory << "\n";
102         f << "rating " << _rating << "\n";
103         f << "studio " << _studio << "\n";
104         f << "facility " << _facility << "\n";
105         f << "package_type " << _package_type << "\n";
106
107         /* Cached stuff; this is information about our content; we could
108            look it up each time, but that's slow.
109         */
110         for (vector<int>::const_iterator i = _thumbs.begin(); i != _thumbs.end(); ++i) {
111                 f << "thumb " << *i << "\n";
112         }
113         f << "width " << _size.width << "\n";
114         f << "height " << _size.height << "\n";
115         f << "length " << _length << "\n";
116         f << "audio_sample_rate " << _audio_sample_rate << "\n";
117         f << "content_digest " << _content_digest << "\n";
118         f << "has_subtitles " << _has_subtitles << "\n";
119
120         for (vector<AudioStream>::const_iterator i = _audio_streams.begin(); i != _audio_streams.end(); ++i) {
121                 f << "audio_stream " << i->to_string () << "\n";
122         }
123
124         for (vector<SubtitleStream>::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
125                 f << "subtitle_stream " << i->to_string () << "\n";
126         }
127
128         f << "frames_per_second " << _frames_per_second << "\n";
129         
130         _dirty = false;
131 }
132
133 /** Read state from our metadata file */
134 void
135 FilmState::read_metadata ()
136 {
137         ifstream f (file("metadata").c_str());
138         multimap<string, string> kv = read_key_value (f);
139         for (multimap<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
140                 string const k = i->first;
141                 string const v = i->second;
142
143                 /* User-specified stuff */
144                 if (k == "name") {
145                         _name = v;
146                 } else if (k == "use_dci_name") {
147                         _use_dci_name = (v == "1");
148                 } else if (k == "content") {
149                         _content = v;
150                 } else if (k == "dcp_content_type") {
151                         _dcp_content_type = DCPContentType::from_pretty_name (v);
152                 } else if (k == "format") {
153                         _format = Format::from_metadata (v);
154                 } else if (k == "left_crop") {
155                         _crop.left = atoi (v.c_str ());
156                 } else if (k == "right_crop") {
157                         _crop.right = atoi (v.c_str ());
158                 } else if (k == "top_crop") {
159                         _crop.top = atoi (v.c_str ());
160                 } else if (k == "bottom_crop") {
161                         _crop.bottom = atoi (v.c_str ());
162                 } else if (k == "filter") {
163                         _filters.push_back (Filter::from_id (v));
164                 } else if (k == "scaler") {
165                         _scaler = Scaler::from_id (v);
166                 } else if (k == "dcp_frames") {
167                         _dcp_frames = atoi (v.c_str ());
168                 } else if (k == "dcp_trim_action") {
169                         if (v == "cut") {
170                                 _dcp_trim_action = CUT;
171                         } else if (v == "black_out") {
172                                 _dcp_trim_action = BLACK_OUT;
173                         }
174                 } else if (k == "dcp_ab") {
175                         _dcp_ab = (v == "1");
176                 } else if (k == "selected_audio_stream") {
177                         _audio_stream = atoi (v.c_str ());
178                 } else if (k == "audio_gain") {
179                         _audio_gain = atof (v.c_str ());
180                 } else if (k == "audio_delay") {
181                         _audio_delay = atoi (v.c_str ());
182                 } else if (k == "still_duration") {
183                         _still_duration = atoi (v.c_str ());
184                 } else if (k == "selected_subtitle_stream") {
185                         _subtitle_stream = atoi (v.c_str ());
186                 } else if (k == "with_subtitles") {
187                         _with_subtitles = (v == "1");
188                 } else if (k == "subtitle_offset") {
189                         _subtitle_offset = atoi (v.c_str ());
190                 } else if (k == "subtitle_scale") {
191                         _subtitle_scale = atof (v.c_str ());
192                 } else if (k == "audio_language") {
193                         _audio_language = v;
194                 } else if (k == "subtitle_language") {
195                         _subtitle_language = v;
196                 } else if (k == "territory") {
197                         _territory = v;
198                 } else if (k == "rating") {
199                         _rating = v;
200                 } else if (k == "studio") {
201                         _studio = v;
202                 } else if (k == "facility") {
203                         _facility = v;
204                 } else if (k == "package_type") {
205                         _package_type = v;
206                 }
207                 
208                 /* Cached stuff */
209                 if (k == "thumb") {
210                         int const n = atoi (v.c_str ());
211                         /* Only add it to the list if it still exists */
212                         if (filesystem::exists (thumb_file_for_frame (n))) {
213                                 _thumbs.push_back (n);
214                         }
215                 } else if (k == "width") {
216                         _size.width = atoi (v.c_str ());
217                 } else if (k == "height") {
218                         _size.height = atoi (v.c_str ());
219                 } else if (k == "length") {
220                         _length = atof (v.c_str ());
221                 } else if (k == "audio_sample_rate") {
222                         _audio_sample_rate = atoi (v.c_str ());
223                 } else if (k == "content_digest") {
224                         _content_digest = v;
225                 } else if (k == "has_subtitles") {
226                         _has_subtitles = (v == "1");
227                 } else if (k == "audio_stream") {
228                         _audio_streams.push_back (AudioStream (v));
229                 } else if (k == "subtitle_stream") {
230                         _subtitle_streams.push_back (SubtitleStream (v));
231                 } else if (k == "frames_per_second") {
232                         _frames_per_second = atof (v.c_str ());
233                 }
234         }
235                 
236         _dirty = false;
237 }
238
239 /** @param n A thumb index.
240  *  @return The path to the thumb's image file.
241  */
242 string
243 FilmState::thumb_file (int n) const
244 {
245         return thumb_file_for_frame (thumb_frame (n));
246 }
247
248 /** @param n A frame index within the Film.
249  *  @return The path to the thumb's image file for this frame;
250  *  we assume that it exists.
251  */
252 string
253 FilmState::thumb_file_for_frame (int n) const
254 {
255         return thumb_base_for_frame(n) + ".png";
256 }
257
258 string
259 FilmState::thumb_base (int n) const
260 {
261         return thumb_base_for_frame (thumb_frame (n));
262 }
263
264 string
265 FilmState::thumb_base_for_frame (int n) const
266 {
267         stringstream s;
268         s.width (8);
269         s << setfill('0') << n;
270         
271         filesystem::path p;
272         p /= dir ("thumbs");
273         p /= s.str ();
274                 
275         return p.string ();
276 }
277
278
279 /** @param n A thumb index.
280  *  @return The frame within the Film that it is for.
281  */
282 int
283 FilmState::thumb_frame (int n) const
284 {
285         assert (n < int (_thumbs.size ()));
286         return _thumbs[n];
287 }
288
289 Size
290 FilmState::cropped_size (Size s) const
291 {
292         s.width -= _crop.left + _crop.right;
293         s.height -= _crop.top + _crop.bottom;
294         return s;
295 }
296
297 /** Given a directory name, return its full path within the Film's directory.
298  *  The directory (and its parents) will be created if they do not exist.
299  */
300 string
301 FilmState::dir (string d) const
302 {
303         filesystem::path p;
304         p /= _directory;
305         p /= d;
306         filesystem::create_directories (p);
307         return p.string ();
308 }
309
310 /** Given a file or directory name, return its full path within the Film's directory */
311 string
312 FilmState::file (string f) const
313 {
314         filesystem::path p;
315         p /= _directory;
316         p /= f;
317         return p.string ();
318 }
319
320 /** @return full path of the content (actual video) file
321  *  of the Film.
322  */
323 string
324 FilmState::content_path () const
325 {
326         if (filesystem::path(_content).has_root_directory ()) {
327                 return _content;
328         }
329
330         return file (_content);
331 }
332
333 ContentType
334 FilmState::content_type () const
335 {
336 #if BOOST_FILESYSTEM_VERSION == 3
337         string ext = filesystem::path(_content).extension().string();
338 #else
339         string ext = filesystem::path(_content).extension();
340 #endif
341
342         transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
343         
344         if (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png") {
345                 return STILL;
346         }
347
348         return VIDEO;
349 }
350
351 int
352 FilmState::target_sample_rate () const
353 {
354         /* Resample to a DCI-approved sample rate */
355         double t = dcp_audio_sample_rate (_audio_sample_rate);
356
357         /* Compensate for the fact that video will be rounded to the
358            nearest integer number of frames per second.
359         */
360         if (rint (_frames_per_second) != _frames_per_second) {
361                 t *= _frames_per_second / rint (_frames_per_second);
362         }
363
364         return rint (t);
365 }
366
367 int
368 FilmState::dcp_length () const
369 {
370         if (_dcp_frames) {
371                 return _dcp_frames;
372         }
373
374         return _length;
375 }
376
377 /** @return a DCI-compliant name for a DCP of this film */
378 string
379 FilmState::dci_name () const
380 {
381         stringstream d;
382
383         string fixed_name = to_upper_copy (_name);
384         for (size_t i = 0; i < fixed_name.length(); ++i) {
385                 if (fixed_name[i] == ' ') {
386                         fixed_name[i] = '-';
387                 }
388         }
389
390         /* Spec is that the name part should be maximum 14 characters, as I understand it */
391         if (fixed_name.length() > 14) {
392                 fixed_name = fixed_name.substr (0, 14);
393         }
394
395         d << fixed_name << "_";
396
397         if (_dcp_content_type) {
398                 d << _dcp_content_type->dci_name() << "_";
399         }
400
401         if (_format) {
402                 d << _format->dci_name() << "_";
403         }
404
405         if (!_audio_language.empty ()) {
406                 d << _audio_language;
407                 if (!_subtitle_language.empty() && _with_subtitles) {
408                         d << "-" << _subtitle_language;
409                 } else {
410                         d << "-XX";
411                 }
412                         
413                 d << "_";
414         }
415
416         if (!_territory.empty ()) {
417                 d << _territory;
418                 if (!_rating.empty ()) {
419                         d << "-" << _rating;
420                 }
421                 d << "_";
422         }
423
424         switch (_audio_streams[_audio_stream].channels()) {
425         case 1:
426                 d << "10_";
427                 break;
428         case 2:
429                 d << "20_";
430                 break;
431         case 6:
432                 d << "51_";
433                 break;
434         }
435
436         d << "2K_";
437
438         if (!_studio.empty ()) {
439                 d << _studio << "_";
440         }
441
442         gregorian::date today = gregorian::day_clock::local_day ();
443         d << gregorian::to_iso_string (today) << "_";
444
445         if (!_facility.empty ()) {
446                 d << _facility << "_";
447         }
448
449         if (!_package_type.empty ()) {
450                 d << _package_type;
451         }
452
453         return d.str ();
454 }
455
456 /** @return name to give the DCP */
457 string
458 FilmState::dcp_name () const
459 {
460         if (_use_dci_name) {
461                 return dci_name ();
462         }
463
464         return _name;
465 }
466
467
468 void
469 FilmState::set_directory (string d)
470 {
471         _directory = d;
472         _dirty = true;
473 }
474
475 void
476 FilmState::set_name (string n)
477 {
478         _name = n;
479         signal_changed (NAME);
480 }
481
482 void
483 FilmState::set_use_dci_name (bool u)
484 {
485         _use_dci_name = u;
486         signal_changed (USE_DCI_NAME);
487 }
488
489 void
490 FilmState::set_content (string c)
491 {
492         string check = _directory;
493
494 #if BOOST_FILESYSTEM_VERSION == 3
495         filesystem::path slash ("/");
496         string platform_slash = slash.make_preferred().string ();
497 #else
498 #ifdef DVDOMATIC_WINDOWS
499         string platform_slash = "\\";
500 #else
501         string platform_slash = "/";
502 #endif
503 #endif  
504
505         if (!ends_with (check, platform_slash)) {
506                 check += platform_slash;
507         }
508         
509         if (filesystem::path(c).has_root_directory () && starts_with (c, check)) {
510                 c = c.substr (_directory.length() + 1);
511         }
512
513         if (c == _content) {
514                 return;
515         }
516
517         /* Create a temporary decoder so that we can get information
518            about the content.
519         */
520
521         shared_ptr<FilmState> s = state_copy ();
522         s->_content = c;
523         shared_ptr<Options> o (new Options ("", "", ""));
524         o->out_size = Size (1024, 1024);
525         
526         shared_ptr<Decoder> d = decoder_factory (s, o, 0, 0);
527         
528         set_size (d->native_size ());
529         set_length (d->length_in_frames ());
530         set_frames_per_second (d->frames_per_second ());
531         set_audio_sample_rate (d->audio_sample_rate ());
532         set_has_subtitles (d->has_subtitles ());
533         set_audio_streams (d->audio_streams ());
534         set_subtitle_streams (d->subtitle_streams ());
535         set_audio_stream (audio_streams().empty() ? -1 : 0);
536         set_subtitle_stream (subtitle_streams().empty() ? -1 : 0);
537         set_content_digest (md5_digest (content_path ()));
538         
539         _content = c;
540         signal_changed (CONTENT);
541 }
542                
543 void
544 FilmState::set_dcp_content_type (DCPContentType const * t)
545 {
546         _dcp_content_type = t;
547         signal_changed (DCP_CONTENT_TYPE);
548 }
549
550 void
551 FilmState::set_format (Format const * f)
552 {
553         _format = f;
554         signal_changed (FORMAT);
555 }
556
557 void
558 FilmState::set_crop (Crop c)
559 {
560         _crop = c;
561         signal_changed (CROP);
562 }
563
564 void
565 FilmState::set_left_crop (int c)
566 {
567         if (_crop.left == c) {
568                 return;
569         }
570
571         _crop.left = c;
572         signal_changed (CROP);
573 }
574
575 void
576 FilmState::set_right_crop (int c)
577 {
578         if (_crop.right == c) {
579                 return;
580         }
581
582         _crop.right = c;
583         signal_changed (CROP);
584 }
585
586 void
587 FilmState::set_top_crop (int c)
588 {
589         if (_crop.top == c) {
590                 return;
591         }
592
593         _crop.top = c;
594         signal_changed (CROP);
595 }
596
597 void
598 FilmState::set_bottom_crop (int c)
599 {
600         if (_crop.bottom == c) {
601                 return;
602         }
603
604         _crop.bottom = c;
605         signal_changed (CROP);
606 }
607
608 void
609 FilmState::set_filters (vector<Filter const *> f)
610 {
611         _filters = f;
612         signal_changed (FILTERS);
613 }
614
615 void
616 FilmState::set_scaler (Scaler const * s)
617 {
618         _scaler = s;
619         signal_changed (SCALER);
620 }
621
622 void
623 FilmState::set_dcp_frames (int f)
624 {
625         _dcp_frames = f;
626         signal_changed (DCP_FRAMES);
627 }
628
629 void
630 FilmState::set_dcp_trim_action (TrimAction a)
631 {
632         _dcp_trim_action = a;
633         signal_changed (DCP_TRIM_ACTION);
634 }
635
636 void
637 FilmState::set_dcp_ab (bool a)
638 {
639         _dcp_ab = a;
640         signal_changed (DCP_AB);
641 }
642
643 void
644 FilmState::set_audio_stream (int s)
645 {
646         _audio_stream = s;
647         signal_changed (AUDIO_STREAM);
648 }
649
650 void
651 FilmState::set_audio_gain (float g)
652 {
653         _audio_gain = g;
654         signal_changed (AUDIO_GAIN);
655 }
656
657 void
658 FilmState::set_audio_delay (int d)
659 {
660         _audio_delay = d;
661         signal_changed (AUDIO_DELAY);
662 }
663
664 void
665 FilmState::set_still_duration (int d)
666 {
667         _still_duration = d;
668         signal_changed (STILL_DURATION);
669 }
670
671 void
672 FilmState::set_subtitle_stream (int s)
673 {
674         _subtitle_stream = s;
675         signal_changed (SUBTITLE_STREAM);
676 }
677
678 void
679 FilmState::set_with_subtitles (bool w)
680 {
681         _with_subtitles = w;
682         signal_changed (WITH_SUBTITLES);
683 }
684
685 void
686 FilmState::set_subtitle_offset (int o)
687 {
688         _subtitle_offset = o;
689         signal_changed (SUBTITLE_OFFSET);
690 }
691
692 void
693 FilmState::set_subtitle_scale (float s)
694 {
695         _subtitle_scale = s;
696         signal_changed (SUBTITLE_SCALE);
697 }
698
699 void
700 FilmState::set_audio_language (string l)
701 {
702         _audio_language = l;
703         signal_changed (DCI_METADATA);
704 }
705
706 void
707 FilmState::set_subtitle_language (string l)
708 {
709         _subtitle_language = l;
710         signal_changed (DCI_METADATA);
711 }
712
713 void
714 FilmState::set_territory (string t)
715 {
716         _territory = t;
717         signal_changed (DCI_METADATA);
718 }
719
720 void
721 FilmState::set_rating (string r)
722 {
723         _rating = r;
724         signal_changed (DCI_METADATA);
725 }
726
727 void
728 FilmState::set_studio (string s)
729 {
730         _studio = s;
731         signal_changed (DCI_METADATA);
732 }
733
734 void
735 FilmState::set_facility (string f)
736 {
737         _facility = f;
738         signal_changed (DCI_METADATA);
739 }
740
741 void
742 FilmState::set_package_type (string p)
743 {
744         _package_type = p;
745         signal_changed (DCI_METADATA);
746 }
747
748 void
749 FilmState::set_thumbs (vector<int> t)
750 {
751         _thumbs = t;
752         signal_changed (THUMBS);
753 }
754
755 void
756 FilmState::set_size (Size s)
757 {
758         _size = s;
759         signal_changed (SIZE);
760 }
761
762 void
763 FilmState::set_length (int l)
764 {
765         _length = l;
766         signal_changed (LENGTH);
767 }
768
769 void
770 FilmState::set_audio_sample_rate (int r)
771 {
772         _audio_sample_rate = r;
773         signal_changed (AUDIO_SAMPLE_RATE);
774 }
775
776 void
777 FilmState::set_content_digest (string d)
778 {
779         _content_digest = d;
780         _dirty = true;
781 }
782
783 void
784 FilmState::set_has_subtitles (bool s)
785 {
786         _has_subtitles = s;
787         signal_changed (HAS_SUBTITLES);
788 }
789
790 void
791 FilmState::set_audio_streams (vector<AudioStream> s)
792 {
793         _audio_streams = s;
794         _dirty = true;
795 }
796
797 void
798 FilmState::set_subtitle_streams (vector<SubtitleStream> s)
799 {
800         _subtitle_streams = s;
801         _dirty = true;
802 }
803
804 void
805 FilmState::set_frames_per_second (float f)
806 {
807         _frames_per_second = f;
808         signal_changed (FRAMES_PER_SECOND);
809 }
810         
811 void
812 FilmState::signal_changed (Property p)
813 {
814         _dirty = true;
815         Changed (p);
816 }
817
818 shared_ptr<FilmState>
819 FilmState::state_copy () const
820 {
821         return shared_ptr<FilmState> (new FilmState (*this));
822 }
823
824 int
825 FilmState::audio_channels () const
826 {
827         if (_audio_stream == -1) {
828                 return 0;
829         }
830
831         return _audio_streams[_audio_stream].channels ();
832 }
833
834
835