From 6251555f1c670c66aa781bf9f29e9b5b33d90be1 Mon Sep 17 00:00:00 2001 From: pukkandan Date: Tue, 24 Aug 2021 05:42:45 +0530 Subject: [PATCH] [downloader/ffmpeg] Support for DASH manifests (experimental) Closes #159 --- yt_dlp/downloader/__init__.py | 4 ++++ yt_dlp/downloader/external.py | 15 +++++++-------- yt_dlp/extractor/common.py | 6 ++++-- yt_dlp/utils.py | 1 + 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/yt_dlp/downloader/__init__.py b/yt_dlp/downloader/__init__.py index a0144227e..cd45610fc 100644 --- a/yt_dlp/downloader/__init__.py +++ b/yt_dlp/downloader/__init__.py @@ -94,6 +94,10 @@ def _get_suitable_downloader(info_dict, params, default): if ed.can_download(info_dict, external_downloader): return ed + if protocol == 'http_dash_segments': + if info_dict.get('is_live') and external_downloader.lower() != 'native': + return FFmpegFD + if protocol in ('m3u8', 'm3u8_native'): if info_dict.get('is_live'): return FFmpegFD diff --git a/yt_dlp/downloader/external.py b/yt_dlp/downloader/external.py index be5bd6dd8..fdfabb38d 100644 --- a/yt_dlp/downloader/external.py +++ b/yt_dlp/downloader/external.py @@ -342,7 +342,7 @@ def _make_cmd(self, tmpfilename, info_dict): class FFmpegFD(ExternalFD): - SUPPORTED_PROTOCOLS = ('http', 'https', 'ftp', 'ftps', 'm3u8', 'm3u8_native', 'rtsp', 'rtmp', 'rtmp_ffmpeg', 'mms') + SUPPORTED_PROTOCOLS = ('http', 'https', 'ftp', 'ftps', 'm3u8', 'm3u8_native', 'rtsp', 'rtmp', 'rtmp_ffmpeg', 'mms', 'http_dash_segments') can_download_to_stdout = True @classmethod @@ -462,12 +462,11 @@ def _call_downloader(self, tmpfilename, info_dict): args += self._configuration_args((f'_i{i + 1}', '_i')) + ['-i', url] args += ['-c', 'copy'] - if info_dict.get('requested_formats'): - for (i, fmt) in enumerate(info_dict['requested_formats']): - if fmt.get('acodec') != 'none': - args.extend(['-map', '%d:a:0' % i]) - if fmt.get('vcodec') != 'none': - args.extend(['-map', '%d:v:0' % i]) + if info_dict.get('requested_formats') or protocol == 'http_dash_segments': + for (i, fmt) in enumerate(info_dict.get('requested_formats') or [info_dict]): + stream_number = fmt.get('manifest_stream_number', 0) + a_or_v = 'a' if fmt.get('acodec') != 'none' else 'v' + args.extend(['-map', f'{i}:{a_or_v}:{stream_number}']) if self.params.get('test', False): args += ['-fs', compat_str(self._TEST_FILE_SIZE)] @@ -490,7 +489,7 @@ def _call_downloader(self, tmpfilename, info_dict): else: args += ['-f', EXT_TO_OUT_FORMATS.get(ext, ext)] - args += self._configuration_args((f'_o1', '_o', '')) + args += self._configuration_args(('_o1', '_o', '')) args = [encodeArgument(opt) for opt in args] args.append(encodeFilename(ffpp._ffmpeg_filename_argument(tmpfilename), True)) diff --git a/yt_dlp/extractor/common.py b/yt_dlp/extractor/common.py index 4e6deee1c..b7a55177f 100644 --- a/yt_dlp/extractor/common.py +++ b/yt_dlp/extractor/common.py @@ -2584,8 +2584,8 @@ def extract_Initialization(source): return ms_info mpd_duration = parse_duration(mpd_doc.get('mediaPresentationDuration')) - formats = [] - subtitles = {} + formats, subtitles = [], {} + stream_numbers = {'audio': 0, 'video': 0} for period in mpd_doc.findall(_add_ns('Period')): period_duration = parse_duration(period.get('duration')) or mpd_duration period_ms_info = extract_multisegment_info(period, { @@ -2647,8 +2647,10 @@ def extract_Initialization(source): 'format_note': 'DASH %s' % content_type, 'filesize': filesize, 'container': mimetype2ext(mime_type) + '_dash', + 'manifest_stream_number': stream_numbers[content_type] } f.update(parse_codecs(codecs)) + stream_numbers[content_type] += 1 elif content_type == 'text': f = { 'ext': mimetype2ext(mime_type), diff --git a/yt_dlp/utils.py b/yt_dlp/utils.py index 04c280ba3..c5fbc1c5c 100644 --- a/yt_dlp/utils.py +++ b/yt_dlp/utils.py @@ -4987,6 +4987,7 @@ def cli_configuration_args(argdict, keys, default=[], use_compat=True): return [arg for args in arg_list for arg in args] return default + def _configuration_args(main_key, argdict, exe, keys=None, default=[], use_compat=True): main_key, exe = main_key.lower(), exe.lower() root_key = exe if main_key == exe else f'{main_key}+{exe}'