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, 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 api_exceptions.TranscodingException( \
95 'FFmpeg exited with code ' + str(exit_code) + '.')
100 class FFmpegThumbExtractor(base.BaseThumbExtractor):
102 FFmpeg CLI API for video thumbnail extraction.
107 log_file = 'log/FFmpegThumbExtractor.log'
109 def extract_thumb(self, seek_pos, resolution="120x90", index=0):
110 output_file = self.get_output_file_name(index)
112 args = self.prog_bin + ' -y -i "' + self.input_file \
113 + '" -f rawvideo -vcodec mjpeg' + (' -ss ' + str(seek_pos)) \
114 + " -vframes 1 -an -s " + resolution + ' "' \
117 # READ handler for process's output.
118 p = subprocess.Popen(args, shell=True,
119 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
122 # WRITE handler for logging.
123 log = open(self.log_file, 'w')
124 log.write(args + '\n')
127 line = pipe.readline()
134 raise api_exceptions.ThumbExtractionException( \
135 'FFmpeg exited with code ' + str(exit_code) + '.')
137 # FFmpeg bug: when no key frame is found from seek_pos to the
138 # end of file an empty image file is created.
139 if os.path.getsize(output_file) == 0L:
140 os.unlink(output_file)
141 raise api_exceptions.ThumbExtractionException( \
142 'FFmpeg created an empty file.')
144 def get_video_duration(self):
145 return FFmpegAVInfo.get_video_duration(self.input_file)
148 class FFmpegAVInfo(base.BaseAVInfo):
153 def get_video_duration(input_file, formated=False):
154 args = FFmpegAVInfo.prog_bin + ' -show_format "' \
157 # READ handler for process's output.
158 p = subprocess.Popen(args, shell=True,
159 stdout=subprocess.PIPE, stderr=open(os.devnull, 'w'))
162 # Parse ffprobe's output.
164 line = pipe.readline()
168 # Search for the line which contains duration information.
169 m = re.match(r"duration=([\d\.]+)", line)
171 seconds = float(m.group(1))
175 seconds = math.floor(seconds)
176 minutes = math.floor(seconds / 60)
177 seconds = seconds % 60
179 hours = math.floor(minutes / 60)
180 minutes = minutes % 60
182 return "%02d:%02d:%02d" % (hours, minutes, seconds)
184 return "%02d:%02d" % (minutes, seconds)
188 raise api_exceptions.AVInfoException( \
189 'ffprobe exited with code ' + str(exit_code) + '.')