1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
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.
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.
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/>.
21 from mediagoblin
.processing
import BadMediaFail
22 from mediagoblin
.media_types
.audio
import audioprocessing
25 _log
= logging
.getLogger(__name__
)
27 CPU_COUNT
= 2 # Just assuming for now
29 # IMPORT MULTIPROCESSING
31 import multiprocessing
33 CPU_COUNT
= multiprocessing
.cpu_count()
34 except NotImplementedError:
35 _log
.warning('multiprocessing.cpu_count not implemented!\n'
36 'Assuming 2 CPU cores')
38 _log
.warning('Could not import multiprocessing, assuming 2 CPU cores')
43 gobject
.threads_init()
45 raise Exception('gobject could not be found')
51 # We won't settle for less. For now, this is an arbitrary limit
52 # as we have not tested with > 0.10
57 import gst
.extend
.discoverer
59 raise Exception('gst/pygst >= 0.10 could not be imported')
64 class AudioThumbnailer(object):
66 _log
.info('Initializing {0}'.format(self
.__class
__.__name
__))
68 def spectrogram(self
, src
, dst
, **kw
):
70 height
= int(kw
.get('height', float(width
) * 0.3))
71 fft_size
= kw
.get('fft_size', 2048)
72 callback
= kw
.get('progress_callback')
74 processor
= audioprocessing
.AudioProcessor(
79 samples_per_pixel
= processor
.audio_file
.nframes
/ float(width
)
81 spectrogram
= audioprocessing
.SpectrogramImage(width
, height
, fft_size
)
83 for x
in range(width
):
84 if callback
and x
% (width
/ 10) == 0:
85 callback((x
* 100) / width
)
87 seek_point
= int(x
* samples_per_pixel
)
89 (spectral_centroid
, db_spectrum
) = processor
.spectral_centroid(
92 spectrogram
.draw_spectrum(x
, db_spectrum
)
99 def thumbnail_spectrogram(self
, src
, dst
, thumb_size
):
101 Takes a spectrogram and creates a thumbnail from it
103 if not (type(thumb_size
) == tuple and len(thumb_size
) == 2):
104 raise Exception('thumb_size argument should be a tuple(width, height)')
108 im_w
, im_h
= [float(i
) for i
in im
.size
]
109 th_w
, th_h
= [float(i
) for i
in thumb_size
]
111 wadsworth_position
= im_w
* 0.3
114 wadsworth_position
- ((im_h
* (th_w
/ th_h
)) / 2.0),
117 stop_x
= start_x
+ (im_h
* (th_w
/ th_h
))
121 int(stop_x
), int(im_h
)))
123 if th
.size
[0] > th_w
or th
.size
[1] > th_h
:
124 th
.thumbnail(thumb_size
, Image
.ANTIALIAS
)
129 class AudioTranscoder(object):
131 _log
.info('Initializing {0}'.format(self
.__class
__.__name
__))
133 # Instantiate MainLoop
134 self
._loop
= gobject
.MainLoop()
137 def discover(self
, src
):
139 _log
.info('Discovering {0}'.format(src
))
140 self
._discovery
_path
= src
142 self
._discoverer
= gst
.extend
.discoverer
.Discoverer(
143 self
._discovery
_path
)
144 self
._discoverer
.connect('discovered', self
.__on
_discovered
)
145 self
._discoverer
.discover()
147 self
._loop
.run() # Run MainLoop
152 # Once MainLoop has returned, return discovery data
153 return getattr(self
, '_discovery_data', False)
155 def __on_discovered(self
, data
, is_media
):
157 self
._failed
= BadMediaFail()
158 _log
.error('Could not discover {0}'.format(self
._src
_path
))
161 _log
.debug('Discovered: {0}'.format(data
.__dict
__))
163 self
._discovery
_data
= data
165 # Gracefully shut down MainLoop
168 def transcode(self
, src
, dst
, **kw
):
169 _log
.info('Transcoding {0} into {1}'.format(src
, dst
))
170 self
._discovery
_data
= kw
.get('data', self
.discover(src
))
172 self
.__on
_progress
= kw
.get('progress_callback')
174 quality
= kw
.get('quality', 0.3)
178 'vorbisenc quality={0} ! webmmux'.format(quality
))
181 self
.pipeline
= gst
.parse_launch(
182 'filesrc location="{src}" ! '
183 'decodebin2 ! queue ! audiorate tolerance={tolerance} ! '
184 'audioconvert ! audio/x-raw-float,channels=2 ! '
186 'progressreport silent=true ! '
187 'filesink location="{dst}"'.format(
190 mux_string
=mux_string
,
193 self
.bus
= self
.pipeline
.get_bus()
194 self
.bus
.add_signal_watch()
195 self
.bus
.connect('message', self
.__on
_bus
_message
)
197 self
.pipeline
.set_state(gst
.STATE_PLAYING
)
201 def __on_bus_message(self
, bus
, message
):
204 if (message
.type == gst
.MESSAGE_ELEMENT
205 and message
.structure
.get_name() == 'progress'):
206 data
= dict(message
.structure
)
208 if self
.__on
_progress
:
209 self
.__on
_progress
(data
.get('percent'))
211 _log
.info('{0}% done...'.format(
212 data
.get('percent')))
213 elif message
.type == gst
.MESSAGE_EOS
:
218 if getattr(self
, 'pipeline', False):
219 self
.pipeline
.set_state(gst
.STATE_NULL
)
221 _log
.info('Quitting MainLoop gracefully...')
222 gobject
.idle_add(self
._loop
.quit
)
224 if __name__
== '__main__':
226 logging
.basicConfig()
227 _log
.setLevel(logging
.INFO
)
229 #transcoder = AudioTranscoder()
230 #data = transcoder.discover(sys.argv[1])
231 #res = transcoder.transcode(*sys.argv[1:3])
233 thumbnailer
= AudioThumbnailer()
235 thumbnailer
.spectrogram(*sys
.argv
[1:], width
=640)