def _check_cmd(self): if not self.cmd: raise StreamError("`cmd' attribute not set") cmd = compat_which(self.cmd) if not cmd: raise StreamError("Unable to find `{0}' command".format(self.cmd)) return cmd
def open(self): process = self.spawn(self.parameters, self.arguments) # Wait 0.5 seconds to see if program exited prematurely time.sleep(0.5) if not process.poll() is None: if hasattr(self.stderr, "name"): raise StreamError(("Error while executing subprocess, " "error output logged to: {0}").format(self.stderr.name)) else: raise StreamError("Error while executing subprocess") return StreamProcessIO(self.session, process, process.stdout, timeout=self.timeout)
def spawn(self, parameters=None, arguments=None, stderr=None, timeout=None, short_option_prefix="-", long_option_prefix="--"): """ Spawn the process defined in `cmd` parameters is converted to options the short and long option prefixes if a list is given as the value, the parameter is repeated with each value If timeout is set the spawn will block until the process returns or the timeout expires. :param parameters: optional parameters :param arguments: positional arguments :param stderr: where to redirect stderr to :param timeout: timeout for short lived process :param long_option_prefix: option prefix, default - :param short_option_prefix: long option prefix, default -- :return: spawned process """ stderr = stderr or self.stderr cmd = self.bake(self._check_cmd(), parameters, arguments, short_option_prefix, long_option_prefix) self.logger.debug("Spawning command: {0}", subprocess.list2cmdline(cmd)) try: process = subprocess.Popen(cmd, stderr=stderr, stdout=subprocess.PIPE) except (OSError, IOError) as err: raise StreamError("Failed to start process: {0} ({1})".format( self._check_cmd(), str(err))) if timeout: elapsed = 0 while elapsed < timeout and not process.poll(): time.sleep(0.25) elapsed += 0.25 # kill after the timeout has expired and the process still hasn't ended if not process.poll(): try: self.logger.debug( "Process timeout expired ({0}s), killing process". format(timeout)) process.kill() except Exception: pass process.wait() return process
def open(self): if self.session.options.get("rtmp-proxy"): if not self._supports_param("socks"): raise StreamError( "Installed rtmpdump does not support --socks argument") self.parameters["socks"] = self.session.options.get("rtmp-proxy") if "jtv" in self.parameters and not self._supports_param("jtv"): raise StreamError( "Installed rtmpdump does not support --jtv argument") if "weeb" in self.parameters and not self._supports_param("weeb"): raise StreamError( "Installed rtmpdump does not support --weeb argument") if self.redirect: self._check_redirect() self.parameters["flv"] = "-" return StreamProcess.open(self)
def _supports_param(self, param, timeout=5.0): try: rtmpdump = self.spawn(dict(help=True), timeout=timeout, stderr=subprocess.PIPE) except StreamError as err: raise StreamError( "Error while checking rtmpdump compatibility: {0}".format( err.message)) for line in rtmpdump.stderr.readlines(): m = re.match(r"^--(\w+)", str(line, "ascii")) if not m: continue if m.group(1) == param: return True return False
def _create_video_clip(self, chunks, start_offset, stop_offset): playlist_duration = stop_offset - start_offset playlist_offset = 0 playlist_streams = [] playlist_tags = [] for chunk in chunks: chunk_url = chunk["url"] chunk_length = chunk["length"] chunk_start = playlist_offset chunk_stop = chunk_start + chunk_length chunk_stream = HTTPStream(self.session, chunk_url) if chunk_start <= start_offset <= chunk_stop: try: headers = extract_flv_header_tags(chunk_stream) except IOError as err: raise StreamError("Error while parsing FLV: {0}", err) if not headers.metadata: raise StreamError( "Missing metadata tag in the first chunk") metadata = headers.metadata.data.value keyframes = metadata.get("keyframes") if not keyframes: if chunk["upkeep"] == "fail": raise StreamError( "Unable to seek into muted chunk, try another timestamp" ) else: raise StreamError( "Missing keyframes info in the first chunk") keyframe_offset = None keyframe_offsets = keyframes.get("filepositions") keyframe_times = [ playlist_offset + t for t in keyframes.get("times") ] for time, offset in zip(keyframe_times, keyframe_offsets): if time > start_offset: break keyframe_offset = offset if keyframe_offset is None: raise StreamError("Unable to find a keyframe to seek to " "in the first chunk") chunk_headers = dict( Range="bytes={0}-".format(int(keyframe_offset))) chunk_stream = HTTPStream(self.session, chunk_url, headers=chunk_headers) playlist_streams.append(chunk_stream) for tag in headers: playlist_tags.append(tag) elif start_offset <= chunk_start < stop_offset: playlist_streams.append(chunk_stream) playlist_offset += chunk_length return FLVPlaylist(self.session, playlist_streams, tags=playlist_tags, duration=playlist_duration)