Added option to skip transcoding
authorJoar Wandborg <joar@wandborg.se>
Tue, 22 Jan 2013 21:44:19 +0000 (22:44 +0100)
committerJoar Wandborg <joar@wandborg.se>
Tue, 22 Jan 2013 22:03:14 +0000 (23:03 +0100)
- If the video input matches the configurable rules, just copy it to the
  output without transcoding it.

mediagoblin/config_spec.ini
mediagoblin/media_types/video/processing.py
mediagoblin/media_types/video/transcoders.py
mediagoblin/media_types/video/util.py [new file with mode: 0644]

index bee67d4698b2d68f3570fd9b22359fccb814a27c..712d087e56459fd3adbb4d67562e8cfa0aa5bd9d 100644 (file)
@@ -97,6 +97,12 @@ vp8_quality = integer(default=8)
 # Range: -0.1..1
 vorbis_quality = float(default=0.3)
 
+[[skip_transcode]]
+mime_types = string_list(default=list("video/webm"))
+container_formats = string_list(default=list("Matroska"))
+video_codecs = string_list(default=list("VP8 video"))
+audio_codecs = string_list(default=list("Vorbis"))
+dimensions_match = boolean(default=True)
 
 [media_type:mediagoblin.media_types.audio]
 keep_original = boolean(default=True)
index 4c9f01319edff3ec688b3a8e095b0f39145e826e..53fe1a73ad9e879be4b959d5fbda891453ff2275 100644 (file)
@@ -24,6 +24,8 @@ from mediagoblin.processing import \
 from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
 
 from . import transcoders
+from .util import skip_transcode
+
 
 _log = logging.getLogger(__name__)
 _log.setLevel(logging.DEBUG)
@@ -80,24 +82,43 @@ def process_video(entry, workbench=None):
     with tmp_dst:
         # Transcode queued file to a VP8/vorbis file that fits in a 640x640 square
         progress_callback = ProgressCallback(entry)
-        transcoder = transcoders.VideoTranscoder()
-        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'],
-                progress_callback=progress_callback)
 
-    # Push transcoded video to public storage
-    _log.debug('Saving medium...')
-    mgg.public_store.copy_local_to_storage(tmp_dst.name, medium_filepath)
-    _log.debug('Saved medium')
+        dimensions = (
+            mgg.global_config['media:medium']['max_width'],
+            mgg.global_config['media:medium']['max_height'])
+
+        metadata = transcoders.VideoTranscoder().discover(queued_filename)
+
+        if skip_transcode(metadata):
+            _log.debug('Skipping transcoding')
+            # Just push the submitted file to the tmp_dst
+            open(tmp_dst.name, 'wb').write(open(queued_filename, 'rb').read())
+
+            dst_dimensions = metadata['videowidth'], metadata['videoheight']
+        else:
+            transcoder = transcoders.VideoTranscoder()
+
+            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'],
+                    progress_callback=progress_callback,
+                    dimensions=dimensions)
+
+            dst_dimensions = transcoder.dst_data.videowidth,\
+                    transcoder.dst_data.videoheight
+
+        # Push transcoded video to public storage
+        _log.debug('Saving medium...')
+        mgg.public_store.copy_local_to_storage(tmp_dst.name, medium_filepath)
+        _log.debug('Saved medium')
 
-    entry.media_files['webm_640'] = medium_filepath
+        entry.media_files['webm_640'] = medium_filepath
 
-    # Save the width and height of the transcoded video
-    entry.media_data_init(
-        width=transcoder.dst_data.videowidth,
-        height=transcoder.dst_data.videoheight)
+        # Save the width and height of the transcoded video
+        entry.media_data_init(
+            width=dst_dimensions[0],
+            height=dst_dimensions[1])
 
     # Temporary file for the video thumbnail (cleaned up with workbench)
     tmp_thumb = NamedTemporaryFile(dir=workbench.dir, suffix='.jpg', delete=False)
@@ -109,10 +130,10 @@ def process_video(entry, workbench=None):
                 tmp_thumb.name,
                 180)
 
-    # Push the thumbnail to public storage
-    _log.debug('Saving thumbnail...')
-    mgg.public_store.copy_local_to_storage(tmp_thumb.name, thumbnail_filepath)
-    entry.media_files['thumb'] = thumbnail_filepath
+        # Push the thumbnail to public storage
+        _log.debug('Saving thumbnail...')
+        mgg.public_store.copy_local_to_storage(tmp_thumb.name, thumbnail_filepath)
+        entry.media_files['thumb'] = thumbnail_filepath
 
     if video_config['keep_original']:
         # Push original file to public storage
index 152de288d2385a123d32f8a8acd5b37b03ff5493..8aa7121f5d0c7fa23b0671c9cfacdadda7d251da 100644 (file)
@@ -673,6 +673,7 @@ class VideoTranscoder:
         self._setup()
         self._run()
 
+    # XXX: This could be a static method.
     def discover(self, src):
         '''
         Discover properties about a media file
@@ -793,7 +794,8 @@ class VideoTranscoder:
         self.audioconvert = gst.element_factory_make('audioconvert', 'audioconvert')
         self.pipeline.add(self.audioconvert)
 
-        self.audiocapsfilter = gst.element_factory_make('capsfilter', 'audiocapsfilter')
+        self.audiocapsfilter = gst.element_factory_make('capsfilter',
+                                                        'audiocapsfilter')
         audiocaps = ['audio/x-raw-float']
         self.audiocapsfilter.set_property(
             'caps',
diff --git a/mediagoblin/media_types/video/util.py b/mediagoblin/media_types/video/util.py
new file mode 100644 (file)
index 0000000..93f098f
--- /dev/null
@@ -0,0 +1,60 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+import logging
+
+from mediagoblin import mg_globals as mgg
+
+_log = logging.getLogger(__name__)
+
+
+def skip_transcode(metadata):
+    '''
+    Checks video metadata against configuration values for skip_transcode.
+
+    Returns True if the video matches the requirements in the configuration.
+    '''
+    config = mgg.global_config['media_type:mediagoblin.media_types.video']\
+            ['skip_transcode']
+
+    medium_config = mgg.global_config['media:medium']
+
+    _log.debug('skip_transcode config: {0}'.format(config))
+
+    if config['mime_types'] and metadata.get('mimetype'):
+        if not metadata['mimetype'] in config['mime_types']:
+            return False
+
+    if config['container_formats'] and metadata['tags'].get('audio-codec'):
+        if not metadata['tags']['container-format'] in config['container_formats']:
+            return False
+
+    if config['video_codecs'] and metadata['tags'].get('audio-codec'):
+        if not metadata['tags']['video-codec'] in config['video_codecs']:
+            return False
+
+    if config['audio_codecs'] and metadata['tags'].get('audio-codec'):
+        if not metadata['tags']['audio-codec'] in config['audio_codecs']:
+            return False
+
+    if config['dimensions_match']:
+        if not metadata['videoheight'] <= medium_config['max_height']:
+            return False
+        if not metadata['videowidth'] <= medium_config['max_width']:
+            return False
+
+    return True
+