mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-12-18 01:07:50 +01:00
Add HDR information to formats
This commit is contained in:
parent
17bddf3e95
commit
176f1866cb
@ -1060,6 +1060,7 @@ # OUTPUT TEMPLATE
|
|||||||
- `asr` (numeric): Audio sampling rate in Hertz
|
- `asr` (numeric): Audio sampling rate in Hertz
|
||||||
- `vbr` (numeric): Average video bitrate in KBit/s
|
- `vbr` (numeric): Average video bitrate in KBit/s
|
||||||
- `fps` (numeric): Frame rate
|
- `fps` (numeric): Frame rate
|
||||||
|
- `dynamic_range` (string): The dynamic range of the video
|
||||||
- `vcodec` (string): Name of the video codec in use
|
- `vcodec` (string): Name of the video codec in use
|
||||||
- `container` (string): Name of the container format
|
- `container` (string): Name of the container format
|
||||||
- `filesize` (numeric): The number of bytes, if known in advance
|
- `filesize` (numeric): The number of bytes, if known in advance
|
||||||
@ -1283,6 +1284,7 @@ ## Sorting Formats
|
|||||||
- `width`: Width of video
|
- `width`: Width of video
|
||||||
- `res`: Video resolution, calculated as the smallest dimension.
|
- `res`: Video resolution, calculated as the smallest dimension.
|
||||||
- `fps`: Framerate of video
|
- `fps`: Framerate of video
|
||||||
|
- `hdr`: The dynamic range of the video (`DV` > `HDR12` > `HDR10+` > `HDR10` > `SDR`)
|
||||||
- `tbr`: Total average bitrate in KBit/s
|
- `tbr`: Total average bitrate in KBit/s
|
||||||
- `vbr`: Average video bitrate in KBit/s
|
- `vbr`: Average video bitrate in KBit/s
|
||||||
- `abr`: Average audio bitrate in KBit/s
|
- `abr`: Average audio bitrate in KBit/s
|
||||||
@ -1293,9 +1295,9 @@ ## Sorting Formats
|
|||||||
|
|
||||||
All fields, unless specified otherwise, are sorted in descending order. To reverse this, prefix the field with a `+`. Eg: `+res` prefers format with the smallest resolution. Additionally, you can suffix a preferred value for the fields, separated by a `:`. Eg: `res:720` prefers larger videos, but no larger than 720p and the smallest video if there are no videos less than 720p. For `codec` and `ext`, you can provide two preferred values, the first for video and the second for audio. Eg: `+codec:avc:m4a` (equivalent to `+vcodec:avc,+acodec:m4a`) sets the video codec preference to `h264` > `h265` > `vp9` > `vp9.2` > `av01` > `vp8` > `h263` > `theora` and audio codec preference to `mp4a` > `aac` > `vorbis` > `opus` > `mp3` > `ac3` > `dts`. You can also make the sorting prefer the nearest values to the provided by using `~` as the delimiter. Eg: `filesize~1G` prefers the format with filesize closest to 1 GiB.
|
All fields, unless specified otherwise, are sorted in descending order. To reverse this, prefix the field with a `+`. Eg: `+res` prefers format with the smallest resolution. Additionally, you can suffix a preferred value for the fields, separated by a `:`. Eg: `res:720` prefers larger videos, but no larger than 720p and the smallest video if there are no videos less than 720p. For `codec` and `ext`, you can provide two preferred values, the first for video and the second for audio. Eg: `+codec:avc:m4a` (equivalent to `+vcodec:avc,+acodec:m4a`) sets the video codec preference to `h264` > `h265` > `vp9` > `vp9.2` > `av01` > `vp8` > `h263` > `theora` and audio codec preference to `mp4a` > `aac` > `vorbis` > `opus` > `mp3` > `ac3` > `dts`. You can also make the sorting prefer the nearest values to the provided by using `~` as the delimiter. Eg: `filesize~1G` prefers the format with filesize closest to 1 GiB.
|
||||||
|
|
||||||
The fields `hasvid` and `ie_pref` are always given highest priority in sorting, irrespective of the user-defined order. This behaviour can be changed by using `--format-sort-force`. Apart from these, the default order used is: `lang,quality,res,fps,codec:vp9.2,size,br,asr,proto,ext,hasaud,source,id`. The extractors may override this default order, but they cannot override the user-provided order.
|
The fields `hasvid` and `ie_pref` are always given highest priority in sorting, irrespective of the user-defined order. This behaviour can be changed by using `--format-sort-force`. Apart from these, the default order used is: `lang,quality,res,fps,hdr:12,codec:vp9.2,size,br,asr,proto,ext,hasaud,source,id`. The extractors may override this default order, but they cannot override the user-provided order.
|
||||||
|
|
||||||
Note that the default has `codec:vp9.2`; i.e. `av1` is not prefered
|
Note that the default has `codec:vp9.2`; i.e. `av1` is not prefered. Similarly, the default for hdr is `hdr:12`; i.e. dolby vision is not prefered. These choices are made since DV and AV1 formats are not yet fully compatible with most devices. This may be changed in the future as more devices become capable of smoothly playing back these formats.
|
||||||
|
|
||||||
If your format selector is `worst`, the last item is selected after sorting. This means it will select the format that is worst in all respects. Most of the time, what you actually want is the video with the smallest filesize instead. So it is generally better to use `-f best -S +size,+br,+res,+fps`.
|
If your format selector is `worst`, the last item is selected after sorting. This means it will select the format that is worst in all respects. Most of the time, what you actually want is the video with the smallest filesize instead. So it is generally better to use `-f best -S +size,+br,+res,+fps`.
|
||||||
|
|
||||||
|
@ -848,30 +848,52 @@ def test_parse_codecs(self):
|
|||||||
self.assertEqual(parse_codecs('avc1.77.30, mp4a.40.2'), {
|
self.assertEqual(parse_codecs('avc1.77.30, mp4a.40.2'), {
|
||||||
'vcodec': 'avc1.77.30',
|
'vcodec': 'avc1.77.30',
|
||||||
'acodec': 'mp4a.40.2',
|
'acodec': 'mp4a.40.2',
|
||||||
|
'dynamic_range': None,
|
||||||
})
|
})
|
||||||
self.assertEqual(parse_codecs('mp4a.40.2'), {
|
self.assertEqual(parse_codecs('mp4a.40.2'), {
|
||||||
'vcodec': 'none',
|
'vcodec': 'none',
|
||||||
'acodec': 'mp4a.40.2',
|
'acodec': 'mp4a.40.2',
|
||||||
|
'dynamic_range': None,
|
||||||
})
|
})
|
||||||
self.assertEqual(parse_codecs('mp4a.40.5,avc1.42001e'), {
|
self.assertEqual(parse_codecs('mp4a.40.5,avc1.42001e'), {
|
||||||
'vcodec': 'avc1.42001e',
|
'vcodec': 'avc1.42001e',
|
||||||
'acodec': 'mp4a.40.5',
|
'acodec': 'mp4a.40.5',
|
||||||
|
'dynamic_range': None,
|
||||||
})
|
})
|
||||||
self.assertEqual(parse_codecs('avc3.640028'), {
|
self.assertEqual(parse_codecs('avc3.640028'), {
|
||||||
'vcodec': 'avc3.640028',
|
'vcodec': 'avc3.640028',
|
||||||
'acodec': 'none',
|
'acodec': 'none',
|
||||||
|
'dynamic_range': None,
|
||||||
})
|
})
|
||||||
self.assertEqual(parse_codecs(', h264,,newcodec,aac'), {
|
self.assertEqual(parse_codecs(', h264,,newcodec,aac'), {
|
||||||
'vcodec': 'h264',
|
'vcodec': 'h264',
|
||||||
'acodec': 'aac',
|
'acodec': 'aac',
|
||||||
|
'dynamic_range': None,
|
||||||
})
|
})
|
||||||
self.assertEqual(parse_codecs('av01.0.05M.08'), {
|
self.assertEqual(parse_codecs('av01.0.05M.08'), {
|
||||||
'vcodec': 'av01.0.05M.08',
|
'vcodec': 'av01.0.05M.08',
|
||||||
'acodec': 'none',
|
'acodec': 'none',
|
||||||
|
'dynamic_range': None,
|
||||||
|
})
|
||||||
|
self.assertEqual(parse_codecs('vp9.2'), {
|
||||||
|
'vcodec': 'vp9.2',
|
||||||
|
'acodec': 'none',
|
||||||
|
'dynamic_range': 'HDR10',
|
||||||
|
})
|
||||||
|
self.assertEqual(parse_codecs('av01.0.12M.10.0.110.09.16.09.0'), {
|
||||||
|
'vcodec': 'av01.0.12M.10',
|
||||||
|
'acodec': 'none',
|
||||||
|
'dynamic_range': 'HDR10',
|
||||||
|
})
|
||||||
|
self.assertEqual(parse_codecs('dvhe'), {
|
||||||
|
'vcodec': 'dvhe',
|
||||||
|
'acodec': 'none',
|
||||||
|
'dynamic_range': 'DV',
|
||||||
})
|
})
|
||||||
self.assertEqual(parse_codecs('theora, vorbis'), {
|
self.assertEqual(parse_codecs('theora, vorbis'), {
|
||||||
'vcodec': 'theora',
|
'vcodec': 'theora',
|
||||||
'acodec': 'vorbis',
|
'acodec': 'vorbis',
|
||||||
|
'dynamic_range': None,
|
||||||
})
|
})
|
||||||
self.assertEqual(parse_codecs('unknownvcodec, unknownacodec'), {
|
self.assertEqual(parse_codecs('unknownvcodec, unknownacodec'), {
|
||||||
'vcodec': 'unknownvcodec',
|
'vcodec': 'unknownvcodec',
|
||||||
|
@ -2291,6 +2291,8 @@ def is_wellformed(f):
|
|||||||
format['protocol'] = determine_protocol(format)
|
format['protocol'] = determine_protocol(format)
|
||||||
if format.get('resolution') is None:
|
if format.get('resolution') is None:
|
||||||
format['resolution'] = self.format_resolution(format, default=None)
|
format['resolution'] = self.format_resolution(format, default=None)
|
||||||
|
if format.get('dynamic_range') is None and format.get('vcodec') != 'none':
|
||||||
|
format['dynamic_range'] = 'SDR'
|
||||||
# Add HTTP headers, so that external programs can use them from the
|
# Add HTTP headers, so that external programs can use them from the
|
||||||
# json output
|
# json output
|
||||||
full_format_info = info_dict.copy()
|
full_format_info = info_dict.copy()
|
||||||
@ -3176,6 +3178,7 @@ def list_formats(self, info_dict):
|
|||||||
format_field(f, 'ext'),
|
format_field(f, 'ext'),
|
||||||
self.format_resolution(f),
|
self.format_resolution(f),
|
||||||
format_field(f, 'fps', '%d'),
|
format_field(f, 'fps', '%d'),
|
||||||
|
format_field(f, 'dynamic_range', '%s', ignore=(None, 'SDR')).replace('HDR', ''),
|
||||||
'|',
|
'|',
|
||||||
format_field(f, 'filesize', ' %s', func=format_bytes) + format_field(f, 'filesize_approx', '~%s', func=format_bytes),
|
format_field(f, 'filesize', ' %s', func=format_bytes) + format_field(f, 'filesize_approx', '~%s', func=format_bytes),
|
||||||
format_field(f, 'tbr', '%4dk'),
|
format_field(f, 'tbr', '%4dk'),
|
||||||
@ -3193,7 +3196,7 @@ def list_formats(self, info_dict):
|
|||||||
format_field(f, 'container', ignore=(None, f.get('ext'))),
|
format_field(f, 'container', ignore=(None, f.get('ext'))),
|
||||||
))),
|
))),
|
||||||
] for f in formats if f.get('preference') is None or f['preference'] >= -1000]
|
] for f in formats if f.get('preference') is None or f['preference'] >= -1000]
|
||||||
header_line = ['ID', 'EXT', 'RESOLUTION', 'FPS', '|', ' FILESIZE', ' TBR', 'PROTO',
|
header_line = ['ID', 'EXT', 'RESOLUTION', 'FPS', 'HDR', '|', ' FILESIZE', ' TBR', 'PROTO',
|
||||||
'|', 'VCODEC', ' VBR', 'ACODEC', ' ABR', ' ASR', 'MORE INFO']
|
'|', 'VCODEC', ' VBR', 'ACODEC', ' ABR', ' ASR', 'MORE INFO']
|
||||||
else:
|
else:
|
||||||
table = [
|
table = [
|
||||||
|
@ -147,6 +147,8 @@ class InfoExtractor(object):
|
|||||||
* width Width of the video, if known
|
* width Width of the video, if known
|
||||||
* height Height of the video, if known
|
* height Height of the video, if known
|
||||||
* resolution Textual description of width and height
|
* resolution Textual description of width and height
|
||||||
|
* dynamic_range The dynamic range of the video. One of:
|
||||||
|
"SDR" (None), "HDR10", "HDR10+, "HDR12", "HLG, "DV"
|
||||||
* tbr Average bitrate of audio and video in KBit/s
|
* tbr Average bitrate of audio and video in KBit/s
|
||||||
* abr Average audio bitrate in KBit/s
|
* abr Average audio bitrate in KBit/s
|
||||||
* acodec Name of the audio codec in use
|
* acodec Name of the audio codec in use
|
||||||
@ -1507,7 +1509,7 @@ class FormatSort:
|
|||||||
regex = r' *((?P<reverse>\+)?(?P<field>[a-zA-Z0-9_]+)((?P<separator>[~:])(?P<limit>.*?))?)? *$'
|
regex = r' *((?P<reverse>\+)?(?P<field>[a-zA-Z0-9_]+)((?P<separator>[~:])(?P<limit>.*?))?)? *$'
|
||||||
|
|
||||||
default = ('hidden', 'aud_or_vid', 'hasvid', 'ie_pref', 'lang', 'quality',
|
default = ('hidden', 'aud_or_vid', 'hasvid', 'ie_pref', 'lang', 'quality',
|
||||||
'res', 'fps', 'codec:vp9.2', 'size', 'br', 'asr',
|
'res', 'fps', 'hdr:12', 'codec:vp9.2', 'size', 'br', 'asr',
|
||||||
'proto', 'ext', 'hasaud', 'source', 'format_id') # These must not be aliases
|
'proto', 'ext', 'hasaud', 'source', 'format_id') # These must not be aliases
|
||||||
ytdl_default = ('hasaud', 'lang', 'quality', 'tbr', 'filesize', 'vbr',
|
ytdl_default = ('hasaud', 'lang', 'quality', 'tbr', 'filesize', 'vbr',
|
||||||
'height', 'width', 'proto', 'vext', 'abr', 'aext',
|
'height', 'width', 'proto', 'vext', 'abr', 'aext',
|
||||||
@ -1518,6 +1520,8 @@ class FormatSort:
|
|||||||
'order': ['av0?1', 'vp0?9.2', 'vp0?9', '[hx]265|he?vc?', '[hx]264|avc', 'vp0?8', 'mp4v|h263', 'theora', '', None, 'none']},
|
'order': ['av0?1', 'vp0?9.2', 'vp0?9', '[hx]265|he?vc?', '[hx]264|avc', 'vp0?8', 'mp4v|h263', 'theora', '', None, 'none']},
|
||||||
'acodec': {'type': 'ordered', 'regex': True,
|
'acodec': {'type': 'ordered', 'regex': True,
|
||||||
'order': ['opus', 'vorbis', 'aac', 'mp?4a?', 'mp3', 'e?a?c-?3', 'dts', '', None, 'none']},
|
'order': ['opus', 'vorbis', 'aac', 'mp?4a?', 'mp3', 'e?a?c-?3', 'dts', '', None, 'none']},
|
||||||
|
'hdr': {'type': 'ordered', 'regex': True, 'field': 'dynamic_range',
|
||||||
|
'order': ['dv', '(hdr)?12', r'(hdr)?10\+', '(hdr)?10', 'hlg', '', 'sdr', None]},
|
||||||
'proto': {'type': 'ordered', 'regex': True, 'field': 'protocol',
|
'proto': {'type': 'ordered', 'regex': True, 'field': 'protocol',
|
||||||
'order': ['(ht|f)tps', '(ht|f)tp$', 'm3u8.+', '.*dash', 'ws|websocket', '', 'mms|rtsp', 'none', 'f4']},
|
'order': ['(ht|f)tps', '(ht|f)tp$', 'm3u8.+', '.*dash', 'ws|websocket', '', 'mms|rtsp', 'none', 'f4']},
|
||||||
'vext': {'type': 'ordered', 'field': 'video_ext',
|
'vext': {'type': 'ordered', 'field': 'video_ext',
|
||||||
|
@ -2653,7 +2653,7 @@ def feed_entry(name):
|
|||||||
|
|
||||||
# Source is given priority since formats that throttle are given lower source_preference
|
# Source is given priority since formats that throttle are given lower source_preference
|
||||||
# When throttling issue is fully fixed, remove this
|
# When throttling issue is fully fixed, remove this
|
||||||
self._sort_formats(formats, ('quality', 'res', 'fps', 'source', 'codec:vp9.2', 'lang'))
|
self._sort_formats(formats, ('quality', 'res', 'fps', 'hdr:12', 'source', 'codec:vp9.2', 'lang'))
|
||||||
|
|
||||||
keywords = get_first(video_details, 'keywords', expected_type=list) or []
|
keywords = get_first(video_details, 'keywords', expected_type=list) or []
|
||||||
if not keywords and webpage:
|
if not keywords and webpage:
|
||||||
|
@ -4618,12 +4618,21 @@ def parse_codecs(codecs_str):
|
|||||||
return {}
|
return {}
|
||||||
split_codecs = list(filter(None, map(
|
split_codecs = list(filter(None, map(
|
||||||
str.strip, codecs_str.strip().strip(',').split(','))))
|
str.strip, codecs_str.strip().strip(',').split(','))))
|
||||||
vcodec, acodec = None, None
|
vcodec, acodec, hdr = None, None, None
|
||||||
for full_codec in split_codecs:
|
for full_codec in split_codecs:
|
||||||
codec = full_codec.split('.')[0]
|
codec = full_codec.split('.')[0]
|
||||||
if codec in ('avc1', 'avc2', 'avc3', 'avc4', 'vp9', 'vp8', 'hev1', 'hev2', 'h263', 'h264', 'mp4v', 'hvc1', 'av01', 'theora', 'dvh1', 'dvhe'):
|
if codec in ('avc1', 'avc2', 'avc3', 'avc4', 'vp9', 'vp8', 'hev1', 'hev2', 'h263', 'h264', 'mp4v', 'hvc1', 'av01', 'theora', 'dvh1', 'dvhe'):
|
||||||
if not vcodec:
|
if not vcodec:
|
||||||
vcodec = full_codec
|
vcodec = full_codec
|
||||||
|
if codec in ('dvh1', 'dvhe'):
|
||||||
|
hdr = 'DV'
|
||||||
|
elif codec == 'vp9' and vcodec.startswith('vp9.2'):
|
||||||
|
hdr = 'HDR10'
|
||||||
|
elif codec == 'av01':
|
||||||
|
parts = full_codec.split('.')
|
||||||
|
if len(parts) > 3 and parts[3] == '10':
|
||||||
|
hdr = 'HDR10'
|
||||||
|
vcodec = '.'.join(parts[:4])
|
||||||
elif codec in ('mp4a', 'opus', 'vorbis', 'mp3', 'aac', 'ac-3', 'ec-3', 'eac3', 'dtsc', 'dtse', 'dtsh', 'dtsl'):
|
elif codec in ('mp4a', 'opus', 'vorbis', 'mp3', 'aac', 'ac-3', 'ec-3', 'eac3', 'dtsc', 'dtse', 'dtsh', 'dtsl'):
|
||||||
if not acodec:
|
if not acodec:
|
||||||
acodec = full_codec
|
acodec = full_codec
|
||||||
@ -4639,6 +4648,7 @@ def parse_codecs(codecs_str):
|
|||||||
return {
|
return {
|
||||||
'vcodec': vcodec or 'none',
|
'vcodec': vcodec or 'none',
|
||||||
'acodec': acodec or 'none',
|
'acodec': acodec or 'none',
|
||||||
|
'dynamic_range': hdr,
|
||||||
}
|
}
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user