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/>.
23 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')
40 # uncomment this to get a lot of logs from gst
41 # import os;os.environ['GST_DEBUG'] = '5,python:5'
44 gi
.require_version('Gst', '1.0')
45 from gi
.repository
import GObject
, Gst
51 class AudioThumbnailer(object):
53 _log
.info('Initializing {0}'.format(self
.__class
__.__name
__))
55 def spectrogram(self
, src
, dst
, **kw
):
57 height
= int(kw
.get('height', float(width
) * 0.3))
58 fft_size
= kw
.get('fft_size', 2048)
59 callback
= kw
.get('progress_callback')
60 processor
= audioprocessing
.AudioProcessor(
65 samples_per_pixel
= processor
.audio_file
.nframes
/ float(width
)
67 spectrogram
= audioprocessing
.SpectrogramImage(width
, height
, fft_size
)
69 for x
in range(width
):
70 if callback
and x
% (width
/ 10) == 0:
71 callback((x
* 100) / width
)
73 seek_point
= int(x
* samples_per_pixel
)
75 (spectral_centroid
, db_spectrum
) = processor
.spectral_centroid(
78 spectrogram
.draw_spectrum(x
, db_spectrum
)
85 def thumbnail_spectrogram(self
, src
, dst
, thumb_size
):
87 Takes a spectrogram and creates a thumbnail from it
89 if not (type(thumb_size
) == tuple and len(thumb_size
) == 2):
90 raise Exception('thumb_size argument should be a tuple(width, height)')
94 im_w
, im_h
= [float(i
) for i
in im
.size
]
95 th_w
, th_h
= [float(i
) for i
in thumb_size
]
97 wadsworth_position
= im_w
* 0.3
100 wadsworth_position
- ((im_h
* (th_w
/ th_h
)) / 2.0),
103 stop_x
= start_x
+ (im_h
* (th_w
/ th_h
))
107 int(stop_x
), int(im_h
)))
109 th
.thumbnail(thumb_size
, Image
.ANTIALIAS
)
114 class AudioTranscoder(object):
116 _log
.info('Initializing {0}'.format(self
.__class
__.__name
__))
118 # Instantiate MainLoop
119 self
._loop
= GObject
.MainLoop()
122 def transcode(self
, src
, dst
, mux_name
='webmmux',quality
=0.3,
123 progress_callback
=None, **kw
):
124 def _on_pad_added(element
, pad
, connect_to
):
125 caps
= pad
.query_caps(None)
126 name
= caps
.to_string()
127 _log
.debug('on_pad_added: {0}'.format(name
))
128 if name
.startswith('audio') and not connect_to
.is_linked():
130 _log
.info('Transcoding {0} into {1}'.format(src
, dst
))
131 self
.__on
_progress
= progress_callback
134 self
.pipeline
= Gst
.Pipeline()
135 filesrc
= Gst
.ElementFactory
.make('filesrc', 'filesrc')
136 filesrc
.set_property('location', src
)
137 decodebin
= Gst
.ElementFactory
.make('decodebin', 'decodebin')
138 queue
= Gst
.ElementFactory
.make('queue', 'queue')
139 decodebin
.connect('pad-added', _on_pad_added
,
140 queue
.get_static_pad('sink'))
141 audiorate
= Gst
.ElementFactory
.make('audiorate', 'audiorate')
142 audiorate
.set_property('tolerance', tolerance
)
143 audioconvert
= Gst
.ElementFactory
.make('audioconvert', 'audioconvert')
144 caps_struct
= Gst
.Structure
.new_empty('audio/x-raw')
145 caps_struct
.set_value('channels', 2)
146 caps
= Gst
.Caps
.new_empty()
147 caps
.append_structure(caps_struct
)
148 capsfilter
= Gst
.ElementFactory
.make('capsfilter', 'capsfilter')
149 capsfilter
.set_property('caps', caps
)
150 enc
= Gst
.ElementFactory
.make('vorbisenc', 'enc')
151 enc
.set_property('quality', quality
)
152 mux
= Gst
.ElementFactory
.make(mux_name
, 'mux')
153 progressreport
= Gst
.ElementFactory
.make('progressreport', 'progress')
154 progressreport
.set_property('silent', True)
155 sink
= Gst
.ElementFactory
.make('filesink', 'sink')
156 sink
.set_property('location', dst
)
158 for e
in [filesrc
, decodebin
, queue
, audiorate
, audioconvert
,
159 capsfilter
, enc
, mux
, progressreport
, sink
]:
162 filesrc
.link(decodebin
)
163 decodebin
.link(queue
)
164 queue
.link(audiorate
)
165 audiorate
.link(audioconvert
)
166 audioconvert
.link(capsfilter
)
169 mux
.link(progressreport
)
170 progressreport
.link(sink
)
171 self
.bus
= self
.pipeline
.get_bus()
172 self
.bus
.add_signal_watch()
173 self
.bus
.connect('message', self
.__on
_bus
_message
)
175 self
.pipeline
.set_state(Gst
.State
.PLAYING
)
178 def __on_bus_message(self
, bus
, message
):
179 _log
.debug(message
.type)
180 if (message
.type == Gst
.MessageType
.ELEMENT
181 and message
.has_name('progress')):
182 structure
= message
.get_structure()
183 (success
, percent
) = structure
.get_int('percent')
184 if self
.__on
_progress
and success
:
185 self
.__on
_progress
(percent
)
186 _log
.info('{0}% done...'.format(percent
))
187 elif message
.type == Gst
.MessageType
.EOS
:
190 elif message
.type == Gst
.MessageType
.ERROR
:
191 _log
.error(message
.parse_error())
195 if getattr(self
, 'pipeline', False):
196 self
.pipeline
.set_state(Gst
.State
.NULL
)
198 _log
.info('Quitting MainLoop gracefully...')
199 GObject
.idle_add(self
._loop
.quit
)
201 if __name__
== '__main__':
203 logging
.basicConfig()
204 _log
.setLevel(logging
.INFO
)
206 #transcoder = AudioTranscoder()
207 #data = transcoder.discover(sys.argv[1])
208 #res = transcoder.transcode(*sys.argv[1:3])
210 thumbnailer
= AudioThumbnailer()
212 thumbnailer
.spectrogram(*sys
.argv
[1:], width
=640)