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