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