f346cb6e03d0377eb9403d0d29cbc0a4f1835434
[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_frame_rate ();
136                 }
137                 break;
138         }
139         case AUDIO_SNDFILE:
140                 return _sndfile.front()->audio_frame_rate ();
141         }
142
143         return 0;
144 }
145
146 float
147 Playlist::video_frame_rate () const
148 {
149         if (_video.empty ()) {
150                 return 0;
151         }
152         
153         /* XXX: assuming all the same */
154         return _video.front()->video_frame_rate ();
155 }
156
157 libdcp::Size
158 Playlist::video_size () const
159 {
160         if (_video.empty ()) {
161                 return libdcp::Size ();
162         }
163
164         /* XXX: assuming all the same */
165         return _video.front()->video_size ();
166 }
167
168 ContentVideoFrame
169 Playlist::video_length () const
170 {
171         ContentVideoFrame len = 0;
172         for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
173                 len += (*i)->video_length ();
174         }
175         
176         return len;
177 }
178
179 bool
180 Playlist::has_audio () const
181 {
182         /* XXX */
183         return true;
184 }
185
186 void
187 Playlist::content_changed (weak_ptr<Content> c, int p)
188 {
189         ContentChanged (c, p);
190 }
191
192 shared_ptr<const FFmpegContent>
193 Playlist::first_ffmpeg () const
194 {
195         for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
196                 shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
197                 if (fc) {
198                         return fc;
199                 }
200         }
201
202         return shared_ptr<const FFmpegContent> ();
203 }
204         
205
206 AudioMapping
207 Playlist::default_audio_mapping () const
208 {
209         AudioMapping m;
210
211         switch (_audio_from) {
212         case AUDIO_FFMPEG:
213         {
214                 shared_ptr<const FFmpegContent> fc = first_ffmpeg ();
215                 if (!fc) {
216                         break;
217                 }
218                 
219                 /* XXX: assumes all the same */
220                 if (fc->audio_channels() == 1) {
221                         /* Map mono sources to centre */
222                         m.add (AudioMapping::Channel (fc, 0), libdcp::CENTRE);
223                 } else {
224                         int const N = min (fc->audio_channels (), MAX_AUDIO_CHANNELS);
225                         /* Otherwise just start with a 1:1 mapping */
226                         for (int i = 0; i < N; ++i) {
227                                 m.add (AudioMapping::Channel (fc, i), (libdcp::Channel) i);
228                         }
229                 }
230                 break;
231         }
232
233         case AUDIO_SNDFILE:
234         {
235                 int n = 0;
236                 for (list<shared_ptr<const SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
237                         for (int j = 0; j < (*i)->audio_channels(); ++j) {
238                                 m.add (AudioMapping::Channel (*i, j), (libdcp::Channel) n);
239                                 ++n;
240                                 if (n >= MAX_AUDIO_CHANNELS) {
241                                         break;
242                                 }
243                         }
244                         if (n >= MAX_AUDIO_CHANNELS) {
245                                 break;
246                         }
247                 }
248                 break;
249         }
250         }
251
252         return m;
253 }