def _loop(self): executable = self.get_executable() args = self.get_parameters() self._start_logging(executable, args) args.insert(0, executable) logging.debug("Conversion: (%s)", " ".join(args)) kwargs = {"bufsize": 1, "stdout": subprocess.PIPE, "stderr": subprocess.STDOUT, "stdin": subprocess.PIPE, "startupinfo": util.no_console_startupinfo()} if os.name != "nt": kwargs["close_fds"] = True try: self.process_handle = subprocess.Popen(args, **kwargs) self.process_output(line_reader(self.process_handle.stdout)) self.process_handle.wait() except OSError, ose: if ose.errno == errno.ENOENT: self.error = _("%(program)s does not exist.", {"program": self.get_executable()}) else: logging.exception("Exception in conversion loop: %s %s", args, kwargs)
def run_movie_data_program(self, command_line, env): start_time = time.time() # create tempfiles to catch output for the movie data program. Using # a pipe fails if the movie data program outputs enough to fill up the # buffers (see #17059) movie_data_stdout = tempfile.TemporaryFile() movie_data_stderr = tempfile.TemporaryFile() pipe = subprocess.Popen(command_line, stdout=movie_data_stdout, stdin=subprocess.PIPE, stderr=movie_data_stderr, env=env, startupinfo=util.no_console_startupinfo()) # close stdin since we won't write to it. pipe.stdin.close() while pipe.poll() is None and not self.in_shutdown: time.sleep(SLEEP_DELAY) if time.time() - start_time > MOVIE_DATA_UTIL_TIMEOUT: logging.warning("Movie data process hung, killing it") self.kill_process(pipe) raise ProcessHung if self.in_shutdown: if pipe.poll() is None: logging.warning("Movie data process running after shutdown, " "killing it") self.kill_process(pipe) raise Shutdown # FIXME: should we do anything with stderr? movie_data_stdout.seek(0) return movie_data_stdout.read()
def _start_subprocess(self): cmd_line, env = miro_helper_program_info() kwargs = { "stdout": subprocess.PIPE, "stdin": subprocess.PIPE, "startupinfo": util.no_console_startupinfo(), "env": env, } if _on_windows(): # normally we just clone stderr for the subprocess, but on windows # this doesn't work. So we use a pipe that we immediately close kwargs["stderr"] = subprocess.PIPE else: kwargs["stderr"] = None kwargs["close_fds"] = True process = subprocess.Popen(cmd_line, **kwargs) if _on_windows(): process.stderr.close() return process
def run_movie_data_program(self, command_line, env): start_time = time.time() pipe = subprocess.Popen(command_line, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, env=env, startupinfo=util.no_console_startupinfo()) while pipe.poll() is None and not self.in_shutdown: time.sleep(SLEEP_DELAY) if time.time() - start_time > MOVIE_DATA_UTIL_TIMEOUT: logging.info("Movie data process hung, killing it") self.kill_process(pipe.pid) return '' if self.in_shutdown: if pipe.poll() is None: logging.info("Movie data process running after shutdown, " "killing it") self.kill_process(pipe.pid) return '' return pipe.stdout.read()
def transcode(self): rc = True try: ffmpeg_exe = get_ffmpeg_executable_path() kwargs = { "stdin": open(os.devnull, 'rb'), "stdout": subprocess.PIPE, "stderr": open(os.devnull, 'wb'), "startupinfo": util.no_console_startupinfo() } if os.name != "nt": kwargs["close_fds"] = True args = [ffmpeg_exe, "-i", self.media_file] if self.time_offset: logging.debug('transcode: start job @ %d' % self.time_offset) args += TranscodeObject.time_offset_args + [ str(self.time_offset) ] video_needs_trancode = False if self.has_video: logging.debug('Video codec: %s', self.video_codec) logging.debug('Video size: %s', self.video_size) if video_can_copy(self.video_codec, self.video_size): args += get_transcode_video_copy_options() else: args += get_transcode_video_options() video_needs_transcode = True if self.has_audio: logging.debug('Audio codec: %s', self.audio_codec) logging.debug('Audio sample rate: %s', self.audio_sample_rate) if (valid_av_combo(self.video_codec, self.audio_codec) and audio_can_copy(self.audio_codec, self.audio_sample_rate)): args += get_transcode_audio_copy_options() else: args += get_transcode_audio_options() else: raise ValueError('no video or audio stream present') args += TranscodeObject.output_args logging.debug('Running command %s' % ' '.join(args)) self.ffmpeg_handle = subprocess.Popen(args, **kwargs) segmenter_exe = get_segmenter_executable_path() args = [segmenter_exe] address, port = self.sink.server_address args += TranscodeObject.segmenter_args + [str(port)] kwargs = { "stdout": open(os.devnull, 'rb'), "stdin": self.ffmpeg_handle.stdout, "stderr": open(os.devnull, 'wb'), "startupinfo": util.no_console_startupinfo() } # XXX Can't use this - need to pass on the child fds #if os.name != "nt": # kwargs["close_fds"] = True logging.debug('Running command %s' % ' '.join(args)) self.segmenter_handle = subprocess.Popen(args, **kwargs) self.sink_thread = threading.Thread(target=thread_body, args=[self.segmenter_consumer], name="Segmenter Consumer") self.sink_thread.daemon = True self.sink_thread.start() except StandardError: (typ, value, tb) = sys.exc_info() logging.error('ERROR: %s %s' % (str(typ), str(value))) rc = False self.transcode_gate.set() return rc
def needs_transcode(media_file): """needs_transcode() Returns (False, None) if no need to transcode. Returns (True, info) if there is a need to transcode. where info is (duration, has_audio, has_video) The duration is transmitted for transcoding purposes because we need to build a m3u8 playlist and what we get out of the Miro database may be unreliable (does not exist). May throw exception if ffmpeg not found. Remember to catch.""" ffmpeg_exe = get_ffmpeg_executable_path() kwargs = { "stdout": subprocess.PIPE, "stderr": subprocess.PIPE, "stdin": subprocess.PIPE, "startupinfo": util.no_console_startupinfo() } if os.name != "nt": kwargs["close_fds"] = True args = [ffmpeg_exe, "-i", media_file] handle = subprocess.Popen(args, **kwargs) # XXX unbounded read here but should be okay, ffmpeg output is finite. # note that we need to read from stderr, since that's what ffmpeg spits # out. text = handle.stderr.read() # Initial determination based on the file type - need to drill down # to see if the resolution, etc are within parameters. if container_regex.search(text): transcode = False else: transcode = True # " Duration: XX:XX:XX.XX, ..." match = duration_regex.search(text) start, end = match.span() duration_start = text[end:] duration = duration_start[:duration_start.index(',')] # Convert to seconds. We can't handle fractions of a second so # skip over those bits. hours, minutes, seconds = duration.split(':') # Strip the fractional seconds. Always round up, then we won't miss # any data. seconds = int(float(seconds) + 0.5) seconds += int(minutes) * 60 seconds += int(hours) * 3600 has_audio = has_audio_regex.search(text) has_video = has_video_regex.search(text) vcodec = acodec = size = sample_rate = None vcopy = acopy = False if has_video: vcodec, size = get_video_parameters(has_video) vcopy = video_can_copy(vcodec, size) if not vcopy: transcode = False if has_audio: acodec, sample_rate = get_audio_parameters(has_audio) acopy = audio_can_copy(acodec, sample_rate) if not acopy: transcode = False return (transcode, (seconds, has_audio, acodec, sample_rate, has_video, vcodec, size))
def transcode(self): rc = True try: ffmpeg_exe = get_ffmpeg_executable_path() kwargs = { "stdin": open(os.devnull, "rb"), "stdout": subprocess.PIPE, "stderr": open(os.devnull, "wb"), "startupinfo": util.no_console_startupinfo(), } if os.name != "nt": kwargs["close_fds"] = True args = [ffmpeg_exe, "-i", self.media_file] if self.time_offset: logging.debug("transcode: start job @ %d" % self.time_offset) args += TranscodeObject.time_offset_args + [str(self.time_offset)] video_needs_trancode = False if self.has_video: logging.debug("Video codec: %s", self.video_codec) logging.debug("Video size: %s", self.video_size) if video_can_copy(self.video_codec, self.video_size): args += get_transcode_video_copy_options() else: args += get_transcode_video_options() video_needs_transcode = True if self.has_audio: logging.debug("Audio codec: %s", self.audio_codec) logging.debug("Audio sample rate: %s", self.audio_sample_rate) if valid_av_combo(self.video_codec, self.audio_codec) and audio_can_copy( self.audio_codec, self.audio_sample_rate ): args += get_transcode_audio_copy_options() else: args += get_transcode_audio_options() else: raise ValueError("no video or audio stream present") args += TranscodeObject.output_args logging.debug("Running command %s" % " ".join(args)) self.ffmpeg_handle = subprocess.Popen(args, **kwargs) segmenter_exe = get_segmenter_executable_path() args = [segmenter_exe] address, port = self.sink.server_address args += TranscodeObject.segmenter_args + [str(port)] kwargs = { "stdout": open(os.devnull, "rb"), "stdin": self.ffmpeg_handle.stdout, "stderr": open(os.devnull, "wb"), "startupinfo": util.no_console_startupinfo(), } # XXX Can't use this - need to pass on the child fds # if os.name != "nt": # kwargs["close_fds"] = True logging.debug("Running command %s" % " ".join(args)) self.segmenter_handle = subprocess.Popen(args, **kwargs) self.sink_thread = threading.Thread( target=thread_body, args=[self.segmenter_consumer], name="Segmenter Consumer" ) self.sink_thread.daemon = True self.sink_thread.start() except StandardError: (typ, value, tb) = sys.exc_info() logging.error("ERROR: %s %s" % (str(typ), str(value))) rc = False self.transcode_gate.set() return rc
def needs_transcode(media_file): """needs_transcode() Returns (False, None) if no need to transcode. Returns (True, info) if there is a need to transcode. where info is (duration, has_audio, has_video) The duration is transmitted for transcoding purposes because we need to build a m3u8 playlist and what we get out of the Miro database may be unreliable (does not exist). May throw exception if ffmpeg not found. Remember to catch.""" ffmpeg_exe = get_ffmpeg_executable_path() kwargs = { "stdout": subprocess.PIPE, "stderr": subprocess.PIPE, "stdin": subprocess.PIPE, "startupinfo": util.no_console_startupinfo(), } if os.name != "nt": kwargs["close_fds"] = True args = [ffmpeg_exe, "-i", media_file] handle = subprocess.Popen(args, **kwargs) # XXX unbounded read here but should be okay, ffmpeg output is finite. # note that we need to read from stderr, since that's what ffmpeg spits # out. text = handle.stderr.read() # Initial determination based on the file type - need to drill down # to see if the resolution, etc are within parameters. if container_regex.search(text): transcode = False else: transcode = True # " Duration: XX:XX:XX.XX, ..." match = duration_regex.search(text) start, end = match.span() duration_start = text[end:] duration = duration_start[: duration_start.index(",")] # Convert to seconds. We can't handle fractions of a second so # skip over those bits. hours, minutes, seconds = duration.split(":") # Strip the fractional seconds. Always round up, then we won't miss # any data. seconds = int(float(seconds) + 0.5) seconds += int(minutes) * 60 seconds += int(hours) * 3600 has_audio = has_audio_regex.search(text) has_video = has_video_regex.search(text) vcodec = acodec = size = sample_rate = None vcopy = acopy = False if has_video: vcodec, size = get_video_parameters(has_video) vcopy = video_can_copy(vcodec, size) if not vcopy: transcode = False if has_audio: acodec, sample_rate = get_audio_parameters(has_audio) acopy = audio_can_copy(acodec, sample_rate) if not acopy: transcode = False return (transcode, (seconds, has_audio, acodec, sample_rate, has_video, vcodec, size))