# GNU MediaGoblin -- federated, autonomous media hosting # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import logging try: from PIL import Image except ImportError: import Image _log = logging.getLogger(__name__) CPU_COUNT = 2 # Just assuming for now # IMPORT MULTIPROCESSING try: import multiprocessing try: CPU_COUNT = multiprocessing.cpu_count() except NotImplementedError: _log.warning('multiprocessing.cpu_count not implemented!\n' 'Assuming 2 CPU cores') except ImportError: _log.warning('Could not import multiprocessing, assuming 2 CPU cores') # uncomment this to get a lot of logs from gst # import os;os.environ['GST_DEBUG'] = '5,python:5' import gi gi.require_version('Gst', '1.0') from gi.repository import GObject, Gst Gst.init(None) class Python3AudioThumbnailer: def __init__(self): _log.info(f'Initializing {self.__class__.__name__}') def spectrogram(self, src, dst, **kw): from mediagoblin.media_types.audio import audiotospectrogram fft_size = kw.get('fft_size', 1024) callback = kw.get('progress_callback') audiotospectrogram.drawSpectrogram(src, dst, fftSize = fft_size, progressCallback = callback) def thumbnail_spectrogram(self, src, dst, thumb_size): ''' Takes a spectrogram and creates a thumbnail from it ''' if not (type(thumb_size) == tuple and len(thumb_size) == 2): raise Exception('thumb_size argument should be a tuple(width, height)') im = Image.open(src) im_w, im_h = [float(i) for i in im.size] th_w, th_h = [float(i) for i in thumb_size] wadsworth_position = im_w * 0.3 start_x = max(( wadsworth_position - ((im_h * (th_w / th_h)) / 2.0), 0.0)) stop_x = start_x + (im_h * (th_w / th_h)) th = im.crop(( int(start_x), 0, int(stop_x), int(im_h))) th.thumbnail(thumb_size, Image.ANTIALIAS) th.save(dst) AudioThumbnailer = Python3AudioThumbnailer class AudioTranscoder: def __init__(self): _log.info(f'Initializing {self.__class__.__name__}') # Instantiate MainLoop self._loop = GObject.MainLoop() self._failed = None def transcode(self, src, dst, mux_name='webmmux',quality=0.3, progress_callback=None, **kw): def _on_pad_added(element, pad, connect_to): caps = pad.query_caps(None) name = caps.to_string() _log.debug(f'on_pad_added: {name}') if name.startswith('audio') and not connect_to.is_linked(): pad.link(connect_to) _log.info(f'Transcoding {src} into {dst}') self.__on_progress = progress_callback # Set up pipeline tolerance = 80000000 self.pipeline = Gst.Pipeline() filesrc = Gst.ElementFactory.make('filesrc', 'filesrc') filesrc.set_property('location', src) decodebin = Gst.ElementFactory.make('decodebin', 'decodebin') queue = Gst.ElementFactory.make('queue', 'queue') decodebin.connect('pad-added', _on_pad_added, queue.get_static_pad('sink')) audiorate = Gst.ElementFactory.make('audiorate', 'audiorate') audiorate.set_property('tolerance', tolerance) audioconvert = Gst.ElementFactory.make('audioconvert', 'audioconvert') caps_struct = Gst.Structure.new_empty('audio/x-raw') caps_struct.set_value('channels', 2) caps = Gst.Caps.new_empty() caps.append_structure(caps_struct) capsfilter = Gst.ElementFactory.make('capsfilter', 'capsfilter') capsfilter.set_property('caps', caps) enc = Gst.ElementFactory.make('vorbisenc', 'enc') enc.set_property('quality', quality) mux = Gst.ElementFactory.make(mux_name, 'mux') progressreport = Gst.ElementFactory.make('progressreport', 'progress') progressreport.set_property('silent', True) sink = Gst.ElementFactory.make('filesink', 'sink') sink.set_property('location', dst) # add to pipeline for e in [filesrc, decodebin, queue, audiorate, audioconvert, capsfilter, enc, mux, progressreport, sink]: self.pipeline.add(e) # link elements filesrc.link(decodebin) decodebin.link(queue) queue.link(audiorate) audiorate.link(audioconvert) audioconvert.link(capsfilter) capsfilter.link(enc) enc.link(mux) mux.link(progressreport) progressreport.link(sink) self.bus = self.pipeline.get_bus() self.bus.add_signal_watch() self.bus.connect('message', self.__on_bus_message) # run self.pipeline.set_state(Gst.State.PLAYING) self._loop.run() def __on_bus_message(self, bus, message): _log.debug(message.type) if (message.type == Gst.MessageType.ELEMENT and message.has_name('progress')): structure = message.get_structure() (success, percent) = structure.get_int('percent') if self.__on_progress and success: self.__on_progress(percent) _log.info(f'{percent}% done...') elif message.type == Gst.MessageType.EOS: _log.info('Done') self.halt() elif message.type == Gst.MessageType.ERROR: _log.error(message.parse_error()) self.halt() def halt(self): if getattr(self, 'pipeline', False): self.pipeline.set_state(Gst.State.NULL) del self.pipeline _log.info('Quitting MainLoop gracefully...') GObject.idle_add(self._loop.quit) if __name__ == '__main__': import sys logging.basicConfig() _log.setLevel(logging.INFO) #transcoder = AudioTranscoder() #data = transcoder.discover(sys.argv[1]) #res = transcoder.transcode(*sys.argv[1:3]) thumbnailer = AudioThumbnailer() thumbnailer.spectrogram(*sys.argv[1:], width=640)