예제 #1
0
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
예제 #2
0
    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)
예제 #3
0
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
예제 #4
0
    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()
예제 #5
0
 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
예제 #6
0
    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()
예제 #7
0
    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)
예제 #8
0
    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()