某简单易懂的LibASS使用说明

前言

    最近在写Foobar的弹幕支持插件(见 Foobar字幕(弹幕)支持插件开发日志),在ASS格式支持部分需要用到LibASS,这里就把使用的情况记录一下吧~

简介

    ASS / SSA格式,全称Advanced Substation Alpha / Substation Alpha,是一种功能极为强大的字幕格式,其实跟ASS WE CAN是米有关系的~ASS本质是文本文档,编辑非常方便.关于ASS格式的使用不是本文的讨论范围,这里不予叙述~

    LibASS则是一个轻量级的对ASS / SSA格式字幕进行渲染的函数库.该库使用C编写,效率较高.据官方说明,LibASS和VSFilter兼容性最好~
    *LibASS依赖的第三方库: FreeType, iConv(可选), FontConfig(可选)

    FreeType是LibASS使用的通用字体渲染库,也是很强大的库,作用是把系统的字库(ClearType神马的)渲染成单张Bitmap(位图)~

使用流程

    以下就以LibASS自带的Test.c为例,说明一下LibASS的工作方式.下列函数顺序调用,即可最简使用LibASS.

  1. 初始化LibASS / init()
    这个函数用于初始化LibASS的,只有两个参数:输出位图的长和宽.
    1.ass_library_init()函数:
        作用:初始化LibASS的字幕库(ASS_Library)
        参数:无
        返回值:字幕库的指针/ ASS_Library*
    2.ass_renderer_init()函数:
        作用:初始化LibAss的渲染器(ASS_Renderer)
        参数:字幕库的指针 / ASS_Library*
        返回值:渲染器的指针 / ASS_Renderer*
    3.ass_set_frame_size()函数:
        作用:指定输出位图大小
        参数1:渲染器的指针 / ASS_Render*
        参数2:输出位图的宽 / int
        参数3:输出位图的长 / int
        返回值:无
    4.ass_set_fonts()函数:
        作用:指定使用的字体库,如果没有使用FontConfig库的话,必须指定字体,否则渲染失败.
        参数1:渲染器的指针 / ASS_Renderer*
        参数2:字库完整文件路径 / const char*
        参数3:字体类型 / const char*
        参数4:是否使用FontConfig / int
        参数5:FontConfig路径 / const char*
        参数6:是否更新FontConfig的缓冲 / int
        返回值:无
    ASS_Library结构体:
        字体信息库.
    ASS_Renderer结构体:
        字幕渲染器,包含该帧的渲染信息.
  2. 读取字幕
    初始化完成了就可以开始读取ASS格式字幕了,LibASS很方便地把读取文件写在一个函数里面了,基本不用考虑文件编码问题,因为这个函数已经为我们做好转换的工作了.
    1.ass_read_file()函数:
        作用:读取ASS格式字幕到音轨库(ASS_Track)
        参数1:字幕库的指针 / ASS_Library*
        参数2:ASS字幕文件完整路径 / char*
        参数3:代码页 / char* (如果ASS文件不是Unicode编码的话,需要手动检测ANSI的代码页)
        返回值:音轨库的指针 / ASS_Track*
    ASS_Track结构体:
        与其叫音轨库,我觉得叫字幕事件库更合适,包含一个曲目的字幕事件.
  3. 渲染字幕
        万事具备,在上面函数调用完后,LibASS有足够的信息去渲染字幕了.
    1.ass_render_frame()函数:
        作用:生成某帧的字符位图库(ASS_Image).
        参数1:渲染器的指针 / ASS_Renderer*
        参数2:音轨库的指针 / ASS_Track*
        参数3:需要渲染帧的时间(单位:毫秒) / long long
        参数4:检测前后两帧是否相同的标识 / int*
        返回值:当前帧的字符位图库的指针 / ASS_Image*
        *注意这个函数,每次调用会把上一次生成的ASS_Image释放
    2.gen_image()函数:
        这个函数其实不是LibASS的函数,是test.c的函数例子.
        作用:生成一张自定义大小的空自定义位图(image_t),且为其分配内存空间.
        参数1:输出位图的宽 / int
        参数2:输出位图的长 / int
        返回值:自定义位图的指针 / image_t*
    3.blend()函数:
        这个函数也不是LibASS的函数,且循环调用blend_single()函数.
        作用:把参数2中字符位图库的位图混合到参数1的自定义位图中.
        参数1:自定义位图的指针 / image_t*
        参数2:字符位图库的指针 / ASS_Image*
        返回值:无
  4. 清理工作
        至此,已经完成对1帧字幕的渲染,得到了一幅自定义格式的位图了.如果不需要再渲染其他帧,则需要对LibASS进行清理.
    1.ass_free_track()函数:
        参数:音轨库的指针 / ASS_Track*
    2.ass_renderer_done()函数:
        参数:渲染器的指针 / ASS_Renderer*
    3.ass_library_done()函数:
        参数:字幕库的指针 / ASS_Library*

    至此完成对LibASS的使用,生成的位图(Bitmap)可以按照自己需要再加工成别的图片格式,详情可以查看其他开源图片格式的开发文档,这里不再叙述.
    需要特别注意的是,如果要获得可以直接让视频接口支持的位图,还需要自行了解接口的SDK说明~

Remark

这里说明一下使用LibASS要注意的地方.

  1. 关于ASS_Image的数据结构
    结构体定义:
        w:字符位图的宽 / int
        h:字符位图的长 / int
        stride:字符位图的块大小 / int
        bitmap:字符位图的数据 / unsigned char*
        color:字符位图的颜色 / unsigned
        dst_x:字符位图在自定义位图的x坐标 / int
        dst_y:字符位图在自定义位图的y坐标 / int
        next:下一张字符位图的指针 / ASS_Image*
    说明:
    Stride是一幅位图的块大小,在很多情况都需要用到,计算公式如下:
        Stride = 每像素字节数 * 位图宽度
    ASS_Image定义的是单色位图,故每像素字节数为1,则这里Stride = 位图宽度.可得Bitmap的总Buffer Size:
        Size = Stride * 位图高度,代入上式:
        Size = 位图宽度 * 位图高度 (单位:Byte)
    在这里,Bitmap的数据用于表示每像素的灰度(或Alpha值).
  2. 关于image_t的数据结构
    结构体定义
        width:输出位图的长 / int
        height:输出位图的宽 / int
        stride:位图块大小 / int
        buffer:位图数据 / unsigned char*
    说明:
    这个结构体并不是LibASS的数据结构,是test.c的自定义位图格式.
    这里的Stride依照需求,取值会不同,公式仍然和上一样.如果要获得标准RGB(24Bit / 3Byte)位图,则这里的位图块大小:
        Stride = 3 * 位图宽度
    如果想要获得ARGB(32Bit / 4Byte)的位图的话,则位图块大小:
        Stride = 4 * 位图宽度
    总位图Buffer Size计算方式同上.
  3. 关于位图数据(Bitmap Buffer)的数据结构
    按照标准Windows Bitmap定义,(24bit / 32bit)位图每像素信息储存方式:
    0xBBGGRR(AA)
    如果是2Bit – 16Bit的位图,储存将按照颜色表(Color Pattern)方式存储,这里不在讨论.

后记

    LibASS一直作为视频播放器内部使用的开源库,以其轻便高效的特点,完全没有压力地占领了大部分视频播放器对ASS / SSA的支持方式.
虽然官方没有给出完成的SDK文档,但是我觉得源代码 / 头文件给出的注释已经很给力了,我写这篇东西貌似有点多此一举?!

  1. 挺有用的,感谢。
    菜鸡不太熟 C。
    最近要用 Electron.js 做一个类似 Aegisub (功能) 的编辑器。
    所以在网上查相关文章

  1. トラックバック 0



return top