#!/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
+import math
class FFmpegTranscoder(base.BaseTranscoder):
"""
prog_bin = "ffmpeg"
- log_file = 'log/ffmpeg.log'
+ log_file = 'log/FFmpegTranscoder.log'
containers = {
"avi": "avi",
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
+ args = self.prog_bin + ' -y -i "' + self.input_file + '" -f ' + container
# Audio
if a_codec != None:
# 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,
# WRITE handler for logging.
log = open(self.log_file, 'w')
+ log.write(args + '\n')
while True:
line = pipe.readline()
break
log.write(line)
- if p.poll() > 0:
- raise cis_exceptions.TranscodingException
+ exit_code = p.wait()
+ if exit_code > 0:
+ raise cis_exceptions.TranscodingException( \
+ 'FFmpeg exited with code ' + str(exit_code) + '.')
log.close()
+
+ return self.output_file
+
+
+class FFmpegThumbExtractor(base.BaseThumbExtractor):
+ """
+ FFmpeg CLI API for video thumbnail extraction.
+ """
+
+ prog_bin = "ffmpeg"
+
+ 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):
+ return FFprobeAVInfo.get_video_duration(self.input_file)
+
+
+class FFprobeAVInfo(base.BaseAVInfo):
+
+ prog_bin = "ffprobe"
+
+ log_file = 'log/FFprobeAVInfo.log'
+
+ @staticmethod
+ def get_video_duration(input_file, formated=False):
+ args = FFprobeAVInfo.prog_bin + ' -show_format "' \
+ + input_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(FFprobeAVInfo.log_file, 'w')
+ log.write(args + '\n')
+
+ # Parse ffprobe's output.
+ while True:
+ line = pipe.readline()
+ if len(line) == 0:
+ break
+ log.write(line)
+
+ # Search for the line which contains duration information.
+ m = re.match(r"duration=([\d\.]+)", line)
+ if m is not None:
+ seconds = float(m.group(1))
+ if not formated:
+ return seconds
+ else:
+ seconds = math.floor(seconds)
+ minutes = math.floor(seconds / 60)
+ seconds = seconds % 60
+ if minutes >= 60:
+ hours = math.floor(minutes / 60)
+ minutes = minutes % 60
+
+ return "%02d:%02d:%02d" % (hours, minutes, seconds)
+ else:
+ return "%02d:%02d" % (minutes, seconds)
+
+ exit_code = p.wait()
+ if exit_code > 0:
+ raise cis_exceptions.AVInfoException( \
+ 'ffprobe exited with code ' + str(exit_code) + '.')