Commit | Line | Data |
---|---|---|
5a34a80d JW |
1 | # GNU MediaGoblin -- federated, autonomous media hosting |
2 | # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. | |
3 | # | |
4 | # This program is free software: you can redistribute it and/or modify | |
5 | # it under the terms of the GNU Affero General Public License as published by | |
6 | # the Free Software Foundation, either version 3 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 Affero General Public License for more details. | |
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | import pdb | |
18 | import logging | |
352a1807 | 19 | import Image |
5a34a80d JW |
20 | |
21 | from mediagoblin.processing import BadMediaFail | |
10085b77 | 22 | from mediagoblin.media_types.audio import audioprocessing |
5a34a80d JW |
23 | |
24 | ||
25 | _log = logging.getLogger(__name__) | |
26 | ||
c56d4b55 | 27 | CPU_COUNT = 2 # Just assuming for now |
5a34a80d JW |
28 | |
29 | # IMPORT MULTIPROCESSING | |
30 | try: | |
31 | import multiprocessing | |
32 | try: | |
33 | CPU_COUNT = multiprocessing.cpu_count() | |
34 | except NotImplementedError: | |
35 | _log.warning('multiprocessing.cpu_count not implemented!\n' | |
36 | 'Assuming 2 CPU cores') | |
37 | except ImportError: | |
38 | _log.warning('Could not import multiprocessing, assuming 2 CPU cores') | |
39 | ||
40 | # IMPORT GOBJECT | |
41 | try: | |
42 | import gobject | |
43 | gobject.threads_init() | |
44 | except ImportError: | |
45 | raise Exception('gobject could not be found') | |
46 | ||
47 | # IMPORT PYGST | |
48 | try: | |
49 | import pygst | |
50 | ||
51 | # We won't settle for less. For now, this is an arbitrary limit | |
52 | # as we have not tested with > 0.10 | |
53 | pygst.require('0.10') | |
54 | ||
55 | import gst | |
56 | ||
57 | import gst.extend.discoverer | |
58 | except ImportError: | |
59 | raise Exception('gst/pygst >= 0.10 could not be imported') | |
60 | ||
10085b77 JW |
61 | import numpy |
62 | ||
c56d4b55 | 63 | |
10085b77 JW |
64 | class AudioThumbnailer(object): |
65 | def __init__(self): | |
66 | _log.info('Initializing {0}'.format(self.__class__.__name__)) | |
67 | ||
68 | def spectrogram(self, src, dst, **kw): | |
69 | width = kw['width'] | |
70 | height = int(kw.get('height', float(width) * 0.3)) | |
71 | fft_size = kw.get('fft_size', 2048) | |
72 | callback = kw.get('progress_callback') | |
73 | ||
74 | processor = audioprocessing.AudioProcessor( | |
75 | src, | |
76 | fft_size, | |
77 | numpy.hanning) | |
78 | ||
79 | samples_per_pixel = processor.audio_file.nframes / float(width) | |
80 | ||
81 | spectrogram = audioprocessing.SpectrogramImage(width, height, fft_size) | |
82 | ||
83 | for x in range(width): | |
84 | if callback and x % (width / 10) == 0: | |
85 | callback((x * 100) / width) | |
86 | ||
87 | seek_point = int(x * samples_per_pixel) | |
88 | ||
89 | (spectral_centroid, db_spectrum) = processor.spectral_centroid( | |
90 | seek_point) | |
91 | ||
92 | spectrogram.draw_spectrum(x, db_spectrum) | |
93 | ||
94 | if callback: | |
95 | callback(100) | |
96 | ||
97 | spectrogram.save(dst) | |
98 | ||
99 | def thumbnail_spectrogram(self, src, dst, thumb_size): | |
100 | ''' | |
101 | Takes a spectrogram and creates a thumbnail from it | |
102 | ''' | |
103 | if not (type(thumb_size) == tuple and len(thumb_size) == 2): | |
4f4f2531 | 104 | raise Exception('thumb_size argument should be a tuple(width, height)') |
10085b77 JW |
105 | |
106 | im = Image.open(src) | |
107 | ||
108 | im_w, im_h = [float(i) for i in im.size] | |
109 | th_w, th_h = [float(i) for i in thumb_size] | |
110 | ||
111 | wadsworth_position = im_w * 0.3 | |
112 | ||
113 | start_x = max(( | |
4f4f2531 | 114 | wadsworth_position - ((im_h * (th_w / th_h)) / 2.0), |
10085b77 JW |
115 | 0.0)) |
116 | ||
117 | stop_x = start_x + (im_h * (th_w / th_h)) | |
118 | ||
119 | th = im.crop(( | |
120 | int(start_x), 0, | |
121 | int(stop_x), int(im_h))) | |
122 | ||
123 | if th.size[0] > th_w or th.size[1] > th_h: | |
124 | th.thumbnail(thumb_size, Image.ANTIALIAS) | |
125 | ||
126 | th.save(dst) | |
127 | ||
128 | ||
5a34a80d JW |
129 | class AudioTranscoder(object): |
130 | def __init__(self): | |
131 | _log.info('Initializing {0}'.format(self.__class__.__name__)) | |
132 | ||
133 | # Instantiate MainLoop | |
134 | self._loop = gobject.MainLoop() | |
ec4261a4 | 135 | self._failed = None |
5a34a80d JW |
136 | |
137 | def discover(self, src): | |
ec4261a4 | 138 | self._src_path = src |
5a34a80d JW |
139 | _log.info('Discovering {0}'.format(src)) |
140 | self._discovery_path = src | |
141 | ||
142 | self._discoverer = gst.extend.discoverer.Discoverer( | |
143 | self._discovery_path) | |
144 | self._discoverer.connect('discovered', self.__on_discovered) | |
145 | self._discoverer.discover() | |
146 | ||
147 | self._loop.run() # Run MainLoop | |
148 | ||
ec4261a4 JW |
149 | if self._failed: |
150 | raise self._failed | |
151 | ||
5a34a80d | 152 | # Once MainLoop has returned, return discovery data |
ec4261a4 | 153 | return getattr(self, '_discovery_data', False) |
5a34a80d JW |
154 | |
155 | def __on_discovered(self, data, is_media): | |
156 | if not is_media: | |
ec4261a4 | 157 | self._failed = BadMediaFail() |
5a34a80d | 158 | _log.error('Could not discover {0}'.format(self._src_path)) |
ec4261a4 | 159 | self.halt() |
5a34a80d JW |
160 | |
161 | _log.debug('Discovered: {0}'.format(data.__dict__)) | |
162 | ||
163 | self._discovery_data = data | |
164 | ||
165 | # Gracefully shut down MainLoop | |
166 | self.halt() | |
167 | ||
168 | def transcode(self, src, dst, **kw): | |
ec4261a4 | 169 | _log.info('Transcoding {0} into {1}'.format(src, dst)) |
5a34a80d JW |
170 | self._discovery_data = kw.get('data', self.discover(src)) |
171 | ||
172 | self.__on_progress = kw.get('progress_callback') | |
173 | ||
174 | quality = kw.get('quality', 0.3) | |
175 | ||
10085b77 JW |
176 | mux_string = kw.get( |
177 | 'mux_string', | |
178 | 'vorbisenc quality={0} ! webmmux'.format(quality)) | |
179 | ||
5a34a80d JW |
180 | # Set up pipeline |
181 | self.pipeline = gst.parse_launch( | |
c56d4b55 | 182 | 'filesrc location="{src}" ! ' |
5a34a80d JW |
183 | 'decodebin2 ! queue ! audiorate tolerance={tolerance} ! ' |
184 | 'audioconvert ! audio/x-raw-float,channels=2 ! ' | |
10085b77 | 185 | '{mux_string} ! ' |
5a34a80d JW |
186 | 'progressreport silent=true ! ' |
187 | 'filesink location="{dst}"'.format( | |
188 | src=src, | |
189 | tolerance=80000000, | |
10085b77 | 190 | mux_string=mux_string, |
5a34a80d JW |
191 | dst=dst)) |
192 | ||
193 | self.bus = self.pipeline.get_bus() | |
194 | self.bus.add_signal_watch() | |
195 | self.bus.connect('message', self.__on_bus_message) | |
196 | ||
197 | self.pipeline.set_state(gst.STATE_PLAYING) | |
198 | ||
199 | self._loop.run() | |
200 | ||
201 | def __on_bus_message(self, bus, message): | |
202 | _log.debug(message) | |
203 | ||
204 | if (message.type == gst.MESSAGE_ELEMENT | |
205 | and message.structure.get_name() == 'progress'): | |
206 | data = dict(message.structure) | |
207 | ||
208 | if self.__on_progress: | |
64712915 | 209 | self.__on_progress(data.get('percent')) |
5a34a80d JW |
210 | |
211 | _log.info('{0}% done...'.format( | |
212 | data.get('percent'))) | |
213 | elif message.type == gst.MESSAGE_EOS: | |
214 | _log.info('Done') | |
215 | self.halt() | |
216 | ||
217 | def halt(self): | |
10085b77 JW |
218 | if getattr(self, 'pipeline', False): |
219 | self.pipeline.set_state(gst.STATE_NULL) | |
220 | del self.pipeline | |
5a34a80d JW |
221 | _log.info('Quitting MainLoop gracefully...') |
222 | gobject.idle_add(self._loop.quit) | |
223 | ||
224 | if __name__ == '__main__': | |
225 | import sys | |
226 | logging.basicConfig() | |
227 | _log.setLevel(logging.INFO) | |
228 | ||
10085b77 JW |
229 | #transcoder = AudioTranscoder() |
230 | #data = transcoder.discover(sys.argv[1]) | |
231 | #res = transcoder.transcode(*sys.argv[1:3]) | |
232 | ||
233 | thumbnailer = AudioThumbnailer() | |
234 | ||
235 | thumbnailer.spectrogram(*sys.argv[1:], width=640) | |
5a34a80d JW |
236 | |
237 | pdb.set_trace() |