示例#1
0
    def __get_streaming_video_download_cmd(self):
        # --retries infinite: in case downloading has transient errors
        youtube_dl_cmd_template = "yt-dlp {0} --retries infinite --format {1} --output - {2} | {3}"

        log_opts = '--no-progress'
        if Logger.get_level() <= Logger.DEBUG:
            log_opts = ''  # show video download progress
        if not sys.stderr.isatty():
            log_opts += ' --newline'

        # 50 MB. Based on one video, 1080p avc1 video consumes about 0.36 MB/s. So this should
        # be enough buffer for ~139s for a 1080p video, which is a lot higher resolution than we
        # are ever likely to use.
        video_buffer_size = 1024 * 1024 * 50

        # Choose 'worst' video because we want our pixel ffmpeg video scaler to do less work when we
        # scale the video down to the LED matrix size.
        #
        # But the height should be at least the height of the LED matrix (this probably only matters
        # if someone made a very large LED matrix such that the worst quality video was lower resolution
        # than the LED matrix pixel dimensions). Filter on only height so that vertical videos don't
        # result in a super large resolution being chosen? /shrug ... could consider adding a filter on
        # width too.
        #
        # Use avc1 because this means h264, and the pi has hardware acceleration for this format.
        # See: https://github.com/dasl-/piwall2/blob/88030a47790e5ae208d2c9fe19f9c623fc736c83/docs/video_formats_and_hardware_acceleration.adoc#youtube--youtube-dl
        #
        # Fallback onto 'worst' rather than 'worstvideo', because some videos (live videos) only
        # have combined video + audio formats. Thus, 'worstvideo' would fail for them.
        video_format = (
            f'worstvideo[vcodec^=avc1][height>={Config.get_or_throw("leds.display_height")}]/'
            +
            f'worst[vcodec^=avc1][height>={Config.get_or_throw("leds.display_height")}]'
        )
        youtube_dl_video_cmd = youtube_dl_cmd_template.format(
            shlex.quote(self.__url), shlex.quote(video_format), log_opts,
            self.__get_mbuffer_cmd(video_buffer_size))

        # Also use a 50MB buffer, because in some cases, the audio stream we download may also contain video.
        audio_buffer_size = 1024 * 1024 * 50
        youtube_dl_audio_cmd = youtube_dl_cmd_template.format(
            shlex.quote(self.__url),
            # bestaudio: try to select the best audio-only format
            # bestaudio*: this is the fallback option -- select the best quality format that contains audio.
            #   It may also contain video, e.g. in the case that there are no audio-only formats available.
            #   Some videos (live videos) only have combined video + audio formats. Thus 'bestaudio' would
            #   fail for them.
            shlex.quote('bestaudio/bestaudio*'),
            log_opts,
            self.__get_mbuffer_cmd(audio_buffer_size))

        # Mux video from the first input with audio from the second input: https://stackoverflow.com/a/12943003/627663
        # We need to specify, because in some cases, either input could contain both audio and video. But in most
        # cases, the first input will have only video, and the second input will have only audio.
        return (
            f"{self.get_standard_ffmpeg_cmd()} -i <({youtube_dl_video_cmd}) -i <({youtube_dl_audio_cmd}) "
            + "-c copy -map 0:v:0 -map 1:a:0 -shortest -f mpegts -")
示例#2
0
    def get_standard_ffmpeg_cmd():
        # unfortunately there's no way to make ffmpeg output its stats progress stuff with line breaks
        log_opts = '-nostats '
        if sys.stderr.isatty():
            log_opts = '-stats '

        if Logger.get_level() <= Logger.DEBUG:
            pass  # don't change anything, ffmpeg is pretty verbose by default
        else:
            log_opts += '-loglevel error'

        # Note: don't use ffmpeg's `-xerror` flag:
        # https://gist.github.com/dasl-/1ad012f55f33f14b44393960f66c6b00
        return f"ffmpeg -hide_banner {log_opts} "