def open_stream(stream): """Opens a stream and reads 8192 bytes from it. This is useful to check if a stream actually has data before opening the output. """ global stream_fd # Attempts to open the stream try: stream_fd = stream.open() except StreamError as err: raise StreamError("Could not open stream: {0}".format(err)) # Read 8192 bytes before proceeding to check for errors. # This is to avoid opening the output unnecessarily. try: console.logger.debug("Pre-buffering 8192 bytes") prebuffer = stream_fd.read(8192) except IOError as err: raise StreamError("Failed to read data from stream: {0}".format(err)) if not prebuffer: raise StreamError("No data returned from stream") return stream_fd, prebuffer
def __init__(self, session, *streams, **options): if not self.is_usable(session): raise StreamError("cannot use FFMPEG") self.session = session self.process = None self.logger = session.logger.new_module("stream.mp4mux-ffmpeg") self.streams = streams self.pipes = [ NamedPipe("ffmpeg-{0}-{1}".format(os.getpid(), random.randint(0, 1000))) for _ in self.streams ] self.pipe_threads = [ threading.Thread(target=self.copy_to_pipe, args=(self, stream, np)) for stream, np in zip(self.streams, self.pipes) ] ofmt = options.pop("format", "matroska") outpath = options.pop("outpath", "pipe:1") videocodec = session.options.get( "ffmpeg-video-transcode") or options.pop("vcodec", "copy") audiocodec = session.options.get( "ffmpeg-audio-transcode") or options.pop("acodec", "copy") metadata = options.pop("metadata", {}) maps = options.pop("maps", []) self._cmd = [self.command(session), '-nostats', '-y'] for np in self.pipes: self._cmd.extend(["-i", np.path]) self._cmd.extend(['-c:v', videocodec]) self._cmd.extend(['-c:a', audiocodec]) for m in maps: self._cmd.extend(["-map", str(m)]) for stream, data in metadata.items(): for datum in data: self._cmd.extend(["-metadata:{0}".format(stream), datum]) self._cmd.extend(['-f', ofmt, outpath]) self.logger.debug("ffmpeg command: {0}".format(' '.join(self._cmd))) self.close_errorlog = False if session.options.get("ffmpeg-verbose"): self.errorlog = sys.stderr elif session.options.get("ffmpeg-verbose-path"): self.errorlog = open(session.options.get("ffmpeg-verbose-path"), "w") self.close_errorlog = True else: self.errorlog = compat_devnull()
def url(self): # If the watch timeout has passed then refresh the playlist from the API if int(time.time()) >= self.watch_timeout: for stream in self._get_stream_data(): if stream["quality"] == self.quality: self.watch_timeout = int( time.time()) + stream["watch-timeout"] self._url = stream["url"] return self._url raise StreamError("cannot refresh FilmOn HLS Stream playlist") else: return self._url