--- /dev/null
+#!/usr/bin/env python
+
+
+"""
+Classes derived from BaseTranscoder and BaseThumbExtractor for transcoding of
+videos and thumbnail extraction from videos using FFmpeg CLI program.
+"""
+
+import base
+import cis_exceptions
+import subprocess
+import re
+import os
+
+class FFmpegTranscoder(base.BaseTranscoder):
+ """
+ FFmpeg CLI API for video transcoding.
+ """
+
+ prog_bin = "ffmpeg"
+
+ log_file = 'log/FFmpegTranscoder.log'
+
+ containers = {
+ "avi": "avi",
+ "flv": "flv",
+ "mp4": "mp4",
+ "ogg": "ogg",
+ "webm": "webm",
+ "mpegts": "mpegts"
+ }
+ a_codecs = {
+ "mp3": "libmp3lame",
+ "vorbis": "libvorbis"
+ }
+ v_codecs = {
+ "h264": "libx264",
+ "theora": "libtheora",
+ "vp8": "libvpx"
+ }
+
+ def _transcode(self, container, a_codec=None, v_codec=None,
+ a_bitrate=None, a_samplingrate=None, a_channels=None,
+ v_bitrate=None, v_framerate=None, v_resolution=None, v_dar=None):
+
+ args = self.prog_bin + ' -y -i "' + self.input_file + '" -f ' + container
+
+ # Audio
+ if a_codec != None:
+ args += ' -acodec ' + a_codec
+ if a_bitrate != None:
+ args += ' -ab ' + str(a_bitrate)
+ if a_samplingrate != None:
+ args += ' -ar ' + str(a_samplingrate)
+ if a_channels != None:
+ args += ' -ac ' + str(a_channels)
+
+ # Video
+ if v_codec != None:
+ args += ' -vcodec ' + v_codec
+ # Video codec specific options.
+ if v_codec == 'libx264':
+ args += ' -vpre normal'
+ if v_bitrate != None:
+ args += ' -b ' + str(v_bitrate)
+ if v_framerate != None:
+ args += ' -r ' + str(v_framerate)
+ if v_resolution != None:
+ args += ' -s ' + v_resolution
+ if v_dar != None:
+ args += ' -aspect ' + v_dar
+
+ # Output file.
+ args += ' "' + self.output_file + '"'
+
+ # READ handler for process's output.
+ p = subprocess.Popen(args, shell=True,
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ pipe = p.stdout
+
+ # WRITE handler for logging.
+ log = open(self.log_file, 'w')
+ log.write(args + '\n')
+
+ while True:
+ line = pipe.readline()
+ if len(line) == 0:
+ break
+ log.write(line)
+
+ exit_code = p.wait()
+ if exit_code > 0:
+ raise cis_exceptions.TranscodingException( \
+ 'FFmpeg exited with code ' + str(exit_code) + '.')
+
+ log.close()
+
+
+class FFmpegThumbExtractor(base.BaseThumbExtractor):
+ """
+ FFmpeg CLI API for video thumbnail extraction.
+ """
+
+ prog_bin = "ffmpeg"
+ info_prog_bin = "ffprobe"
+
+ log_file = 'log/FFmpegThumbExtractor.log'
+
+ def extract_thumb(self, seek_pos, resolution="120x90", index=0):
+ output_file = self.get_output_file_name(index)
+
+ args = self.prog_bin + ' -y -i "' + self.input_file \
+ + '" -f rawvideo -vcodec mjpeg' + (' -ss ' + str(seek_pos)) \
+ + " -vframes 1 -an -s " + resolution + ' "' \
+ + output_file + '"'
+
+ # READ handler for process's output.
+ p = subprocess.Popen(args, shell=True,
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ pipe = p.stdout
+
+ # WRITE handler for logging.
+ log = open(self.log_file, 'w')
+ log.write(args + '\n')
+
+ while True:
+ line = pipe.readline()
+ if len(line) == 0:
+ break
+ log.write(line)
+
+ exit_code = p.wait()
+ if exit_code > 0:
+ raise cis_exceptions.ThumbExtractionException( \
+ 'FFmpeg exited with code ' + str(exit_code) + '.')
+
+ # FFmpeg bug: when no key frame is found from seek_pos to the
+ # end of file an empty image file is created.
+ if os.path.getsize(output_file) == 0L:
+ os.unlink(output_file)
+ raise cis_exceptions.ThumbExtractionException( \
+ 'FFmpeg created an empty file.')
+
+ def get_video_duration(self):
+ args = self.info_prog_bin + ' -show_format "' \
+ + self.input_file + '"'
+
+ # READ handler for process's output.
+ p = subprocess.Popen(args, shell=True,
+ stdout=subprocess.PIPE, stderr=open(os.devnull, 'w'))
+ pipe = p.stdout
+
+ # Parse ffprobe's output.
+ while True:
+ line = pipe.readline()
+ if len(line) == 0:
+ break
+
+ # Search for the line which contains duration information.
+ m = re.match(r"duration=([\d\.]+)", line)
+ if m is not None:
+ return float(m.group(1))
+
+ exit_code = p.wait()
+ if exit_code > 0:
+ raise cis_exceptions.ThumbExtractionException( \
+ 'FFmpeg exited with code ' + str(exit_code) + '.')
+
import cis_exceptions
import re
+import cis_util
+import random
class BaseTranscoder:
"""
self.prog_bin = prog_bin
if name is None:
- if input_file.find('/') is not -1:
- name = input_file[(input_file.rindex('/')+1):]
- else:
- name = input_file
- if name.find('.') is not -1:
- name = name[:name.rindex('.')]
+ name = cis_util.get_name(input_file)
self.name = name
raise cis_exceptions.NotImplementedException("Video Codec " + name)
return self.v_codecs[name]
+
+
+class BaseThumbExtractor:
+ """
+ Abstraction of the API class for the thumbnail extraction program.
+
+ Thumbnail extracted are in JPEG format.
+ """
+
+ prog_bin = None
+ input_file = None
+ dest_path = ''
+ name = None
+
+ def __init__(self, input_file, name=None, prog_bin=None):
+ self.input_file = input_file
+ if prog_bin is not None:
+ self.prog_bin = prog_bin
+
+ if name is None:
+ name = cis_util.get_name(input_file)
+
+ self.name = name
+
+ def extract_thumb(self, seek_pos, resolution="120x90", index=0):
+ """
+ Extracts a thumbnail from the video from a specified position
+ expressed in seconds (int/float).
+
+ index: an index appended to the image name in order to avoid
+ overwriting.
+ """
+ pass
+
+ def extract_random_thumb(self, resolution="120x90", index=0):
+ """
+ Extracts a thumbnail from the video from a random position.
+ """
+ duration = self.get_video_duration()
+ seek_pos = random.random() * duration
+ self.extract_thumb(seek_pos, resolution, index)
+
+ def extract_summary_thumbs(self, count, resolution="120x90"):
+ """
+ Extracts a series of thumbnails from a video by taking several
+ snapshots.
+
+ The snapshots are taken from equally spaced positions such that
+ `count` thumbs are extracted.
+ """
+ duration = self.get_video_duration()
+ interval = duration / (count + 1)
+
+ n_thumbs_extracted = 0
+ seek_pos = interval
+ for index in range (0, count):
+ thumb_extracted = True
+ try:
+ self.extract_thumb(seek_pos, resolution, index)
+ except cis_exceptions.ThumbExtractionException as e:
+ thumb_extracted = False
+
+ if thumb_extracted:
+ n_thumbs_extracted += 1
+
+ seek_pos += interval
+
+ return n_thumbs_extracted
+
+ def get_video_duration(self):
+ """
+ Returns the number of seconds of a video (int/float).
+ """
+ pass
+
+ def get_output_file_name(self, index):
+ """ Returns the name required as output file name based on index. """
+ output_file_name = self.dest_path + self.name \
+ + '_t' + ("%02d" % index) + '.jpg'
+ return output_file_name
+++ /dev/null
-#!/usr/bin/env python
-
-"""
-Classes derived from BaseTranscoder and BaseThumbExtractor for transcoding of
-videos and thumbnail extraction from videos using FFmpeg CLI program.
-"""
-
-import base
-import cis_exceptions
-import subprocess
-import os
-
-class FFmpegTranscoder(base.BaseTranscoder):
- """
- FFmpeg CLI API for video transcoding.
- """
-
- prog_bin = "ffmpeg"
-
- log_file = 'log/ffmpeg.log'
-
- containers = {
- "avi": "avi",
- "flv": "flv",
- "mp4": "mp4",
- "ogg": "ogg",
- "webm": "webm",
- "mpegts": "mpegts"
- }
- a_codecs = {
- "mp3": "libmp3lame",
- "vorbis": "libvorbis"
- }
- v_codecs = {
- "h264": "libx264",
- "theora": "libtheora",
- "vp8": "libvpx"
- }
-
- def _transcode(self, container, a_codec=None, v_codec=None,
- a_bitrate=None, a_samplingrate=None, a_channels=None,
- v_bitrate=None, v_framerate=None, v_resolution=None, v_dar=None):
-
- args = self.prog_bin + ' -i "' + self.input_file + '" -f ' + container
-
- # Audio
- if a_codec != None:
- args += ' -acodec ' + a_codec
- if a_bitrate != None:
- args += ' -ab ' + str(a_bitrate)
- if a_samplingrate != None:
- args += ' -ar ' + str(a_samplingrate)
- if a_channels != None:
- args += ' -ac ' + str(a_channels)
-
- # Video
- if v_codec != None:
- args += ' -vcodec ' + v_codec
- # Video codec specific options.
- if v_codec == 'libx264':
- args += ' -vpre normal'
- if v_bitrate != None:
- args += ' -b ' + str(v_bitrate)
- if v_framerate != None:
- args += ' -r ' + str(v_framerate)
- if v_resolution != None:
- args += ' -s ' + v_resolution
- if v_dar != None:
- args += ' -aspect ' + v_dar
-
- # Output file.
- args += ' "' + self.output_file + '"'
- try:
- os.unlink(self.output_file)
- except OSError:
- pass
-
- # READ handler for process's output.
- p = subprocess.Popen(args, shell=True,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- pipe = p.stdout
-
- # WRITE handler for logging.
- log = open(self.log_file, 'w')
-
- while True:
- line = pipe.readline()
- if len(line) == 0:
- break
- log.write(line)
-
- if p.poll() > 0:
- raise cis_exceptions.TranscodingException
-
- log.close()
class TranscodingException(Exception):
pass
+
+class ThumbExtractionException(Exception):
+ pass
--- /dev/null
+#!/usr/bin/env python
+
+"""
+Useful functions for CIS.
+"""
+
+def get_name(file_name):
+ if file_name.find('/') is not -1:
+ name = file_name[(file_name.rindex('/')+1):]
+ else:
+ name = file_name
+ if name.find('.') is not -1:
+ name = name[:name.rindex('.')]
+
+ return name
if __name__ == '__main__':
- transcoder = config.TRANSCODER_CLASS("../data/media/test.ogv")
- transcoder.transcode('ogg', "vorbis", "theora", a_bitrate="192k", a_samplingrate=44100, a_channels=2, v_bitrate="700k", v_framerate=25, v_resolution="800x600", v_dar="16:9")
+# transcoder = config.TRANSCODER_CLASS(sys.argv[1])
+# transcoder.transcode('webm', "vorbis", "vp8", a_bitrate="128k", a_samplingrate=22050, a_channels=2, v_bitrate="256k", v_framerate=15, v_resolution="320x240", v_dar="4:3")
+
+ thumb_extractor = config.THUMB_EXTRACTOR_CLASS(sys.argv[1])
+ #print thumb_extractor.get_video_duration()
+ #thumb_extractor.extract_random_thumb()
+ print thumb_extractor.extract_summary_thumbs(5)
#!/usr/bin/env python
# Make here all necessary imports required for API classes.
-from api import ffmpeg
+from api import avhandling
# External programs API classes.
-TRANSCODER_CLASS = ffmpeg.FFmpegTranscoder
-THUMB_EXTRACTER_CLASS = None # TODO
+TRANSCODER_CLASS = avhandling.FFmpegTranscoder
+THUMB_EXTRACTOR_CLASS = avhandling.FFmpegThumbExtractor
BT_CLIENT_CLASS = None # TODO
FILE_TRANSFERER_CLASS = None # TODO