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