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: log.debug("Pre-buffering 8192 bytes") prebuffer = stream_fd.read(8192) except OSError as err: stream_fd.close() raise StreamError("Failed to read data from stream: {0}".format(err)) if not prebuffer: stream_fd.close() raise StreamError("No data returned from stream") return stream_fd, prebuffer
def reload_playlist(self): if self.closed: return self.reader.buffer.wait_free() log.debug("Reloading playlist") if self.stream.channel: parsed = urlparse(self.stream.url) if self.stream._first_netloc is None: # save the first netloc self.stream._first_netloc = parsed.netloc # always use the first saved netloc new_stream_url = parsed._replace( netloc=self.stream._first_netloc).geturl() else: new_stream_url = self.stream.url try: res = self.session.http.get(new_stream_url, exception=StreamError, retries=self.playlist_reload_retries, **self.reader.request_params) except StreamError as err: if (hasattr(self.stream, "watch_timeout") and any( x in str(err) for x in ("403 Client Error", "502 Server Error"))): self.stream.watch_timeout = 0 self.playlist_reload_time = 0 log.debug("Force reloading the channel playlist on error: {0}", err) return raise err try: playlist = hls_playlist.load(res.text, res.url) except ValueError as err: raise StreamError(err) if playlist.is_master: raise StreamError("Attempted to play a variant playlist, use " "'hls://{0}' instead".format(self.stream.url)) if playlist.iframes_only: raise StreamError( "Streams containing I-frames only is not playable") media_sequence = playlist.media_sequence or 0 sequences = [ Sequence(media_sequence + i, s) for i, s in enumerate(playlist.segments) ] if sequences: self.process_sequences(playlist, sequences)
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 if args.start_time or args.start_time_unix: time_value = args.start_time if args.start_time else args.start_time_unix if args.start_time: try: start_time = datetime.strptime(args.start_time, '%Y-%m-%dT%H:%M:%S') except ValueError: console.exit('Start time must be formatted as Y-m-dTH:M:S, ' + args.start_time + ' was invalid.') secs_to_wait = (start_time - datetime.now()).total_seconds() elif args.start_time_unix: secs_to_wait = int(args.start_time_unix) - time.time() if secs_to_wait > 0: log.info('Sleeping until start time: ' + time_value) time.sleep(secs_to_wait) else: log.info('Oops! Start time ' + time_value + ' was in the past, starting now.') log.info('Video recording start time: ' + get_timestamp()) # 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: log.debug("Pre-buffering 8192 bytes") prebuffer = stream_fd.read(8192) except IOError as err: stream_fd.close() raise StreamError("Failed to read data from stream: {0}".format(err)) if not prebuffer: stream_fd.close() 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.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", []) copyts = options.pop("copyts", False) 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)]) if copyts: self._cmd.extend(["-copyts"]) self._cmd.extend(["-start_at_zero"]) for stream, data in metadata.items(): for datum in data: self._cmd.extend(["-metadata:{0}".format(stream), datum]) self._cmd.extend(['-f', ofmt, outpath]) log.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 = 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
def __init__(self, session, *streams, **options): if not self.is_usable(session): raise StreamError("cannot use FFMPEG") self.session = session self.process = None self.streams = streams self.chunk_size = int(session.options.get("chunk-size")) self.pipes = [NamedPipe() for _ in self.streams] self.pipe_threads = [threading.Thread(target=self.copy_to_pipe, args=(stream, np, self.chunk_size)) for stream, np in zip(self.streams, self.pipes)] ofmt = session.options.get("ffmpeg-fout") or options.pop("format", self.DEFAULT_OUTPUT_FORMAT) outpath = options.pop("outpath", "pipe:1") videocodec = session.options.get("ffmpeg-video-transcode") or options.pop("vcodec", self.DEFAULT_VIDEO_CODEC) audiocodec = session.options.get("ffmpeg-audio-transcode") or options.pop("acodec", self.DEFAULT_AUDIO_CODEC) metadata = options.pop("metadata", {}) maps = options.pop("maps", []) copyts = session.options.get("ffmpeg-copyts") start_at_zero = session.options.get("ffmpeg-start-at-zero") or options.pop("start_at_zero", False) self._cmd = [self.command(session), '-nostats', '-y'] for np in self.pipes: self._cmd.extend(["-i", str(np.path)]) self._cmd.extend(['-c:v', videocodec]) self._cmd.extend(['-c:a', audiocodec]) for m in maps: self._cmd.extend(["-map", str(m)]) if copyts: self._cmd.extend(["-copyts"]) if start_at_zero: self._cmd.extend(["-start_at_zero"]) for stream, data in metadata.items(): for datum in data: stream_id = ":{0}".format(stream) if stream else "" self._cmd.extend(["-metadata{0}".format(stream_id), datum]) self._cmd.extend(['-f', ofmt, outpath]) log.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 = devnull()
def create_decryptor(self, key, sequence): if key.method != 'AES-128': raise StreamError('Unable to decrypt cipher {0}', key.method) if not key.uri: raise StreamError('Missing URI to decryption key') if self.key_uri != key.uri: log.debug('Diff Key-URI') new_key_uri = key.uri if HLSKeyUriPlugin.get_option('key_uri'): # Repair a broken key-uri log.debug('Old Key-URI: {0}'.format(new_key_uri)) parsed_uri = urlparse(new_key_uri) new_key_uri = HLSKeyUriPlugin.get_option('key_uri') new_data_list = [ (r'\$\{scheme\}', '{0}://'.format(parsed_uri.scheme)), (r'\$\{netloc\}', parsed_uri.netloc), (r'\$\{path\}', parsed_uri.path), (r'\$\{query\}', '?{0}'.format(parsed_uri.query)), ] for _at_re, _old_data in new_data_list: new_key_uri = re.sub(_at_re, _old_data, new_key_uri) log.debug('New Key-URI: {0}'.format(new_key_uri)) res = self.session.http.get(new_key_uri, exception=StreamError, retries=self.retries, **self.reader.request_params) self.key_data = res.content self.key_uri = key.uri iv = key.iv or num_to_iv(sequence) # Pad IV if needed iv = b'\x00' * (16 - len(iv)) + iv return AES.new(self.key_data, AES.MODE_CBC, iv)
def __init__(self, session, *streams, **options): if not self.is_usable(session): raise StreamError("cannot use FFMPEG") self.session = session self.process = None self.streams = streams self.is_muxed = options.pop("is_muxed", True) 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", []) copyts = options.pop("copyts", False) self._cmd = [self.command(session), '-nostats', '-y'] delayVideo = str(getE2config('WPvideoDelay', '0')) if delayVideo != '0': self._cmd.extend(['-itsoffset', delayVideo]) if self.is_muxed: #self.pipes = [NamedPipe("ffmpeg-{0}-{1}".format(os.getpid(), random.randint(0, 1000))) for _ in self.streams] self.pipes = [] self.pipesFiles = [] for _ in self.streams: #zmiana, zeby nie bylo duplikatow tmpName = "ffmpeg-{0}-{1}".format(os.getpid(), random.randint(0, 1000)) pipeFile = os.path.join(tempfile.gettempdir(), tmpName) if os.path.exists(pipeFile): tmpName += str(random.randint(0, 9)) #log.debug("pipeFile: {0}".format(pipeFile)) self.pipes.extend([NamedPipe(tmpName)]) self.pipesFiles.extend([pipeFile]) #log.debug("pipeFile: {0}".format(self.pipesFiles)) self.pipe_threads = [ threading.Thread(target=self.copy_to_pipe, args=(self, stream, np)) for stream, np in zip(self.streams, self.pipes) ] for np in self.pipes: self._cmd.extend(["-i", np.path]) else: self._cmd.extend(["-i", self.streams[0]]) self._cmd.extend(['-c:v', videocodec]) self._cmd.extend(['-c:a', audiocodec]) for m in maps: self._cmd.extend(["-map", str(m)]) if copyts: self._cmd.extend(["-copyts"]) self._cmd.extend(["-start_at_zero"]) for stream, data in metadata.items(): for datum in data: self._cmd.extend(["-metadata:{0}".format(stream), datum]) self._cmd.extend(['-f', ofmt, outpath]) log.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 = devnull()