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