5 Classes derived from BaseTranscoder and BaseThumbExtractor for transcoding of
6 videos and thumbnail extraction from videos using FFmpeg CLI program.
16 class FFmpegTranscoder(base.BaseTranscoder):
18 FFmpeg CLI API for video transcoding.
23 log_file = 'log/FFmpegTranscoder.log'
39 "theora": "libtheora",
43 def _transcode(self, container, extension=None, a_codec=None, v_codec=None,
44 a_bitrate=None, a_samplingrate=None, a_channels=None,
45 v_bitrate=None, v_framerate=None, v_resolution=None, v_dar=None):
47 args = self.prog_bin + ' -y -i "' + self.input_file + '" -f ' + container
51 args += ' -acodec ' + a_codec
53 args += ' -ab ' + str(a_bitrate)
54 if a_samplingrate != None:
55 args += ' -ar ' + str(a_samplingrate)
56 if a_channels != None:
57 args += ' -ac ' + str(a_channels)
61 args += ' -vcodec ' + v_codec
62 # Video codec specific options.
63 if v_codec == 'libx264':
64 args += ' -vpre normal'
66 args += ' -b ' + str(v_bitrate)
67 if v_framerate != None:
68 args += ' -r ' + str(v_framerate)
69 if v_resolution != None:
70 args += ' -s ' + v_resolution
72 args += ' -aspect ' + v_dar
75 args += ' "' + self.output_file + '"'
77 # READ handler for process's output.
78 p = subprocess.Popen(args, shell=True,
79 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
82 # WRITE handler for logging.
83 log = open(self.log_file, 'w')
84 log.write(args + '\n')
87 line = pipe.readline()
94 raise cis_exceptions.TranscodingException( \
95 'FFmpeg exited with code ' + str(exit_code) + '.')
99 return self.output_file
102 class FFmpegThumbExtractor(base.BaseThumbExtractor):
104 FFmpeg CLI API for video thumbnail extraction.
109 log_file = 'log/FFmpegThumbExtractor.log'
111 def extract_thumb(self, seek_pos, resolution="120x90", index=0):
112 output_file = self.get_output_file_name(index)
114 args = self.prog_bin + ' -y -i "' + self.input_file \
115 + '" -f rawvideo -vcodec mjpeg' + (' -ss ' + str(seek_pos)) \
116 + " -vframes 1 -an -s " + resolution + ' "' \
119 # READ handler for process's output.
120 p = subprocess.Popen(args, shell=True,
121 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
124 # WRITE handler for logging.
125 log = open(self.log_file, 'w')
126 log.write(args + '\n')
129 line = pipe.readline()
136 raise cis_exceptions.ThumbExtractionException( \
137 'FFmpeg exited with code ' + str(exit_code) + '.')
139 # FFmpeg bug: when no key frame is found from seek_pos to the
140 # end of file an empty image file is created.
141 if os.path.getsize(output_file) == 0L:
142 os.unlink(output_file)
143 raise cis_exceptions.ThumbExtractionException( \
144 'FFmpeg created an empty file.')
146 def get_video_duration(self):
147 return FFprobeAVInfo.get_video_duration(self.input_file)
150 class FFprobeAVInfo(base.BaseAVInfo):
154 log_file = 'log/FFprobeAVInfo.log'
157 def get_video_duration(input_file, formated=False):
158 args = FFprobeAVInfo.prog_bin + ' -show_format "' \
161 # READ handler for process's output.
162 p = subprocess.Popen(args, shell=True,
163 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
166 # WRITE handler for logging.
167 log = open(FFprobeAVInfo.log_file, 'w')
168 log.write(args + '\n')
170 # Parse ffprobe's output.
172 line = pipe.readline()
177 # Search for the line which contains duration information.
178 m = re.match(r"duration=([\d\.]+)", line)
180 seconds = float(m.group(1))
184 seconds = math.floor(seconds)
185 minutes = math.floor(seconds / 60)
186 seconds = seconds % 60
188 hours = math.floor(minutes / 60)
189 minutes = minutes % 60
191 return "%02d:%02d:%02d" % (hours, minutes, seconds)
193 return "%02d:%02d" % (minutes, seconds)
197 raise cis_exceptions.AVInfoException( \
198 'ffprobe exited with code ' + str(exit_code) + '.')