diff options
-rw-r--r-- | mediagoblin/config_spec.ini | 12 | ||||
-rw-r--r-- | mediagoblin/media_types/ascii/asciitoimage.py | 25 | ||||
-rw-r--r-- | mediagoblin/media_types/ascii/processing.py | 10 | ||||
-rw-r--r-- | mediagoblin/media_types/audio/processing.py | 7 | ||||
-rw-r--r-- | mediagoblin/media_types/video/processing.py | 10 | ||||
-rw-r--r-- | mediagoblin/media_types/video/transcoders.py | 57 | ||||
-rw-r--r-- | mediagoblin/static/css/audio.css | 2 | ||||
-rw-r--r-- | mediagoblin/static/js/audio.js | 10 |
8 files changed, 87 insertions, 46 deletions
diff --git a/mediagoblin/config_spec.ini b/mediagoblin/config_spec.ini index e30825de..01853e48 100644 --- a/mediagoblin/config_spec.ini +++ b/mediagoblin/config_spec.ini @@ -86,11 +86,23 @@ max_height = integer(default=180) # Should we keep the original file? keep_original = boolean(default=False) +# 0 means autodetect, autodetect means number_of_CPUs - 1 +vp8_threads = integer(default=0) +# Range: 0..10 +vp8_quality = integer(default=8) +# Range: -0.1..1 +vorbis_quality = float(default=0.3) + + [media_type:mediagoblin.media_types.audio] # vorbisenc qualiy quality = float(default=0.3) create_spectrogram = boolean(default=True) +spectrogram_fft_size = integer(default=4096) + +[media_type:mediagoblin.media_types.ascii] +thumbnail_font = string(default=None) [beaker.cache] type = string(default="file") diff --git a/mediagoblin/media_types/ascii/asciitoimage.py b/mediagoblin/media_types/ascii/asciitoimage.py index 3017d2ad..108de023 100644 --- a/mediagoblin/media_types/ascii/asciitoimage.py +++ b/mediagoblin/media_types/ascii/asciitoimage.py @@ -34,31 +34,12 @@ class AsciiToImage(object): - font_size: Font size, ``int`` default: 11 ''' - - # Font file path - _font = None - - _font_size = 11 - - # ImageFont instance - _if = None - - # ImageFont - _if_dims = None - - # Image instance - _im = None - def __init__(self, **kw): - if kw.get('font'): - self._font = kw.get('font') - else: - self._font = pkg_resources.resource_filename( + self._font = kw.get('font', pkg_resources.resource_filename( 'mediagoblin.media_types.ascii', - os.path.join('fonts', 'Inconsolata.otf')) + os.path.join('fonts', 'Inconsolata.otf'))) - if kw.get('font_size'): - self._font_size = kw.get('font_size') + self._font_size = kw.get('font_size', 11) self._if = ImageFont.truetype( self._font, diff --git a/mediagoblin/media_types/ascii/processing.py b/mediagoblin/media_types/ascii/processing.py index a2a52e9d..04d1166c 100644 --- a/mediagoblin/media_types/ascii/processing.py +++ b/mediagoblin/media_types/ascii/processing.py @@ -42,6 +42,7 @@ def process_ascii(entry): ''' Code to process a txt file ''' + ascii_config = mgg.global_config['media_type:mediagoblin.media_types.ascii'] workbench = mgg.workbench_manager.create_workbench() # Conversions subdirectory to avoid collisions conversions_subdir = os.path.join( @@ -77,7 +78,14 @@ def process_ascii(entry): tmp_thumb_filename = os.path.join( conversions_subdir, thumb_filepath[-1]) - converter = asciitoimage.AsciiToImage() + ascii_converter_args = {} + + if ascii_config['thumbnail_font']: + ascii_converter_args.update( + {'font': ascii_config['thumbnail_font']}) + + converter = asciitoimage.AsciiToImage( + **ascii_converter_args) thumb = converter._create_image( queued_file.read()) diff --git a/mediagoblin/media_types/audio/processing.py b/mediagoblin/media_types/audio/processing.py index c0ff7bff..f0b8d0f9 100644 --- a/mediagoblin/media_types/audio/processing.py +++ b/mediagoblin/media_types/audio/processing.py @@ -27,7 +27,7 @@ from mediagoblin.media_types.audio.transcoders import AudioTranscoder, \ _log = logging.getLogger(__name__) def sniff_handler(media_file, **kw): - try: + try: transcoder = AudioTranscoder() data = transcoder.discover(media_file.name) except BadMediaFail: @@ -94,7 +94,8 @@ def process_audio(entry): thumbnailer.spectrogram( wav_tmp.name, spectrogram_tmp.name, - width=mgg.global_config['media:medium']['max_width']) + width=mgg.global_config['media:medium']['max_width'], + fft_size=audio_config['spectrogram_fft_size']) _log.debug('Saving spectrogram...') mgg.public_store.get_file(spectrogram_filepath, 'wb').write( @@ -121,7 +122,7 @@ def process_audio(entry): entry.media_files['thumb'] = thumb_filepath else: entry.media_files['thumb'] = ['fake', 'thumb', 'path.jpg'] - + mgg.queue_store.delete_file(queued_filepath) entry.save() diff --git a/mediagoblin/media_types/video/processing.py b/mediagoblin/media_types/video/processing.py index d4b4e983..5bbcc92f 100644 --- a/mediagoblin/media_types/video/processing.py +++ b/mediagoblin/media_types/video/processing.py @@ -16,15 +16,12 @@ import tempfile import logging -import os from mediagoblin import mg_globals as mgg -from mediagoblin.processing import mark_entry_failed, \ +from mediagoblin.processing import \ create_pub_filepath, FilenameBuilder from . import transcoders -logging.basicConfig() - _log = logging.getLogger(__name__) _log.setLevel(logging.DEBUG) @@ -73,7 +70,10 @@ def process_video(entry): with tmp_dst: # Transcode queued file to a VP8/vorbis file that fits in a 640x640 square transcoder = transcoders.VideoTranscoder() - transcoder.transcode(queued_filename, tmp_dst.name) + transcoder.transcode(queued_filename, tmp_dst.name, + vp8_quality=video_config['vp8_quality'], + vp8_threads=video_config['vp8_threads'], + vorbis_quality=video_config['vorbis_quality']) # Push transcoded video to public storage _log.debug('Saving medium...') diff --git a/mediagoblin/media_types/video/transcoders.py b/mediagoblin/media_types/video/transcoders.py index 74821877..d5c162ba 100644 --- a/mediagoblin/media_types/video/transcoders.py +++ b/mediagoblin/media_types/video/transcoders.py @@ -72,6 +72,11 @@ class VideoThumbnailer: Set up playbin pipeline in order to get video properties. Initializes and runs the gobject.MainLoop() + + Abstract + - Set up a playbin with a fake audio sink and video sink. Load the video + into the playbin + - Initialize ''' self.errors = [] @@ -105,9 +110,10 @@ class VideoThumbnailer: self.loop.run() def _on_bus_message(self, bus, message): - _log.debug(' BUS MESSAGE: {0}'.format(message)) + _log.debug(' thumbnail playbin: {0}'.format(message)) if message.type == gst.MESSAGE_ERROR: + _log.error('thumbnail playbin: {0}'.format(message)) gobject.idle_add(self._on_bus_error) elif message.type == gst.MESSAGE_STATE_CHANGED: @@ -154,13 +160,14 @@ class VideoThumbnailer: return False def _on_thumbnail_bus_message(self, bus, message): - _log.debug('Thumbnail bus called, message: {0}'.format(message)) + _log.debug('thumbnail: {0}'.format(message)) if message.type == gst.MESSAGE_ERROR: _log.error(message) gobject.idle_add(self._on_bus_error) if message.type == gst.MESSAGE_STATE_CHANGED: + _log.debug('State changed') _prev, state, _pending = message.parse_state_changed() if (state == gst.STATE_PAUSED and @@ -184,6 +191,7 @@ class VideoThumbnailer: break # Apply the wadsworth constant, fallback to 1 second + # TODO: Will break if video is shorter than 1 sec seek_amount = max(self.duration / 100 * 30, 1 * gst.SECOND) _log.debug('seek amount: {0}'.format(seek_amount)) @@ -204,14 +212,19 @@ class VideoThumbnailer: _log.info(message) self.shutdown() else: - pass - #self.thumbnail_pipeline.set_state(gst.STATE_PAUSED) + _log.debug('Seek successful') + self.thumbnail_pipeline.set_state(gst.STATE_PAUSED) #pdb.set_trace() + else: + _log.debug('Won\'t seek: \t{0}\n\t{1}'.format( + self.state, + message.src)) def buffer_probe_handler_real(self, pad, buff, name): ''' Capture buffers as gdk_pixbufs when told to. ''' + _log.info('Capturing frame') try: caps = buff.caps if caps is None: @@ -237,14 +250,16 @@ class VideoThumbnailer: self.shutdown() - except gst.QueryError: - pass + except gst.QueryError as e: + _log.error('QueryError: {0}'.format(e)) + return False def buffer_probe_handler(self, pad, buff, name): ''' Proxy function for buffer_probe_handler_real ''' + _log.debug('Attaching real buffer handler to gobject idle event') gobject.idle_add( lambda: self.buffer_probe_handler_real(pad, buff, name)) @@ -265,7 +280,7 @@ class VideoThumbnailer: return self._get_duration(pipeline, retries + 1) def _on_timeout(self): - _log.error('TIMEOUT! DROP EVERYTHING!') + _log.error('Timeout in thumbnailer!') self.shutdown() def _on_bus_error(self, *args): @@ -342,8 +357,25 @@ class VideoTranscoder: self.source_path = src self.destination_path = dst - # Options - self.destination_dimensions = kwargs.get('dimensions') or (640, 640) + # vp8enc options + self.destination_dimensions = kwargs.get('dimensions', (640, 640)) + self.vp8_quality = kwargs.get('vp8_quality', 8) + # Number of threads used by vp8enc: + # number of real cores - 1 as per recommendation on + # <http://www.webmproject.org/tools/encoder-parameters/#6-multi-threaded-encode-and-decode> + self.vp8_threads = kwargs.get('vp8_threads', CPU_COUNT - 1) + + # 0 means auto-detect, but dict.get() only falls back to CPU_COUNT + # if value is None, this will correct our incompatibility with + # dict.get() + # This will also correct cases where there's only 1 CPU core, see + # original self.vp8_threads assignment above. + if self.vp8_threads == 0: + self.vp8_threads = CPU_COUNT + + # vorbisenc options + self.vorbis_quality = kwargs.get('vorbis_quality', 0.3) + self._progress_callback = kwargs.get('progress_callback') or None if not type(self.destination_dimensions) == tuple: @@ -456,8 +488,9 @@ class VideoTranscoder: self.pipeline.add(self.capsfilter) self.vp8enc = gst.element_factory_make('vp8enc', 'vp8enc') - self.vp8enc.set_property('quality', 6) - self.vp8enc.set_property('threads', 2) + self.vp8enc.set_property('quality', self.vp8_quality) + self.vp8enc.set_property('threads', self.vp8_threads) + self.vp8enc.set_property('max-latency', 25) self.pipeline.add(self.vp8enc) # Audio elements @@ -480,7 +513,7 @@ class VideoTranscoder: self.pipeline.add(self.audiocapsfilter) self.vorbisenc = gst.element_factory_make('vorbisenc', 'vorbisenc') - self.vorbisenc.set_property('quality', 1) + self.vorbisenc.set_property('quality', self.vorbis_quality) self.pipeline.add(self.vorbisenc) # WebMmux & filesink diff --git a/mediagoblin/static/css/audio.css b/mediagoblin/static/css/audio.css index 387278ec..e007a0e1 100644 --- a/mediagoblin/static/css/audio.css +++ b/mediagoblin/static/css/audio.css @@ -80,5 +80,5 @@ transition: opacity .1s ease-in-out; } .audio-spectrogram:hover .audio-volume { - opacity: 1; + opacity: 0.7; } diff --git a/mediagoblin/static/js/audio.js b/mediagoblin/static/js/audio.js index f50908a1..217a2160 100644 --- a/mediagoblin/static/js/audio.js +++ b/mediagoblin/static/js/audio.js @@ -210,8 +210,14 @@ var audioPlayer = new Object(); $('<div class="seekbar"></div>').appendTo(im.parent()); $('<div class="audio-control-play-pause paused">▶</div>').appendTo(im.parent()); $('<div class="audio-currentTime">00:00</div>').appendTo(im.parent()); - $('<input placeholder="Range input not supported" class="audio-volume"' - +'type="range" min="0" max="1" step="0.01" />').appendTo(im.parent()); + if (navigator && /Firefox/.test(navigator.userAgent)) { + $('<p class="message_warning">Sorry, Firefox does not support the ' + + 'range input type, you won\'t be able to change the volume</p>') + .appendTo(im.parent().parent()); + } else { + $('<input type="range" class="audio-volume"' + +'value="1" min="0" max="1" step="0.001" />').appendTo(im.parent()); + } $('.audio-spectrogram').trigger('attachedControls'); }; })(audioPlayer); |