DVD-o-matic -> DCP-o-matic.
[dcpomatic.git] / src / lib / playlist.cc
1 /*
2     Copyright (C) 2013 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 #include <boost/shared_ptr.hpp>
21 #include <boost/lexical_cast.hpp>
22 #include "playlist.h"
23 #include "sndfile_content.h"
24 #include "sndfile_decoder.h"
25 #include "video_content.h"
26 #include "ffmpeg_decoder.h"
27 #include "ffmpeg_content.h"
28 #include "imagemagick_decoder.h"
29 #include "job.h"
30
31 using std::list;
32 using std::cout;
33 using std::vector;
34 using std::min;
35 using std::max;
36 using std::string;
37 using boost::shared_ptr;
38 using boost::weak_ptr;
39 using boost::dynamic_pointer_cast;
40 using boost::lexical_cast;
41
42 Playlist::Playlist ()
43         : _audio_from (AUDIO_FFMPEG)
44 {
45
46 }
47
48 void
49 Playlist::setup (ContentList content)
50 {
51         _audio_from = AUDIO_FFMPEG;
52
53         _video.clear ();
54         _sndfile.clear ();
55
56         for (list<boost::signals2::connection>::iterator i = _content_connections.begin(); i != _content_connections.end(); ++i) {
57                 i->disconnect ();
58         }
59         
60         _content_connections.clear ();
61
62         for (ContentList::const_iterator i = content.begin(); i != content.end(); ++i) {
63                 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*i);
64                 if (vc) {
65                         _video.push_back (vc);
66                 }
67                 
68                 shared_ptr<SndfileContent> sc = dynamic_pointer_cast<SndfileContent> (*i);
69                 if (sc) {
70                         _sndfile.push_back (sc);
71                         _audio_from = AUDIO_SNDFILE;
72                 }
73
74                 _content_connections.push_back ((*i)->Changed.connect (bind (&Playlist::content_changed, this, _1, _2)));
75         }
76
77         Changed ();
78 }
79
80 ContentAudioFrame
81 Playlist::audio_length () const
82 {
83         ContentAudioFrame len = 0;
84         
85         switch (_audio_from) {
86         case AUDIO_FFMPEG:
87                 for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
88                         shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
89                         if (fc) {
90                                 len += fc->audio_length ();
91                         }
92                 }
93                 break;
94         case AUDIO_SNDFILE:
95                 for (list<shared_ptr<const SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
96                         len += (*i)->audio_length ();
97                 }
98                 break;
99         }
100
101         return len;
102 }
103
104 int
105 Playlist::audio_channels () const
106 {
107         int channels = 0;
108         
109         switch (_audio_from) {
110         case AUDIO_FFMPEG:
111                 for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
112                         shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
113                         if (fc) {
114                                 channels = max (channels, fc->audio_channels ());
115                         }
116                 }
117                 break;
118         case AUDIO_SNDFILE:
119                 for (list<shared_ptr<const SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
120                         channels += (*i)->audio_channels ();
121                 }
122                 break;
123         }
124
125         return channels;
126 }
127
128 int
129 Playlist::audio_frame_rate () const
130 {
131         /* XXX: assuming that all content has the same rate */
132         
133         switch (_audio_from) {
134         case AUDIO_FFMPEG:
135         {
136                 shared_ptr<const FFmpegContent> fc = first_ffmpeg ();
137                 if (fc) {
138                         return fc->audio_frame_rate ();
139                 }
140                 break;
141         }
142         case AUDIO_SNDFILE:
143                 return _sndfile.front()->audio_frame_rate ();
144         }
145
146         return 0;
147 }
148
149 float
150 Playlist::video_frame_rate () const
151 {
152         if (_video.empty ()) {
153                 return 0;
154         }
155         
156         /* XXX: assuming all the same */
157         return _video.front()->video_frame_rate ();
158 }
159
160 libdcp::Size
161 Playlist::video_size () const
162 {
163         if (_video.empty ()) {
164                 return libdcp::Size ();
165         }
166
167         /* XXX: assuming all the same */
168         return _video.front()->video_size ();
169 }
170
171 ContentVideoFrame
172 Playlist::video_length () const
173 {
174         ContentVideoFrame len = 0;
175         for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
176                 len += (*i)->video_length ();
177         }
178         
179         return len;
180 }
181
182 bool
183 Playlist::has_audio () const
184 {
185         /* XXX */
186         return true;
187 }
188
189 void
190 Playlist::content_changed (weak_ptr<Content> c, int p)
191 {
192         ContentChanged (c, p);
193 }
194
195 shared_ptr<const FFmpegContent>
196 Playlist::first_ffmpeg () const
197 {
198         for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
199                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
200                 if (fc) {
201                         return fc;
202                 }
203         }
204
205         return shared_ptr<const FFmpegContent> ();
206 }
207         
208
209 AudioMapping
210 Playlist::default_audio_mapping () const
211 {
212         AudioMapping m;
213
214         switch (_audio_from) {
215         case AUDIO_FFMPEG:
216         {
217                 shared_ptr<const FFmpegContent> fc = first_ffmpeg ();
218                 if (!fc) {
219                         break;
220                 }
221                 
222                 /* XXX: assumes all the same */
223                 if (fc->audio_channels() == 1) {
224                         /* Map mono sources to centre */
225                         m.add (AudioMapping::Channel (fc, 0), libdcp::CENTRE);
226                 } else {
227                         int const N = min (fc->audio_channels (), MAX_AUDIO_CHANNELS);
228                         /* Otherwise just start with a 1:1 mapping */
229                         for (int i = 0; i < N; ++i) {
230                                 m.add (AudioMapping::Channel (fc, i), (libdcp::Channel) i);
231                         }
232                 }
233                 break;
234         }
235
236         case AUDIO_SNDFILE:
237         {
238                 int n = 0;
239                 for (list<shared_ptr<const SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
240                         for (int j = 0; j < (*i)->audio_channels(); ++j) {
241                                 m.add (AudioMapping::Channel (*i, j), (libdcp::Channel) n);
242                                 ++n;
243                                 if (n >= MAX_AUDIO_CHANNELS) {
244                                         break;
245                                 }
246                         }
247                         if (n >= MAX_AUDIO_CHANNELS) {
248                                 break;
249                         }
250                 }
251                 break;
252         }
253         }
254
255         return m;
256 }
257
258 string
259 Playlist::audio_digest () const
260 {
261         string t;
262         
263         switch (_audio_from) {
264         case AUDIO_FFMPEG:
265                 for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
266                         shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
267                         if (fc) {
268                                 t += (*i)->digest ();
269                                 t += lexical_cast<string> (fc->audio_stream()->id);
270                         }
271                 }
272                 break;
273         case AUDIO_SNDFILE:
274                 for (list<shared_ptr<const SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
275                         t += (*i)->digest ();
276                 }
277                 break;
278         }
279
280         return md5_digest (t.c_str(), t.length());
281 }
282
283 string
284 Playlist::video_digest () const
285 {
286         string t;
287         
288         for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
289                 t += (*i)->digest ();
290                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
291                 if (fc) {
292                         t += fc->subtitle_stream()->id;
293                 }
294         }
295
296         return md5_digest (t.c_str(), t.length());
297 }