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