Merge master.
[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         if (!_sndfile.empty ()) {
186                 return true;
187         }
188
189         for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
190                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
191                 if (fc && fc->audio_stream ()) {
192                         return true;
193                 }
194         }
195         
196         return false;
197 }
198
199 void
200 Playlist::content_changed (weak_ptr<Content> c, int p)
201 {
202         ContentChanged (c, p);
203 }
204
205 shared_ptr<const FFmpegContent>
206 Playlist::first_ffmpeg () const
207 {
208         for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
209                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
210                 if (fc) {
211                         return fc;
212                 }
213         }
214
215         return shared_ptr<const FFmpegContent> ();
216 }
217         
218
219 AudioMapping
220 Playlist::default_audio_mapping () const
221 {
222         AudioMapping m;
223
224         switch (_audio_from) {
225         case AUDIO_FFMPEG:
226         {
227                 shared_ptr<const FFmpegContent> fc = first_ffmpeg ();
228                 if (!fc) {
229                         break;
230                 }
231                 
232                 /* XXX: assumes all the same */
233                 if (fc->audio_channels() == 1) {
234                         /* Map mono sources to centre */
235                         m.add (AudioMapping::Channel (fc, 0), libdcp::CENTRE);
236                 } else {
237                         int const N = min (fc->audio_channels (), MAX_AUDIO_CHANNELS);
238                         /* Otherwise just start with a 1:1 mapping */
239                         for (int i = 0; i < N; ++i) {
240                                 m.add (AudioMapping::Channel (fc, i), (libdcp::Channel) i);
241                         }
242                 }
243                 break;
244         }
245
246         case AUDIO_SNDFILE:
247         {
248                 int n = 0;
249                 for (list<shared_ptr<const SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
250                         for (int j = 0; j < (*i)->audio_channels(); ++j) {
251                                 m.add (AudioMapping::Channel (*i, j), (libdcp::Channel) n);
252                                 ++n;
253                                 if (n >= MAX_AUDIO_CHANNELS) {
254                                         break;
255                                 }
256                         }
257                         if (n >= MAX_AUDIO_CHANNELS) {
258                                 break;
259                         }
260                 }
261                 break;
262         }
263         }
264
265         return m;
266 }
267
268 string
269 Playlist::audio_digest () const
270 {
271         string t;
272         
273         switch (_audio_from) {
274         case AUDIO_FFMPEG:
275                 for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
276                         shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
277                         if (fc) {
278                                 t += (*i)->digest ();
279                                 t += lexical_cast<string> (fc->audio_stream()->id);
280                         }
281                 }
282                 break;
283         case AUDIO_SNDFILE:
284                 for (list<shared_ptr<const SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
285                         t += (*i)->digest ();
286                 }
287                 break;
288         }
289
290         return md5_digest (t.c_str(), t.length());
291 }
292
293 string
294 Playlist::video_digest () const
295 {
296         string t;
297         
298         for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
299                 t += (*i)->digest ();
300                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
301                 if (fc && fc->subtitle_stream()) {
302                         t += fc->subtitle_stream()->id;
303                 }
304         }
305
306         return md5_digest (t.c_str(), t.length());
307 }