Skip to content

Update 视频编码器数据库.vb#115

Open
Howard20181 wants to merge 13 commits intoLake1059:mainfrom
Howard20181:patch-1
Open

Update 视频编码器数据库.vb#115
Howard20181 wants to merge 13 commits intoLake1059:mainfrom
Howard20181:patch-1

Conversation

@Howard20181
Copy link

@Howard20181 Howard20181 commented Jan 28, 2026

从 ffmpeg 命令行生成编码器数据库
需要验证

ffmpeg_encoders_info.py
import subprocess
import re
import json

def get_video_encoders():
    result = subprocess.run(['ffmpeg', '-hide_banner', '-encoders'], capture_output=True, text=True)
    encoders = []
    for line in result.stdout.splitlines():
        if line.startswith(' V') and '=' not in line:
            parts = line.split()
            if len(parts) > 1:
                name = parts[1]
                # 排除image/gif/webp/apng等非视频编码器
                lower = name.lower()
                if any(x in lower for x in ["image", "apng", "webp", "gif", "tiff", "png", "jpeg", "jpg", "bmp", "pbm", "pgm", "ppm", "pam", "sgi", "sunrast", "xbm", "xface", "xwd", "pcx", "ico", "exr", "jp2", "j2k", "jpeg2000", "jpegxl", "jxl", "tga", "dpx", "fits", "pic", "qoi", "tiff", "tiff12", "tiff24", "tiff32", "tiff48", "tiff64", "tiff8", "tiff16", "tiff32", "tiff64", "tiffle", "tiffbe", "tiffle16", "tiffbe16", "tiffle32", "tiffbe32", "tiffle64", "tiffbe64"]):
                    continue
                encoders.append(name)
    return encoders

def parse_options(lines, key):
    # 只要有参数行就向下查找缩进行提取可选值
    options = []
    param_pattern = re.compile(rf'-{key}\s+')
    for i, line in enumerate(lines):
        if param_pattern.search(line):
            j = i + 1
            while j < len(lines):
                next_line = lines[j]
                # 如果遇到下一个参数行(以-开头)或空行则停止
                if next_line.strip() == '' or next_line.lstrip().startswith('-'):
                    break
                # 只处理缩进行
                if re.match(r'\s+\S+\s+\S+', next_line):
                    opt = next_line.strip().split()[0]
                    options.append(opt)
                j += 1
            # 如果没有缩进行可选值,且参数行为<int>类型且有(from X to Y),则自动生成整数序列
            if not options:
                m = re.search(r'<int>.*\(from\s+(-?\d+)\s+to\s+(-?\d+)\)', line)
                if m:
                    start, end = int(m.group(1)), int(m.group(2))
                    if start <= 0:
                        start = 1
                    options = [str(i) for i in range(start, end + 1)]
            break
    return options

def get_encoder_info(encoder):
    cmd = ['ffmpeg', '-hide_banner', '-h', f'encoder={encoder}']
    result = subprocess.run(cmd, capture_output=True, text=True)
    lines = result.stdout.splitlines()
    # 提取 pixel formats
    pix_fmt = []
    for line in lines:
        if 'Supported pixel formats:' in line:
            pix_fmt = line.split(':', 1)[1].strip().split()
            break
    # 提取 profile/preset/tune
    def get_option(key):
        opts = parse_options(lines, key)
        # 排除unknown和default字符串
        opts = [x for x in opts if x not in ('unknown', 'default')]
        # nvenc编码器preset仅保留pX级别
        if encoder.lower().endswith('nvenc') and key == 'preset':
            opts = [x for x in opts if re.fullmatch(r'p\d+', x)]
        if opts:
            return opts
        # 检查是否有参数但无可选值
        for line in lines:
            if f'-{key}' in line:
                return []
        return []
    return {
        'preset': get_option('preset'),
        'profile': get_option('profile'),
        'tune': get_option('tune'),
        'pix_fmt': pix_fmt
    }

def main():
    encoders = get_video_encoders()
    all_info = {}
    for enc in encoders:
        all_info[enc] = get_encoder_info(enc)
    with open('ffmpeg_encoders_info.json', 'w', encoding='utf-8') as f:
        json.dump(all_info, f, ensure_ascii=False, indent=2)
    print('已保存到 ffmpeg_encoders_info.json')

if __name__ == '__main__':
    main()
ffmpeg_encoders_db_gen.py
import json, re


def fmt(lst):
    items = [x for x in lst if x]
    return ", ".join(f'"{x}"' for x in items) if items else '""'


def is_int_seq(lst):
    # 判断是否为int序列(允许负数)
    return all(re.fullmatch(r"-?\d+", x) for x in lst if x)


def fmt_preset(lst, enc=None):
    items = [x for x in lst if x]
    if not items:
        return '""'
    # 指定编码器保持json顺序
    if enc and enc in ["av1_amf", "av1_vaapi", "av1_vulkan"]:
        return ", ".join(f'"{x}"' for x in items)
    if is_int_seq(items):
        nums = [x for x in set(items) if re.fullmatch(r"-?\d+", x)]
        if not nums:
            return '""'
        return ", ".join(f'"{x}"' for x in sorted(nums, key=lambda x: int(x)))
    return ", ".join(f'"{x}"' for x in reversed(items)) if items else '""'


# 读取原始数据库,构建查找表
orig_db = {}
try:
    with open("视频编码器数据库.vb", "r", encoding="utf-8") as f:
        vb = f.read()
    for m in re.finditer(
        r'字典\.Add\("([^"]+)", New 视频编码器数据单片结构 With \{(.+?)\}\)', vb, re.S
    ):
        enc = m.group(1)
        block = m.group(2)

        def get_list(field):
            fm = re.search(
                rf"\.{field} = New List\(Of String\) From \{{(.*?)\}}", block, re.S
            )
            if fm:
                return [x.strip().strip('"') for x in fm.group(1).split(",")]
            return [""]

        orig_db[enc] = {
            "preset": get_list("Preset"),
            "profile": get_list("Profile"),
            "tune": get_list("Tune"),
            "pix_fmt": get_list("Pix_fmt"),
        }
except Exception as e:
    orig_db = {}


def fix_field(enc, key, val):
    # 如果json字段为空或不存在,直接用原数据库内容(保持原顺序)
    if (
        not val
        or all(x == "" for x in val)
        or any(x == "(unknown, not listed)" for x in val)
    ):
        if enc in orig_db and orig_db[enc][key]:
            return orig_db[enc][key]
        # 过滤掉unknown
        return [x for x in val if x != "(unknown, not listed)"]
    return val


vb_lines = []
vb_lines.append("Public Class 视频编码器数据库")
vb_lines.append(
    "    Public Shared Property 字典 As New Dictionary(Of String, 视频编码器数据单片结构)"
)
vb_lines.append("")
vb_lines.append("    Public Class 视频编码器数据单片结构")
vb_lines.append("        Public Property Preset As New List(Of String)")
vb_lines.append("        Public Property Profile As New List(Of String)")
vb_lines.append("        Public Property Tune As New List(Of String)")
vb_lines.append("        Public Property Pix_fmt As New List(Of String)")
vb_lines.append("    End Class")
vb_lines.append("")
vb_lines.append("")
vb_lines.append("    Public Shared Sub 初始化()")


# 先加copy项
vb_lines.append('        字典.Add("copy", New 视频编码器数据单片结构 With {')
vb_lines.append('.Preset = New List(Of String) From {""},')
vb_lines.append('.Profile = New List(Of String) From {""},')
vb_lines.append('.Tune = New List(Of String) From {""},')
vb_lines.append('.Pix_fmt = New List(Of String) From {""}')
vb_lines.append("})")

# 读取 ffmpeg_encoders_info.json
with open("ffmpeg_encoders_info.json", "r", encoding="utf-8") as f:
    data = json.load(f)
for enc, info in data.items():
    if enc == "copy":
        continue
    preset = fix_field(enc, "preset", info.get("preset", []))
    profile = fix_field(enc, "profile", info.get("profile", []))
    tune = fix_field(enc, "tune", info.get("tune", []))
    pix_fmt = fix_field(enc, "pix_fmt", info.get("pix_fmt", []))
    vb_lines.append(f'        字典.Add("{enc}", New 视频编码器数据单片结构 With {{')
    vb_lines.append(
        f".Preset = New List(Of String) From {{{fmt_preset(preset, enc)}}},"
    )
    vb_lines.append(f".Profile = New List(Of String) From {{{fmt(profile)}}},")
    vb_lines.append(f".Tune = New List(Of String) From {{{fmt(tune)}}},")
    vb_lines.append(f".Pix_fmt = New List(Of String) From {{{fmt(pix_fmt)}}}")
    vb_lines.append("})")

vb_lines.append("    End Sub")
vb_lines.append("")
vb_lines.append("End Class")

with open("ffmpeg_encoders_db_gen.vb", "w", encoding="utf-8") as f:
    f.write("\n".join(vb_lines))

print("已生成 ffmpeg_encoders_db_gen.vb")

@Howard20181
Copy link
Author

不太行,建议关掉
做成查询工具人工补全

@Howard20181 Howard20181 marked this pull request as ready for review January 28, 2026 22:07
@Lake1059
Copy link
Owner

不太行,建议关掉 做成查询工具人工补全

请继续调整

  • 尤其是preset必须是从慢到快
  • 未收录的编码器请不要加进来

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants