Fix content length reporting; fix crash when player has audio disabled.
[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         _audio.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
64                 /* Video is video */
65                 shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*i);
66                 if (vc) {
67                         _video.push_back (vc);
68                 }
69
70                 /* FFmpegContent is audio if we are doing AUDIO_FFMPEG */
71                 shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (*i);
72                 if (fc && _audio_from == AUDIO_FFMPEG) {
73                         _audio.push_back (fc);
74                 }
75
76                 /* SndfileContent trumps FFmpegContent for audio */
77                 shared_ptr<SndfileContent> sc = dynamic_pointer_cast<SndfileContent> (*i);
78                 if (sc) {
79                         if (_audio_from == AUDIO_FFMPEG) {
80                                 /* This is our fist SndfileContent; clear any FFmpegContent and
81                                    say that we are using Sndfile.
82                                 */
83                                 _audio.clear ();
84                                 _audio_from = AUDIO_SNDFILE;
85                         }
86                         
87                         _audio.push_back (sc);
88                 }
89
90                 _content_connections.push_back ((*i)->Changed.connect (bind (&Playlist::content_changed, this, _1, _2)));
91         }
92
93         Changed ();
94 }
95
96 /** @return Length of our audio */
97 ContentAudioFrame
98 Playlist::audio_length () const
99 {
100         ContentAudioFrame len = 0;
101
102         switch (_audio_from) {
103         case AUDIO_FFMPEG:
104                 /* FFmpeg content is sequential */
105                 for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
106                         len += (*i)->audio_length ();
107                 }
108                 break;
109         case AUDIO_SNDFILE:
110                 /* Sndfile content is simultaneous */
111                 for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
112                         len = max (len, (*i)->audio_length ());
113                 }
114                 break;
115         }
116
117         return len;
118 }
119
120 /** @return number of audio channels */
121 int
122 Playlist::audio_channels () const
123 {
124         int channels = 0;
125         
126         switch (_audio_from) {
127         case AUDIO_FFMPEG:
128                 /* FFmpeg audio is sequential, so use the maximum channel count */
129                 for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
130                         channels = max (channels, (*i)->audio_channels ());
131                 }
132                 break;
133         case AUDIO_SNDFILE:
134                 /* Sndfile audio is simultaneous, so it's the sum of the channel counts */
135                 for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
136                         channels += (*i)->audio_channels ();
137                 }
138                 break;
139         }
140
141         return channels;
142 }
143
144 int
145 Playlist::audio_frame_rate () const
146 {
147         if (_audio.empty ()) {
148                 return 0;
149         }
150
151         /* XXX: assuming that all content has the same rate */
152         return _audio.front()->audio_frame_rate ();
153 }
154
155 float
156 Playlist::video_frame_rate () const
157 {
158         if (_video.empty ()) {
159                 return 0;
160         }
161         
162         /* XXX: assuming all the same */
163         return _video.front()->video_frame_rate ();
164 }
165
166 libdcp::Size
167 Playlist::video_size () const
168 {
169         if (_video.empty ()) {
170                 return libdcp::Size ();
171         }
172
173         /* XXX: assuming all the same */
174         return _video.front()->video_size ();
175 }
176
177 ContentVideoFrame
178 Playlist::video_length () const
179 {
180         ContentVideoFrame len = 0;
181         for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
182                 len += (*i)->video_length ();
183         }
184         
185         return len;
186 }
187
188 bool
189 Playlist::has_audio () const
190 {
191         return !_audio.empty ();
192 }
193
194 void
195 Playlist::content_changed (weak_ptr<Content> c, int p)
196 {
197         ContentChanged (c, p);
198 }
199
200 AudioMapping
201 Playlist::default_audio_mapping () const
202 {
203         AudioMapping m;
204         if (_audio.empty ()) {
205                 return m;
206         }
207
208         switch (_audio_from) {
209         case AUDIO_FFMPEG:
210         {
211                 /* XXX: assumes all the same */
212                 if (_audio.front()->audio_channels() == 1) {
213                         /* Map mono sources to centre */
214                         m.add (AudioMapping::Channel (_audio.front(), 0), libdcp::CENTRE);
215                 } else {
216                         int const N = min (_audio.front()->audio_channels (), MAX_AUDIO_CHANNELS);
217                         /* Otherwise just start with a 1:1 mapping */
218                         for (int i = 0; i < N; ++i) {
219                                 m.add (AudioMapping::Channel (_audio.front(), i), (libdcp::Channel) i);
220                         }
221                 }
222                 break;
223         }
224
225         case AUDIO_SNDFILE:
226         {
227                 int n = 0;
228                 for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
229                         for (int j = 0; j < (*i)->audio_channels(); ++j) {
230                                 m.add (AudioMapping::Channel (*i, j), (libdcp::Channel) n);
231                                 ++n;
232                                 if (n >= MAX_AUDIO_CHANNELS) {
233                                         break;
234                                 }
235                         }
236                         if (n >= MAX_AUDIO_CHANNELS) {
237                                 break;
238                         }
239                 }
240                 break;
241         }
242         }
243
244         return m;
245 }
246
247 string
248 Playlist::audio_digest () const
249 {
250         string t;
251         
252         for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
253                 t += (*i)->digest ();
254
255                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
256                 if (fc) {
257                         t += lexical_cast<string> (fc->audio_stream()->id);
258                 }
259         }
260
261         return md5_digest (t.c_str(), t.length());
262 }
263
264 string
265 Playlist::video_digest () const
266 {
267         string t;
268         
269         for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
270                 t += (*i)->digest ();
271                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
272                 if (fc && fc->subtitle_stream()) {
273                         t += fc->subtitle_stream()->id;
274                 }
275         }
276
277         return md5_digest (t.c_str(), t.length());
278 }
279
280 ContentVideoFrame
281 Playlist::content_length () const
282 {
283         float const vfr = video_frame_rate() > 0 ? video_frame_rate() : 24;
284         int const afr = audio_frame_rate() > 0 ? audio_frame_rate() : 48000;
285
286         return max (
287                 video_length(),
288                 ContentVideoFrame (audio_length() * vfr / afr)
289                 );
290 }