scripts: Add a script to validate video formats list

These lists have complex ordering and are error prone even for trained
reviewers. This tool will parse the specified define in the specified file
and determin if there is any error, unknown formats or miss-ordering.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5304>
This commit is contained in:
Nicolas Dufresne 2023-09-07 17:05:35 -04:00
parent b7ded81f7b
commit 63b8a5f22d

145
scripts/sort_video_formats.py Executable file
View file

@ -0,0 +1,145 @@
#!/usr/bin/env python3
import argparse
import gi
import os
import sys
gi.require_version("Gst", "1.0")
gi.require_version("GstVideo", "1.0")
from gi.repository import GLib
from gi.repository import Gst, GstVideo
def video_format_exists(f):
return GstVideo.video_format_from_string(f) != GstVideo.VideoFormat.UNKNOWN
def video_format_info_from_string(s):
f = GstVideo.video_format_from_string(s)
return GstVideo.video_format_get_info(f)
def parse_format_from_c(token, filename):
code = open(filename).read().splitlines()
token_found = False
formats = None
for idx, line in enumerate(code):
if formats is None:
match = f"#define {token}"
if line.startswith(match):
formats = []
line = line[len(match):]
else:
continue
tmp = line.replace("\\", "")\
.replace("\"", "")\
.replace(",", " ")\
.replace("{", " ")\
.replace("}", " ")\
.split()
for f in tmp:
if not video_format_exists(f):
basename = os.path.basename(filename)
print(f"WARNING: {basename}:{idx}: unknown format {f}")
formats += tmp
if line[-1] != "\\":
break
if formats is None:
print(f"Define for {token} not found in {filename}")
return None
formats = list(filter(video_format_exists, formats))
return [video_format_info_from_string(f) for f in formats]
def score_endian(fmt, endian):
score = 0
if fmt.flags & GstVideo.VideoFormatFlags.LE:
score = 1
if endian == "LE":
return -score
else:
return score
def sort_video_formats(formats, endian):
return sorted(formats, key=lambda x: (-x.n_components,
[-d for d in x.depth],
x.w_sub,
x.h_sub,
-x.n_planes,
score_endian(x, endian),
[-s for s in x.pixel_stride],
(x.flags & GstVideo.VideoFormatFlags.COMPLEX),
not (x.flags & GstVideo.VideoFormatFlags.YUV),
x.name))
def video_formats_to_c(token, formats, bracket):
s = [f'#define {token} "']
if bracket:
s[-1] += "{ "
for f in formats:
if len(s[-1]) > 80 - len(f.name) - len(', " \\'):
s[-1] += ', " \\'
s.append(' "')
if s[-1][-2:] not in (' "', '{ '):
s[-1] += ", "
s[-1] += f.name
if bracket:
s[-1] += " }"
s[-1] += '"'
return s
def generate_c_code(token, formats_be, formats_le, bracket):
s = ["#if G_BYTE_ORDER == G_BIG_ENDIAN"]
s += video_formats_to_c(token, formats_be, bracket)
s += ["#elif G_BYTE_ORDER == G_LITTLE_ENDIAN"]
s += video_formats_to_c(token, formats_le, bracket)
s += ["#endif"]
return s
def main(args):
formats = parse_format_from_c(args.token, args.filename)
if formats is None:
return 1
formats_be = sort_video_formats(formats, "BE")
formats_le = sort_video_formats(formats, "LE")
# We only check BE order as the list are now generated
if formats_be == formats:
print("Formats are properly ordered.")
return 0
basename = os.path.basename(args.filename)
print(f"ERROR: Formats order differ for {args.token} in {args.filename}")
print("This can be fixed by replacing the define with:\n")
for c_line in generate_c_code(args.token, formats_be, formats_le,
args.bracket):
print(c_line)
return 1
if __name__ == '__main__':
Gst.init(None)
parser = argparse.ArgumentParser(prog='sort_video_formats',
description='Sort video formats list')
parser.add_argument('token')
parser.add_argument('filename')
parser.add_argument('-b', '--bracket', action='store_true')
args = parser.parse_args()
sys.exit(main(args))