剪映专业版for Mac导出.srt及.fcpxml字幕

剪映专业版for Mac导出.srt及.fcpxml字幕

剪映专业版for Mac导出.srt及.fcpxml字幕

剪映工程数据文件分析

工程数据保存路径

无论是Mac还是Win,工程数据都保存在以下目录中(每个工程都是一个文件夹,文件夹名就是工程名)

JianyingPro/User Data/Projects/com.lveditor.draft/

但是“JianyingPro”目录在哪儿,就有点不一样了

# macOS
homeDir + "/Movies/"

# Windows
homeDir + "/AppData/Local/"

对于macOS,工程数据保存在工程文件夹下的draft_info.json文件中,Windows应该是在工程文件夹下的draft_content.json文件中,但由于这个并不确定,所以我们最好不直接写死文件名,而是从com.lveditor.draft/文件夹下的root_meta_info.json中获取(该文件下具有所有项目的信息),旧版剪映该文件名叫root_draft_meta_info.json

工程数据json

以下为draft_info.json的结构(我只列出第一层键值以及所需数据)

{
    "canvas_config": {},
    "color_space": 0,
    "config": {},
    "conver": null,
    "create_time": 0,
    "duration": 2360833333,
    "extra_info": null,
    "fps": 30,
    "id": "84558384-1857-4B67-94B4-0CE425440D42",
    "keyframes": {},
    "last_modified_platform": {},
    "materials": {
        "texts": [
            {
                "content":"哎我现在再去另一个桌面看一下啊"
            }
        ]
    },
    "mutable_config": null,
    "name": "",
    "new_version": "35.0.0",
    "platform": {},
    "relationships": [],
    "tracks": [
        {
            segments: [
                {
                    "target_timerange": {
                        "duration": 260166666,
                        "start": 74000000
                    }
                }
            ]
        }
    ],
    "update_time": 0,
    "version": 340000
}

可以看到,materials→texts里保存的是字幕信息,而tracks→segments里保存的是时间信息(时间属于轨道不属性字幕本身,所以时间是保存在轨道里的)。

我一个39分21秒时长的视频,draft_info.json文件按json格式显示,共有104862行(差不多10万5千行)。

字幕数据json

字幕是存储在materials→texts下,texts是一个数组,里面其中一个元素是一个对象,数据如下

{
    "alignment": 1,
    "background_alpha": 1.0,
    "background_color": "",
    "bold_width": 0.0,
    "border_color": "#ffffff",
    "border_width": 0.08,
    "content": "哎我现在再去另一个桌面看一下啊",
    "font_id": "",
    "font_name": "",
    "font_path": "/Applications/VideoFusion-macOS.app/Contents/Resources/Font/SystemFont/zh-hans.ttf",
    "font_resource_id": "",
    "font_size": 5.0,
    "font_title": "系统",
    "font_url": "",
    "has_shadow": false,
    "id": "2A535D84-73D8-4CD1-9C21-515A7F990D2E",
    "initial_scale": 1.0,
    "italic_degree": 0,
    "ktv_color": "",
    "layer_weight": 0,
    "letter_spacing": 0.0,
    "line_spacing": 0.02,
    "shadow_alpha": 0.8,
    "shadow_angle": -45.0,
    "shadow_color": "",
    "shadow_distance": 8.0,
    "shadow_point": {
        "x": 1.0182337649086284,
        "y": -1.0182337649086284
    },
    "shadow_smoothing": 0.99,
    "shape_clip_x": false,
    "shape_clip_y": false,
    "style_name": "黑字白边",
    "sub_type": 0,
    "text_alpha": 1.0,
    "text_color": "#000000",
    "text_to_audio_ids": [],
    "type": "subtitle",
    "typesetting": 0,
    "underline": false,
    "underline_offset": 0.22,
    "underline_width": 0.05,
    "use_effect_default_color": false
}

可以看到,每一句字幕,除了文字本身外,都记录了它的颜色、字体、样式、透明度、位置、对齐方式等等,其中文字的键是content,不过没有该字幕对应的时间信息,因为时间信息属于轨道,它是保存在轨道中的。

border_width 文字描边宽度,与界面上显示的数值为0.002的关系,即保存的数值除以0.002才是界面上的数值,比如保存的border_width是0.08,其实界面上数值是0.08/0.002=40;测试了另一个数据,界面显示38,查看json保存的数值是0.076,0.076/0.002刚好是38,所以这个规则是没错的。
border_color 描边颜色,为标准的十六进制颜色码,即#号开头的6位十六进制数,如#ffffff
text_alpha 文字透明度,为0-1之间的数0完全透明,1完全不透明;
text_color 文字颜色,为标准的十六进制颜色码,即#开头的6位十六进制数,如#000000

剪映字体选择“系统”

样式选预设样式中的黑字白边

字幕位置为自动识别字幕后的默认位置,即(0,-788)

Final Cut Pro选择PingFang SC Medium,字体大小55.5

文字颜色黑色,描边为白色,宽度为3

位置y轴设置为-414.83px

这样,Final Cut Pro中设置出来的字幕大小和样式,包括字体,几乎是完全重合的。

轨道数据json

之所以要分析轨道信息json,是因为字幕也是在轨道里的,而时间信息属于轨道,所以字幕对应的时间信息其实是在轨道里的(而不是在字幕里)。

轨道信息是在tracks键中,tracks键对应的值是一个数组,它的其中一个元素如下:

{
    "flag": 0,
    "id": "BE3366EB-C5AF-4913-90AA-E20CB33F172E",
    "segments": [
        ……
    ],
    "type": "text",
}

轨道可能有多条,它可能是视频、音频、文本(即字幕),而且视频本身也可能有多条轨、音频也有可能有多条轨、文本(字幕)也有可能有多条轨。

每条轨道都有一种类型,视频轨的type是video,音频我没测试,但肯定是audio,字幕轨的type是text,由于我们只是导出字幕,所以我们只要过滤出type为text的轨道即可。

tracks数组中,从上往下,分别为1轨、2轨、……,每个"type": "text"轨道对应的segments数组就是该轨道内的字幕片段。

需要特别注意的是,每组"type": "text"在数组中的序号并不能作为轨道号,因为type还有其它类型,比如"type": "video"一般排在第一位(而且如果两个video轨道,可能两个都会排在前面),这时"type": "text"在数组中的序号已经是第三位,但其实它属于第一轨,所以我们需要自己搞一个变量来自增就行,不要用数组的索引作为轨道号。

轨道里的片段数据json

每条轨道都有一个segments数组,该数组的每个元素都是一个片段(如一段视频,一段音频,一段字幕),segments数组的其中一个元素(即一个片段)如下

{
    "cartoon": false,
    "clip": {
        "alpha": 1.0,
        "flip": {
            "horizontal": false,
            "vertical": false
        },
        "rotation": 0.0,
        "scale": {
            "x": 1.0,
            "y": 1.0
        },
        "transform": {
            "x": 0.0,
            "y": -0.73
        }
    },
    "enable_adjust": true,
    "enable_lut": true,
    "extra_material_refs": [
        "7D4E7F05-4B4C-4124-9BEB-01F50EE6A466"
    ],
    "id": "723095B4-D057-4856-9CF7-83F5F6F13AFA",
    "intensifies_audio": false,
    "is_tone_modify": false,
    "keyframe_refs": [],
    "last_nonzero_volume": 1.0,
    "material_id": "2A535D84-73D8-4CD1-9C21-515A7F990D2E",
    "render_index": 15028,
    "reverse": false,
    "source_timerange": null,
    "speed": 1.0,
    "target_timerange": {
        "duration": 2100000,
        "start": 2093933333
    },
    "volume": 1.0
}

其中material_id正是对应materials→texts里的id,另外target_timerange键有两个值,一个是start,另一个是duration,分别表示,该字幕从哪个时间开始,以及持续多长时间,单位都是微秒,即startduration的值你要除以100万后才是秒(除以1000后是毫秒),这个就是我们最终需要的数据,只要把它跟texts里的文本相对应,就能组装出每段字幕的开始时间、持续时间以及字幕内容这样的数组。

注意,material_id有时候并不对应materials→texts里的id,而是对应text_templates里的某个元素的id,text_templatestexts是同级的,text_templates里的id是模板id,表示这个字幕是模板字幕,因为模板字幕里不同文字可能样式不同,而样式不同就需要分开来写,所以遇到这种情况我们就不能用material_id,而是用extra_material_refs,它里有可能有多个id,这多个id才是对应materials→texts里的id

片段里的clip数据json

clip主要用于保存字幕的透明度和位置信息

"clip": {
    "alpha": 1.0,
    "flip": {
        "horizontal": false,
        "vertical": false
    },
    "rotation": 0.0,
    "scale": {
        "x": 1.0,
        "y": 1.0
    },
    "transform": {
        "x": 1.0,
        "y": -1.0
    }
}
  • alpha:透明度,1.0完全不透明,0完全透明,透明度是0-1.0之间的小数;
  • flip:翻转,用于设置水平翻转和垂直翻转,注意,翻转和旋转是不一样的,比如一个平静的湖面,你看到的树的倒影就是水平翻转,而旋转180度看上去是倒过来了,但是左边的文字却旋转到了右边,但翻转的话,文字还是在原来位置,翻转也可以叫镜像,目前在剪映macOS版v2.1.0版中,虽然配置文件有这个参数,但是界面上并没有相关选项,应该是等待后面慢慢增加吧;
  • rotation:旋转,用于设置旋转角度;
  • scale:放大缩小文字,剪映的数据文件中保存的font_size为5.0,而界面上并没有设置字体大小的参数,目前是通过这个scale放大缩小来完成字体大小设置,保存的数字是界面上的数字除以100(毕竟界面上的数字是有%号的,实际上就是要除以100);
  • transform:位置,x为横坐标,y为纵坐标,由于不同分辨率的坐标值是不一样的,所以记录数据时,不记录具体的坐标值,而是记录坐标比例。对苹果电脑来说,由于是Retina屏幕,它有虚拟像素的说法,即把4个物理像素认为是一个逻辑像素,这样显示会更加精细,比如以我的MacBook Pro 15寸 2015年中版来说,我的屏幕分辨率是1440×900,然而它实际物理像素点却是2880×1800,也就是宽高各乘以2。所以,一个1920×1080的视频,我导入到我电脑上的剪映里面,它真实像素其实是宽高各乘以2,也就是3840×2160,那么,以画面正中心为原点,即(0,0)点,x轴往左为负,右为正,也就是3840分为两个1920,向左到最左边为-1920,向右到最右边为+1920,注意这个最左边和最右边,是指文字整体x轴长度的中心点与最左边或最右边的边重合,y轴方向同理,向上是+1080,向下是-1080。而transform的(x,y),记录的是文字位置与宽高的比值,比如实际坐标(1920,1080)会记录成(1920/1920,1080/1080),也就是(1.0,1.0),大于宽度或高度的,就是大于1,否则就是在0和1之间,保留17位小数,这也是够精确的,不过保存的数字才是最准确的,显示出来的只显示整数,比如对于1920×1080的视频来说,剪映识别字幕后,默认字幕位置是(0,-788),而根据保存的数据,是0.73,0.73×1080=-788.4,也就是你在剪映界面里看到的数,其实是小数点后面的数被四舍五入过的数。

剪映字体分析

以下剪映字体分析基于v2.6.0-beta5

剪映中自带4种字体,分别为:系统、后现代体、新青年体、悠然体(如下图)

这些字体存储在剪映app里面

# 剪映app字体存储目录
/Applications/VideoFusion-macOS.app/Contents/Resources/Font/

其中“系统”字体存储在以下目录,它是指“剪映”本身的“系统”字体,而不是你的macOS的系统字体,其实就是默认字体的意思,并且这个“系统”并不是一个字体的名称,而是有好多种字体,为什么呢?因为你的字幕可能是简体中文、繁体中文、英文、日文、韩文、泰文等等,它会根据你字幕的语言,使用对应的语言的字幕,目前只支持:简中、繁中、英、日、韩、泰共6种字体

/Applications/VideoFusion-macOS.app/Contents/Resources/Font/SystemFont/

剪映自带的默认字体如下图所示

如下图,当我们在剪映中添加一个默认文本时,使用的就是“系统”字体,当你的字幕文字为简体中文时,它就会使用zh-hans.ttf,字幕为英文就会使用en.ttf,同理,字幕为日文、韩文、泰文,它就会对应使用ja.ttf、ko.ttf、th.ttf

另外就是自带三种简体中文字体,分别是:后现代体、悠然体、新青年体,它们是直接在剪映app的字体目录中

/Applications/VideoFusion-macOS.app/Contents/Resources/Font/

其它字体都是要在线下载的,app本身不自带(字体旁边带个下载箭头,如果没有箭头,说明你已经下载过),下载后会存储在以下目录中,但它是藏在两层目录里的

~/Movies/JianyingPro/User Data/Cache/effect/

下图是“大字报”字体和“萌趣体”,可见字体名称并不一定刚好是字体名称的拼音,比如“萌趣体”它拼音是luoli(其实就是“萝丽”)

但没必要这样找这些字体,因为你设置字体后,它的位置会在draft_info.json中记录,每段文字都有分别记录(因为每段文字可能都用不一样的字体)。

check_flag值分析

texts数组中每一个元素都有一个check_flag属性,该属性不同值表示勾选了一个或多个属性

阴影 边框 描边 混合 位置大小 排列 check_flag
0 0 0 0 0 0 0
0 0 0 0 0 1 1(21-1)
0 0 0 0 1 1 3(22-1)
0 0 0 1 1 1 7(23-1)
0 0 1 1 1 1 15(24-1)
0 1 1 1 1 1 31(25-1)
1 1 1 1 1 1 63(26-1)
0 1 0 0 0 0 16
0 1 0 0 0 1 17
0 1 0 0 1 0 18
0 1 0 0 1 1 19
0 1 0 0 0 0 20

srt字幕分析

srt字幕格式

srt字幕格式中文描述(–>两端各有一个空格)

字幕序号(正整数,一般从1开始)
开始时间 --> 结束时间
字幕内容(英文)
字幕内容(中文)
空白行(表示本字幕段的结束)

开始时间和结束时间格式:时时:分分:秒秒,毫毫毫(分隔毫秒的符号也可以是点号:时时:分分:秒秒.毫毫毫,不过貌似都是用逗号的多),由于1s=1000ms,所以毫毫毫取值范围是0-999,因为到了1000就进位了。

字幕内容可多行,一般典型的是两行,一行中文一行英文,当然遇到古诗可能直接多行。

srt字幕实例

实际例子如下

1
00:00:58,300 --> 00:01:04,240
福爾摩斯二世

2
00:01:51,350 --> 00:01:55,390
有一句古老的諺語是這樣說的

3
00:01:55,390 --> 00:02:00,990
不要嘗試同時做兩件事情  並期望都能做好

4
00:02:01,686 --> 00:02:06,896
這是個想同時做好兩件事的男孩的故事

5
00:02:06,984 --> 00:02:17,334
他在小鎮的劇院裡當電影放映員的同時

6
00:02:17,365 --> 00:02:23,085
還在學習如何成為一個偵探

.fcpxml v1.9分析

本文基于Final Cut Pro 10.5.2及Final Cut Pro 10.6.1。

导出fcpxml

要分析.fcpxml文件,首先需要导出一些文件才能分析。

在任意资源库→任意事件下,新建一个工程,如下图所示

注意,不同帧率的fcpxml文件,它里面的数据是不同的(当然结构是相同的),我是把8种帧率的全部都创建了一遍,并且全部在里面创建了字幕,当然其实我是先创建一个,调好字幕后,把字幕复制到所有其它项目里(注意粘贴的时候那个时间指针要在最前面)

然后就可以点击文件导出XML...即可导出.fcpxml文件,可以导出1.8和1.9版本,我导出的是1.9版本(MetaData View是General),不过我感觉跟1.8没区别(可能我的项目只是测试比较简单的原因吧),我把8种.fcpxml文件都导出后,就可以分析它们的区别了。

外层基础结构

导出的.fcpxml基础结构如下,主要内容是在library标签里面,当然resources标签里也有一些

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE fcpxml>

<fcpxml version="1.9">
    <resources>
        ……
    </resources>
    <library location="file:///Users/bruce/Movies/xxxx.fcpbundle/">
        ……
    </library>
</fcpxml>

resources标签结构

29.97p的

<resources>
    <format id="r1" name="FFVideoFormat1080p2997" frameDuration="1001/30000s" width="1920" height="1080" colorSpace="1-1-1 (Rec. 709)"/>
    <effect id="r2" name="Basic Title" uid=".../Titles.localized/Bumper:Opener.localized/Basic Title.localized/Basic Title.moti"/>
</resources>

30p的

<resources>
    <format id="r1" name="FFVideoFormat1080p30" frameDuration="100/3000s" width="1920" height="1080" colorSpace="1-1-1 (Rec. 709)"/>
    <effect id="r2" name="Basic Title" uid=".../Titles.localized/Bumper:Opener.localized/Basic Title.localized/Basic Title.moti"/>
</resources>

format是格式和effect是效果,它们都有id,分别是r1和r2,用r是因为它们都是resources,第一个字母就是r。

format其实是被sequence标签的format属性引用的,引用方式就是通过id,即format="r1",而effect是被title引用的,引用方式是ref="r2"

format的name,是FFVideoFormat+分辨率(如720p、1080p)+帧率(如30p、29.97p等),但有小数的,都要把小数点去掉,也就是29.97p会写成2997p,同理,23.98p就写成2398p。

library标签的结构

跟我之前讲过的一样,library(资源库)、event(事件)、project(项目)是包含的关系,即library下包含事件,事件下又包含项目。

而project下包含的sequence就是“序列”(在PR中有这个概念),sequence下的spine是脊柱的意思,在这里指骨干,意思是所有的东西都写在里面了。

spine下又有gap,真正的数据,其实都是写在gap里面的(事实上如果不是单纯字幕,而是字幕+视频,它有可能是没有gap标签,而是用其它标签代替的)

<library location="file:///Users/bruce/Movies/test.fcpbundle/">
    <event name="2021-10-01" uid="275C8680-DD70-4C68-89BD-4D03659D0223">
        <project name="test-30p" uid="F847E060-DB77-4F9E-AFBA-7C6B1A09E7D3" modDate="2021-10-03 19:50:08 +0800">
            <sequence duration="222400/3000s" format="r1" tcStart="0s" tcFormat="NDF" audioLayout="stereo" audioRate="48k">
                <spine>
                    <gap name="Gap" offset="0s" duration="222400/3000s" start="3600s">
                        ……
                    </gap>
                </spine>
            </sequence>
        </project>
    </event>
    <smart-collection name="Projects" match="all">
        <match-clip rule="is" type="project"/>
    </smart-collection>
    <smart-collection name="All Video" match="any">
        <match-media rule="is" type="videoOnly"/>
        <match-media rule="is" type="videoWithAudio"/>
    </smart-collection>
    <smart-collection name="Audio Only" match="all">
        <match-media rule="is" type="audioOnly"/>
    </smart-collection>
    <smart-collection name="Stills" match="all">
        <match-media rule="is" type="stills"/>
    </smart-collection>
    <smart-collection name="Favorites" match="all">
        <match-ratings value="favorites"/>
    </smart-collection>
    <smart-collection name="个人收藏" match="all">
        <match-ratings value="favorites"/>
    </smart-collection>
    <smart-collection name="静止图像" match="all">
        <match-media rule="is" type="stills"/>
    </smart-collection>
    <smart-collection name="所有视频" match="any">
        <match-media rule="is" type="videoOnly"/>
        <match-media rule="is" type="videoWithAudio"/>
    </smart-collection>
    <smart-collection name="仅音频" match="all">
        <match-media rule="is" type="audioOnly"/>
    </smart-collection>
    <smart-collection name="项目" match="all">
        <match-clip rule="is" type="project"/>
    </smart-collection>
</library>

这里event和project都有name属性,这两个name其实就是最终你在FCP里导入fcpxml文件后,自动生成的事件和项目的名称,所以这两个值我们自己指定就可以,你想让你的fcpxml导入后生成什么名字的事件和什么名字的项目,你自己决定,当然我是取了剪映里的项目名作为项目名。

也就是说,你导入字幕并不是直接导入到某个项目里面的,而是它会自动新建一个事件,再在该事件下新建一个项目,最后你需要从这个项目里复制这些字幕再粘贴到你要添加字幕的项目里才算是真正“导入”了字幕。

另外就是sequence和gap标签都有一个duration属性,这两个值是相同的,在我这里是duration="222400/3000s",duration是“持续时间”的意思,这里的duration表示整个字幕的时间,其实就是最后那个字幕的时间,注意,如果字幕有多个轨道,最后一个字幕未必是在最后一条轨道,所以它在texts里的排序未必是排在最后的。

比如下图中,“这是测试5”这个字幕的结束时间,其实就是duration值,也就是说,我们需要从剪映中获取到这个字幕的结束时间,并把它用于duration值,其实方法很简单,在循环轨道和片段的时候,把所有字幕的结束时间都放进一个数组里,排序,最大那个就是我们需要的

当然,duration是用分数形式表示的,因为无法整除,所以只能用分数形式表示,事实上,fcpxml里所有表示时间的属性,基本上都是用分数表示的,除非真的能整除,只要带小数,那就不要写成小数,一律用分数表示。

还有就是modDate,修改时间,这个我也是直接用当前时间,当然那个+08时区的我就没管它,因为剪映本来就是中国产品,我直接就定死了东八区。

而与event(事件)同级的那些smart-collection就是fcp左侧的那些智能集合(如下图所示)
Xnip2021-09-30_15-36-17.jpg

gap标签结构

gap标签下有多个title标签,分别表示多个字幕片段,我这里只列出前两个title标签

<gap name="Gap" offset="0s" duration="2693691/30000s" start="107999892/30000s">
    <title name="欢迎大家观看我的 macOS 系列 - Basic Title" lane="1" offset="10803600/3000s" ref="r2" duration="6800/3000s" start="3600s">
        <param name="Position" key="9999/999166631/999166633/1/100/101" value="6.0188 -468.581"/>
        <param name="Flatten" key="9999/999166631/999166633/2/351" value="1"/>
        <param name="Alignment" key="9999/999166631/999166633/2/354/999169573/401" value="1 (Center)"/>
        <param name="Size" key="9999/999166631/999166633/5/999166635/3" value="55.5"/>
        <text>
            <text-style ref="ts2">欢迎大家观看我的 macOS 系列</text-style>
        </text>
        <text-style-def id="ts2">
            <text-style font="PingFang SC" fontSize="55.5" fontFace="Medium" fontColor="0 0 0 1" strokeColor="1 0.999974 0.999991 1" strokeWidth="4" kerning="6.3" alignment="center"/>
        </text-style-def>
    </title>
<gap>    

剪映的字幕数据,其实最终就是一段字幕一个title,最终就是拼出这些title,当然,其它部分也有一些数据需要填充。

首先我们来看一下gap标签的duration和start值(offset不用看,一定是0,因为我们从时间轴的0开始的)。

gap的duration就不用说了,前面library标签的结构里已经讲过了,而start值要讲讲,这个值一定是3600,至于为什么要从3600开始我也不知道,不过因为一些原因,它可能是一个无限接近3600的一个数,但不会超过3600,3600秒就是一小时,而之所以说无限接近3600,是因为它有可能有小数,比如我这个例子是29.97p的,它的值就是:107999892/30000=3599.9964,为什么不直接写3600呢?请先理解下边的一些视频概念

fontColor和strokeColor的参数顺序为(r g b a),即(红 绿 蓝 透明度),注意这个rgb是rgb值与255的比值,如果是0,那0/255还是0,如果是255,那255/255=1,也就是说,这个值是0-1之间的小数,要保留6位小数。

关于fcpxml的轨道

FCP中,每个title标签表示一段字幕,轨道在fcpxml中并没有像剪映那样分组,而是所有轨道的字幕都排在一起,按它们的起始时间排序,通过title中的lane属性来区分属于哪个轨道,无论是FCP还是剪映,都是靠近视频的位置为第一轨(即最下边的字幕轨为第一轨)。

对于FCP10.5.x和FCP10.6.x,每个segments为一条轨道,一条轨道上有多个片段(这多个片段的轨道号相同),但是对于FCP10.4.x,它每个片段占用一个轨道号(跟常识不符),所以对于FCP10.4.x来说,与其说是轨道,倒不如说是片段编号,编号从1开始,按每个片段的开始时间由小到大排序。

但是经常测试,10.4.x这种编号方法,在10.5.x和10.6.x里导入也能被识别,所以应该是做了向下兼容处理的。

一些视频概念

视频的本质:
视频本质是由一张张的图片拼成的,连续不断的播放图片,当每秒钟播放的图片数量够多时,由于人眼的视觉暂留,我们就会感觉图片里面的内容在动。这些一张一张的图片,在视频里有一个专业名词,叫“帧”,英文frame。

帧率
我们把每秒播放的图片张数(即帧数)叫帧率,英文framer,单位就是帧每秒,英文frame per second,简写成fps,当然更简单的写法是直接写成p,比如Final Cut Pro里新建项目,就会让你选择多少多秒p的帧率,注意这个p就是指fps,跟1080p那个p的意思完全不同,请不要混淆!1080p的p是指逐行扫描(prograssive的首字母,与此对应的是1080i,i是interlace,交错,隔行扫描,即显示了1 3 5行,再显示2 4 6行,而逐行扫描是直接1 2 3 4 5 6)。

帧时长
有些人可能会说,按这个说法,30p就是一秒钟播放30张图片,那29.97呢?一秒钟播放29.97张?这是不可能的呀,一张图片放出来了,那就整张图片显示在屏幕上了,你不可能把右边的一半在截掉对吧?其实这只不过是一个等效的说法,我们可以把它放大100倍,即100秒播放2997张,这样是不是就说的通了呢?是的,理论上就是这样的,这样的话,29.97p的视频,它每张图片的播放时间是100/2997,而30p的,每张图片的播放时间是1/30,这个100/2997和1/30就是每张图片在显示器上停留的时间了,我们把这个叫“帧时长”。

事实上,29.97p的视频,它的帧时长并不是用100/2997来表示的,而是用1001/30000来表示的,具体原因我也不太明白,但29.97帧其实是推理出来的,真正的来源是1001/30000,不过100/2997跟1001/30000在微秒级都是完全相同的数,只有在纳秒的时候,才有点差别,但对于视频来讲,到了微秒级已经完全够用了,不过有些在微秒的最后一位就不相同了,比如23.98p,但这不会影响实际使用,因为微秒的最后一位实在太短了。

100/2997s =   0.03336670003s
1001/30000s = 0.03336666667s

还有其它的23.98p、59.94p都是一个原理,它们的帧时长,它们的分母,都是帧率向上取整后,再乘以1000,而分子,都是1001,比如23.94向上取整是24,乘以1000就是24000,分子是1001,所以23.94p的帧时长为1001/24000,而59.94的帧时长为1001/60000。

解释无限接近3600秒的问题

好了,有了以上的知识我们已经可以解释前面留下的问题:为什么start有时候不直接写3600而是一个无限接近3600的小数?

因为时间必须停留在帧的头和尾(严格来说没有尾,因为一帧的尾部其实又是另一帧的头部),不能卡在帧的中间,因为播放视频的时候,播放到某张图片(也就是某一帧)时,它整张图片是一起出来的,不可能这张图片的左侧先出来,右侧后出来,所以整个帧的时间,其实就是这张图片(即这一帧)的头部那个时间。

这个值start="107999892/30000"是29.97p的参数,我们可以计算一下,这个时间有多少帧呢?是(107999892/30000)/(1001/30000)=107892帧,刚好整除,没有小数,因为一帧就是一帧,一张图片播放出来就是整张一起显示出来,不可能显示左侧的三分之一或者三分之二,所以帧数是不可能有小数的,如果我们直接写成3600,我们来除一下:3600/(1001/30000)=107892.1078921079,看,这就有小数了,我已经说过,帧是不可能有小数的,后面这0.1帧是没有用的,因为时间位于帧头的时候,整个帧(整张图片)已经全部显示出来了,不可能只显示左侧的十分之一张图片。

特别注意:fcpxml里面,所有跟时间有关的数据,除以当前的帧时长,都必须得到一个整数才可以,否则FCP里导入这个fcpxml的时候就会报错,当然最终其实也是能导入的,就是它会自动帮你舍弃小数部分。

一般来说,所有涉及到时间的属性,它都用分数形式表示,而且分母就是帧时长的分母,比如对于30p帧率,分母就是3000,29.97p分母是30000,25p分母就是2500。

得到帧时长倍数的时间很简单,就是把你的时间除以帧时长,然后向下取整,再乘回帧时长,就会得到一个是帧时长倍数的时间,这个时间比你原来的时间会少一点点,少的那一点一定是不够一帧的时长的。

FCP文字位置分析

FCP修改字幕位置有两种方法:

  • 1、通过“文本检查器”→“位置”(直接拖动字幕其实就是修改这个位置的值)
  • 2、通过“视频检查器”→“变换”→“位置”(右击字幕→变换→再拖动字幕 修改的就是这个值)

两种方法如下图
Xnip2021-11-25_17-56-35_1

下图是1080×1920的竖屏视频,文字在里面的位置用x、y坐标表示,坐标原点为视频中心,x轴原点左侧为负右侧为正,y轴原点下侧为负上侧为正(如下图)
1920x1080_portrait_half

“视频检查器”中,文字的位置符合常识,比如上图中的1080×1920分辨率的视频,横向有1080个像素,从中间分开,那么从原点到最左边有540个像素,由于在原点左边为负,所以左边的边框位置x坐标为-540,同理,右边框x坐标为540,下边框y坐标为-960(即1920的一半),上边框y坐标为960。

“文本检查器”中的位置就不一定符合我们的常识,准确的说,只有高为1080的视频,才会符合常识,比如一个1920×1080的视频,那么你把文字的x设置为960,文字的原点确实就是在视频的右边框上,-960文字原点就在左边框,540就在上边框,-540就在下边框。

然而,一旦你的视频高度不是1080,比如是1080×1920,这样高度是1920,它就完全对不上了。

y设置960按道理是会把文字设置到上边框的,但实际上它会飞出文字区域

y设置-960也会飞出下边框

x设置-540,按道理字幕应该在左边框的,但实际上已经飞出视频区域很远

同理,x设置为540也会飞出右边框

这到底是什么原因呢?答案来了,坑爹的FCP,这个字幕位置它竟然是默认把视频高度按1080算的,无论你是什么分辨率的视频,它都会把你视频的高认为是1080,宽按比例缩放,你可以尝试在任何分辨率的视频里添加一个字幕,然后把它的文本检查器→位置里的y值设置为540,字幕一定是在上边框,y设置为-540,字幕一定是在下边框。

而视频的宽(虚拟宽)就要按比例算了,由于是等比例缩放,高被强行缩放为1080,那么宽也是被等比例缩放,所以缩放后的宽跟原宽的比值,肯定等于缩放后的高与原高的比值

 虚拟宽     1080
------- = ------
 原宽       原高

 虚报宽 = 原宽x(1080/原高)
 虚拟横坐标 = 原横坐标x(1080/原高) (把原宽换成原横坐标是一个道理)
 虚拟纵坐标 = 原纵坐标x(1080/原高)

 所以虚拟的坐标,其实只要把原坐标乘以一个比值就好了,这个比值就是强行缩放后的高与原高的比值。

这样我们可以计算出1080×1920分辨率视频的字幕位置虚拟宽为:1080x(1080/1920)=607.5,分成左右两半,607.5/2=303.75,所以,我们设置x为-303.75,那么文字就会在左边框上,x为303.75就会在右边框上,而上下边框就不用看了,一定是540和-540,因为我前面说过,FCP在字幕位置上,会把任何分辨率的视频都按高为1080算。

1080×1920视频,x为-303.75px时,文字位于左边框

1080×1920视频,x为303.75px时,文字位于右边框
Xnip2021-11-25_19-40-09

y=540

y=-540

这里要说一下,文字的坐标,其实是指文字中心点的坐标,但是在FCP里,文字中心点在y轴方向其实并不是在中心,而是在中心偏下位置,这个位置是文字的基线(自己去查一下什么是基线)

由于这个原因,y=540的时候,你会发现文字几乎看不见了(大部分都在视频外了),就是因为它的“中心点”靠近文字底部的原因。

fcpxml 1.10分析

基于Final Cut Pro 10.16.1分析。

对于字幕来说,这个版本完全没改,只不过它把原来的.fcpxml文件命名为Info.fcpxml,并把它放到一个文件夹里,文件夹后缀为.fcpxmld(多了个d,d就是directory的意思),但是苹果系统并不会把它识别为文件夹,而是识别为一个文件。

所以,1.10版本表面上看就是后缀变成了.fcpxmld,实际上就是把1.9的fcpxml文件重命名为Info.fcpxml后,放到这个.fcpxmld文件夹里面而已,你可以对它右击→显示包内容就能看到。

细微区别:在Final Cut Pro 10.15.2中描边设置为3时,在fcpxml里用strokeWidth="3"表示,但在Final Cut Pro 10.16.1中,描边设置为3时,实际上在fcpxml里用strokeWidth="-3"表示,不知道为什么突然要用负数表示,完全没道理,但必须这样写,否则会导致文字外观不会被勾上。

自已写转换小工具

简介

有了以上的理论,我相信只要是会写代码的人,都能把这个转换工具给写出来了,毕竟我已经说的那么详细那么明白了,如果哪里还有不明白的可以评论跟我说一下(评论邮箱一定要真实邮箱,这样我回复你的时候,你是会收到邮件提醒的,否则收不到邮件提醒,你可能不知道我已经回复了,你总不可能隔一会儿去看一下吧?)。

目前网上能找到的工具都是二次转换的,也就是要先用一种工具导出.srt,再用另一种工具把.srt转成.fcpxml,而我是一步到位,不仅能直接把剪映项目的字幕直接转成.fcpxml,我还顺带转出一份.srt给你,用不到可以直接删掉,但万一你用的到呢?

安装我写的小工具

  • 1、下载并解压出来,解压出来是一个文件夹;
  • 2、无需安装,双击打开对应版本即可使用,详细使用请看里面的“使用说明”。

Final Cut Pro导入字幕

剪映识别字幕

首先把你要添加字幕的视频添加到剪映,点击:文本→智能字幕→开始识别,稍等一会儿,就会自动识别出字幕,当然如果你是唱歌视频,也可以点“识别歌词”
Xnip2021-10-07_01-10-41.jpg

识别完字幕之后,我建议直接在剪映里修改字幕错误,毕竟是语音识别,肯定有些地方是不对的,当然你也可以直接导出.fcpxml,然后导入Final Cut Pro里后再来修改,但是我发现在剪映里调整特别流畅,而在Final Cut Pro里特别卡,所以我还是建议在剪映里调整好,当然这个调整主要是指文字识别错误的修正以及字幕时间的修正。

导出.fcpxml文件

在剪映里调整好字幕之后,打开终端工具(启动台搜索“终端”),然后输入getfcpxml,按回车,即可进入导出程序,按提示即可导出,如果你在Final Cut Pro中的视频项目是30fps帧率的,那一直按回车就行,如果不是30fps的,也只需要选择一次帧率,其它地方都是按回车就行。

导出字幕过程截图
image.jpg

导入.fcpxml字幕

在Final Cut Pro中点击:文件(File)→导入(Import)→XML…

选择前面导出的.fcpxml文件进行导入,导入之后,它会自动生成一个名为“导入字幕事件”的事件,在该事件下会自动创建一个项目,该项目的名称就是你剪映中的项目名称,双击该项目,或者右击项目→点击“打开项目”(Open Project)来打开该字幕项目

打开字幕项目之后,随便点击一下时间线,然后cmd+A全选,再按cmd+C复制
image.jpg

注意:复制的时候,一定要同时选中下边的灰色条,cmd+A就能全选,千万不要认为这个灰色的条没用,就不复制它
image.jpg
因为如果不复制它,那么你的字幕将会顶到时间轴指针上(由于我们把时间轴指针放在时间轴开头,所以实际上你的字幕开头将会顶到视频的起始时间,也就是00:00:00.000处,然而这在大多数时候都是不对的,因为不可能我刚按下录制视频按键,我就开始说话了,说话时间肯定会稍微迟一点,比如过个5秒才开始说话,很明显第一个字幕应该在视频开始播放5秒后才出现,而那个灰色条就是用来做这个定位的,所以一定不能去掉那个点位的灰色条。


然后再打开你要添加字幕的项目(注意它肯定是在另一个事件里,你要先点击一下那个事件才能看的到你的项目),把时间轴指针定位到时间轴最前面,然后cmd+V粘贴即可
image.jpg

注意:粘贴的时候,时间轴指针一定要在最前面,因为粘贴的位置是从时间轴指针的位置开始的,如果它不在最前面,到时字幕就不是从最前面开始,导致对不上视频。

粘贴完之后,用cmd+-缩小一下时间轴,可以看到最后有一个灰色条,这个条是复制字幕的时候带过来的,把它删掉即可(这个条有时候可能没有,如果有就删掉,没有就不用管)
image.jpg

批量设置样式

由于导入的字幕没有样式,虽然粘贴进来了,但是样式却都是默认样式,不过我们可以批量设置样式。

首先对某一段字幕进行设置样式,设置完之后(保持选中它),点击右上角的“文本检查器”(Text Inspector)→点击“保存所有格式和外观属性”→给你的自定义样式起一个名字→保存

当你把.fcpxml的字幕粘贴进来之后,点击时间轴左上角的“索引”(Index),然后点击下边的“字幕”(Titles)就可以筛选出全部字幕,然后随便选中其中一个字幕,再按cmd+A全选字幕,再点击右上角的文本检查器→选择刚才保存的字幕样式,就可以把字幕样式应用到所有字幕(位置是无法应用到所有字幕的,只有外观样式才可以)
16332821774077.jpg

删除自定义样式

右击程序坞中的访达→前面文件夹,然后会弹出窗口,你把下面的路径复制进去,回车就能打开文件夹

~/Library/Application Support/Motion/Library/Text Styles/

打开文件夹之后,你可以看到,每个保存的样式有三个文件,比如我保存的样式叫“测试1”,则这三个文件分别为

测试1.molo
测试1.png
测试1_menu.png

要删除这个自定义样式,直接把这三个都删掉就行,不过你选择的时候,它还是会有,属于缓存,选了它也不会管用,要关掉Final Cut Pro再打开,但是再看它还是有可能会有,如果还是有,你可以设置一下其它样式,它就会没有了。

详见:在 Final Cut Pro 中应用预置文本样式


参考:
视频格式比例等讲解(从00:31:08看起)
Creating FCPXML Documents

打赏
订阅评论
提醒
guest

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x

扫码在手机查看
iPhone请用自带相机扫
安卓用UC/QQ浏览器扫

剪映专业版for Mac导出.srt及.fcpxml字幕