首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >从技术视频到短视频:AI辅助剪辑全流程技术复盘

从技术视频到短视频:AI辅助剪辑全流程技术复盘

作者头像
heidsoft
发布2026-07-02 10:53:58
发布2026-07-02 10:53:58
400
举报

前言

2024年以来,抖音、视频号等技术知识类账号呈现爆发式增长。相比真人出镜,资料剪辑类视频(将技术大会演讲、课程视频精华片段重新剪辑包装)制作门槛更低、素材获取容易、且能充分发挥个人在特定领域的知识储备优势。

本文是我在制作技术知识类短视频过程中的一套端到端技术方案的完整复盘,涵盖:素材筛选 → 语音识别 → 字幕翻译 → 竖屏转制 → 字幕烧录 → 封面设计 → 视频合并的全部环节。本文不涉及任何本地路径和敏感信息,所有工具均为公开发布的成熟开源方案,代码可直接迁移使用。


一、整体技术架构

1.1 流程总览

代码语言:javascript
复制
原始视频 (横屏 16:9)
    │
    ▼
┌─────────────────────────────────┐
│  Step 1: 素材评估与片段选取     │
│  ffprobe 检测分辨率/时长/编码    │
└────────────────┬────────────────┘
                 │
    ┌────────────▼────────────┐
    │  Step 2: 语音识别        │
    │  faster-whisper (CPU)   │
    │  输出时间戳+原文         │
    └────────────┬────────────┘
                 │
    ┌────────────▼────────────┐
    │  Step 3: 字幕制作        │
    │  人工/AI翻译 + ASS时间轴  │
    └────────────┬────────────┘
                 │
    ┌────────────▼────────────┐
    │  Step 4: 竖屏转制        │
    │  ffmpeg scale+pad 滤镜  │
    │  16:9 → 9:16 加黑边     │
    └────────────┬────────────┘
                 │
    ┌────────────▼────────────┐
    │  Step 5: 字幕烧录        │
    │  PyAV + PIL 逐帧绘制    │
    │  字幕嵌入画面(非外挂)  │
    └────────────┬────────────┘
                 │
    ┌────────────▼────────────┐
    │  Step 6: 封面图制作      │
    │  PIL 绘制静态封面        │
    │  ffmpeg zoompan 动态化  │
    └────────────┬────────────┘
                 │
    ┌────────────▼────────────┐
    │  Step 7: 视频合并        │
    │  ffmpeg concat 协议      │
    │  片头 + 正片 + 音轨     │
    └────────────┬────────────┘
                 │
              成品 MP4
         (9:16 竖屏,带字幕)

1.2 工具选型说明

环节

工具

选择理由

视频信息分析

ffprobe (ffmpeg内置)

零依赖,解析元数据极快

视频转码/处理

ffmpeg 8.x

事实标准,滤镜丰富

语音识别

faster-whisper (OpenAI)

CPU推理,模型小,支持VAD

字幕烧录

PyAV + PIL

ffmpeg无libass/drawtext时的备选方案

封面绘制

PIL (Pillow)

Python图像处理标准库

动态封面

ffmpeg zoompan

Ken Burns效果生成


二、素材评估与片段选取

2.1 视频信息探测

在正式处理之前,先用 ffprobe 了解素材的技术参数,避免后续踩坑:

代码语言:javascript
复制
ffprobe -v quiet -show_entries \
  stream=codec_name,width,height,r_frame_rate \
  -of csv=p=0 input.mp4

输出示例:

代码语言:javascript
复制
h264,1280,720,25/1
aac,44100,2

关键字段含义:

  • codec_name=h264 — 视频编码,H264兼容性最好
  • width=1280, height=720 — 720p横屏,需转竖屏
  • r_frame_rate=25/1 — 25fps,后续合并时需统一

2.2 时长与内容预判

代码语言:javascript
复制
ffprobe -v quiet -show_entries \
  format=duration -of csv=p=0 input.mp4

对于技术类内容,建议按以下原则选段:

原始视频时长

建议切分方式

最终成品

<15分钟

整段使用

1个1-3分钟短片

15-45分钟

切3-5段精华

3-5个独立短片

>45分钟

按主题章节切分

系列内容

选段原则:

  • 完整性:选取概念自洽的独立段落(不选半中间截断的内容)
  • 信息密度:优先选讲原理、讲历史、讲架构的段落
  • 视觉辅助:优先选PPT+讲解的视频,避免纯文字墙
  • 时长控制:单条短片控制在60-180秒最佳

2.3 音频轨道检查

代码语言:javascript
复制
ffprobe -v quiet -show_entries \
  stream=codec_name,sample_rate,channels \
  -select_streams a:0 -of csv=p=0 input.mp4

确认有音频轨道(aac/mp3)后再进行语音识别。纯视频文件无法做字幕。


三、语音识别:faster-whisper

3.1 为什么用 faster-whisper

市面主流语音识别方案对比:

方案

精度

速度

部署难度

本地CPU

OpenAI Whisper API

依赖网络

faster-whisper

高(接近API)

快(int8加速)

Whisper.cpp

极快

讯飞/百度SDK

高(需注册)

faster-whisper 是 Whisper 的 CTranslate2 推理实现,在 CPU 上也能达到较快的转录速度,完全本地运行,无需联网,适合处理敏感技术内容。

3.2 安装

代码语言:javascript
复制
pip install faster-whisper

3.3 语音识别代码

代码语言:javascript
复制
import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"

from faster_whisper import WhisperModel

# 模型选择:tiny(39M) / base(74M) / small(244M) / medium(769M)
# CPU推理推荐 base 或 small,精度和速度平衡
model = WhisperModel("base", compute_type="int8", cpu_threads=4)

segments, info = model.transcribe(
    "input_audio.wav",
    language="en",           # 源语言
    vad_filter=True,         # 启用语音活动检测,过滤静音
    vad_parameters=dict(min_silence_duration_ms=500)
)

print(f"语言: {info.language}, 时长: {info.duration:.1f}s")
for seg in segments:
    print(f"[{seg.start:.1f}s -> {seg.end:.1f}s] {seg.text.strip()}")

参数说明

  • compute_type="int8" — 启用int8量化,大幅降低内存占用且几乎不损失精度
  • cpu_threads=4 — macOS M系列芯片需设置线程数避免OOM
  • vad_filter=True — 自动过滤纯静音片段,减少无意义字幕
  • language — 明确指定语言可提升准确率

3.4 音频提取

ffmpeg将视频中的音轨单独提取为wav(16kHz单声道为Whisper最优输入):

代码语言:javascript
复制
ffmpeg -y \
  -i input.mp4 \
  -vn -acodec pcm_s16le \
  -ar 16000 -ac 1 \
  -ss 0 -t 180 \
  audio_clip.wav

参数说明:

  • -vn — 不要视频
  • -acodec pcm_s16le — 无压缩音频,适合识别
  • -ar 16000 -ac 1 — 16kHz单声道,Whisper最佳输入格式
  • -ss -t — 时间范围,可只截取目标段落

3.5 识别结果处理

Whisper输出的是带时间戳的片段列表,需要进一步处理:

代码语言:javascript
复制
segments_en = [
    (18.8, 22.8, "BPF stands for Berkeley Packet Filter"),
    (22.8, 27.8, "It's a virtual machine that runs in the kernel"),
    # ...
]

def to_ass_time(seconds):
    """将秒数转为ASS时间格式 H:MM:SS.CS"""
    h = int(seconds // 3600)
    m = int((seconds % 3600) // 60)
    s = int(seconds % 60)
    cs = int((seconds % 1) * 100)
    return f"{h}:{m:02d}:{s:02d}.{cs:02d}"

四、字幕制作:ASS格式

4.1 为什么要用ASS格式

ASS(Advanced SubStation Alpha)是比SRT更强大的字幕格式,支持:

  • • 字体大小、颜色、粗细、描边
  • • 卡拉OK效果(逐字变色)
  • • 位置控制(任意坐标)
  • • 旋转、模糊等特效

对于竖屏视频,字幕通常放在画面底部居中,ASS能精确控制这一布局。

4.2 ASS文件结构

代码语言:javascript
复制
[Script Info]
Title: eBPF Subtitles
ScriptType: v4.00+

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,36,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,1,2,10,10,10,1

[Events]
Format: Layer, Start, End, Style, Text
Dialogue: 0,0:00:18.80,0:00:22.80,Default,,0,0,0,,BPF全称是Berkeley Packet Filter
Dialogue: 0,0:00:22.80,0:00:27.80,Default,,0,0,0,,它是一个运行在内核中的虚拟机

样式关键参数说明

参数

含义

Fontname

Arial

字体(建议使用系统内置字体)

Fontsize

36

字号,竖屏视频建议36-44

PrimaryColour

&H00FFFFFF

白色(字幕主体颜色)

OutlineColour

&H00000000

黑色(描边颜色)

Outline

2

描边宽度,2-3视觉效果好

Shadow

1

阴影偏移

Alignment

2

底部居中(竖屏视频推荐)

Alignment值含义

代码语言:javascript
复制
1=左下  2=中下  3=右下
4=左中  5=正中  6=右中
7=左上  8=中上  9=右上

竖屏视频字幕放底部居中,选 Alignment=2

4.3 翻译策略

对于技术类内容,翻译需要特别注意:

  1. 1. 术语一致性:建立术语表(如 BPF/eBPF/XDP/内核探针等统一译法)
  2. 2. 口语化处理:演讲内容口语化较重,翻译时适当调整为符合中文习惯的表达
  3. 3. 时间压缩:英语演讲通常语速较慢,翻译成中文后同等时间的文字量更少,无需额外压缩
  4. 4. 保留关键英文术语:常见缩写(CPU/GPU/API)保留英文,不翻译

五、竖屏转制:ffmpeg

5.1 核心思路

横屏(16:9)转竖屏(9:16)有三种常见做法:

方式

做法

优缺点

裁切(crop)

直接截取中间部分

信息损失,不推荐

缩放填充(scale+pad)

缩放后上下/左右加黑边

保留完整画面,推荐

变形拉伸

直接拉伸

严重失真,绝对禁止

5.2 scale+pad 加黑边方案

以1280x720(16:9)转1080x1920(9:16)为例:

代码语言:javascript
复制
原始: 1280 × 720
目标: 1080 × 1920

计算:
- 宽度缩放到1080,高度等比 → 1080 × 607
- 上下各加 (1920 - 607) / 2 = 656.5 像素的黑边

ffmpeg命令:

代码语言:javascript
复制
ffmpeg -y \
  -ss 18 -t 162 \
  -i input.mp4 \
  -vf "scale=1080:607:force_original_aspect_ratio=decrease,\
       pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black" \
  -c:v libx264 -preset fast -crf 23 \
  -r 25 \
  -an \
  portrait_video.mp4

参数详解

参数

说明

scale=1080:607:force_original_aspect_ratio=decrease

将宽度缩放到1080,高度按比例计算(不超出原比例)

pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black

在图像周围填充黑色区域至目标尺寸;(ow-iw)/2 正好居中

force_original_aspect_ratio=decrease

确保缩放后不超过原尺寸

-crf 23

质量参数,18-28之间推荐,23为平均质量与大小的平衡

-preset fast

编码速度与压缩率权衡:ultrafast → veryslow

-an

不要音频(后续单独处理)

5.3 音频轨道保留

如果竖屏视频不需要单独处理,可以直接保留音频:

代码语言:javascript
复制
ffmpeg -y \
  -ss 18 -t 162 \
  -i input.mp4 \
  -vf "scale=1080:607:force_original_aspect_ratio=decrease,\
       pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black" \
  -c:v libx264 -preset fast -crf 23 \
  -c:a aac -b:a 128k \
  -r 25 \
  portrait_with_audio.mp4

六、字幕烧录:PyAV + PIL

6.1 为什么不用ffmpeg直接烧录

大多数预编译的ffmpeg(如macOS Homebrew bottle版本)没有包含libass和drawtext滤镜:

  • libass — ASS/SSA字幕渲染滤镜
  • drawtext — 文字绘制滤镜

这两个滤镜是ffmpeg烧录字幕的标准方案。如果ffmpeg编译时没有启用,字幕就无法直接烧入画面。

检查方法

代码语言:javascript
复制
ffmpeg -filters 2>/dev/null | grep -E "subtitles|drawtext|ass"

无输出说明没有这两个滤镜。

6.2 PyAV 方案

PyAV是ffmpeg的Python绑定,提供了底层访问能力。结合PIL(图像处理),可以实现逐帧绘制字幕的效果。

安装

代码语言:javascript
复制
pip install av Pillow

核心流程

代码语言:javascript
复制
PyAV解码视频帧
    ↓
PIL Image 绘制字幕
    ↓
PyAV 编码为新视频

6.3 完整烧录代码

代码语言:javascript
复制
#!/usr/bin/env python3
import av
from PIL import Image, ImageDraw, ImageFont
from fractions import Fraction

# 字幕数据:(开始秒, 结束秒, 文本)
subtitles = [
    (18.8, 22.8, "BPF全称是Berkeley Packet Filter"),
    (22.8, 27.8, "它是一个运行在内核中的虚拟机"),
    # ...
]

defget_subtitle_at(time_sec):
    """根据当前时间返回字幕文本"""
    for start, end, text in subtitles:
        if start <= time_sec < end:
            return text
    returnNone

# 字体选择(按优先级尝试)
font = None
for fp in [
    "/System/Library/Fonts/STHeiti Light.ttc",   # macOS黑体
    "/System/Library/Fonts/PingFang.ttc",        # macOS苹方
    "/System/Library/Fonts/Hiragino Sans GB.ttc", # macOS冬青黑体
    "/Library/Fonts/Arial Unicode.ttf",           # 系统通用
]:
    if os.path.exists(fp):
        try:
            font = ImageFont.truetype(fp, 44)
            print(f"Font loaded: {fp}")
            break
        except Exception:
            pass

if font isNone:
    font = ImageFont.load_default()

# 视频参数
FPS = 25
WIDTH, HEIGHT = 1080, 1920
TEXT_MARGIN = 120   # 字幕距底部距离
SUB_BOX_PADDING = 20

defdraw_subtitle(draw, text, width, height):
    """在画面底部居中绘制字幕"""
    bbox = draw.textbbox((0, 0), text, font=font)
    text_w = bbox[2] - bbox[0]
    text_h = bbox[3] - bbox[1]
    
    # 计算字幕框尺寸
    box_w = text_w + SUB_BOX_PADDING * 2
    box_h = text_h + SUB_BOX_PADDING * 2 + 10
    box_x = (width - box_w) // 2
    box_y = height - TEXT_MARGIN - box_h
    
    # 圆角黑色半透明背景
    draw.rounded_rectangle(
        [box_x, box_y, box_x + box_w, box_y + box_h],
        radius=8, fill=(0, 0, 0, 200)
    )
    
    # 黑色描边(4个方向偏移)
    for dx in [-2, -1, 1, 2]:
        for dy in [-2, -1, 1, 2]:
            ifabs(dx) == abs(dy):  # 对角线方向
                draw.text(
                    (box_x + SUB_BOX_PADDING + dx, 
                     box_y + SUB_BOX_PADDING + dy),
                    text, font=font, fill=(0, 0, 0)
                )
    
    # 白色主体文字
    draw.text(
        (box_x + SUB_BOX_PADDING, box_y + SUB_BOX_PADDING),
        text, font=font, fill=(255, 255, 255)
    )

# 打开输入输出
in_container = av.open("input_portrait.mp4")
out_container = av.open("output_with_subs.mp4", 'w')

# 创建输出流
out_video = out_container.add_stream('libx264', rate=FPS)
out_video.width = WIDTH
out_video.height = HEIGHT
out_video.pix_fmt = 'yuv420p'
out_video.options = {'crf': '23', 'preset': 'fast'}

frame_idx = 0
frame_duration = 1.0 / FPS

# 逐帧处理
for packet in in_container.demux(video=0):
    if packet.dts isNone:
        continue
    for frame in packet.decode():
        current_time = frame_idx * frame_duration
        
        # 转PIL Image
        img = frame.to_image()
        
        # 绘制字幕
        sub = get_subtitle_at(current_time)
        if sub:
            draw = ImageDraw.Draw(img)
            draw_subtitle(draw, sub, WIDTH, HEIGHT)
        
        # 编码输出
        out_frame = av.VideoFrame.from_image(img)
        out_frame.pts = frame_idx
        out_frame.dts = frame_idx
        out_frame.time_base = Fraction(1, FPS)
        
        for p in out_video.encode(out_frame):
            out_container.mux(p)
        
        frame_idx += 1
        if frame_idx % 500 == 0:
            print(f"  Processed {frame_idx} frames...")

# flush编码器
for p in out_video.encode():
    out_container.mux(p)

out_container.close()
in_container.close()
print("Done!")

6.4 性能数据参考

实测数据(PyAV + PIL逐帧烧录):

  • • 分辨率:1080x1920
  • • 帧数:约4000帧(160秒视频)
  • • 处理时间:约30秒(macOS普通配置)
  • • 输出大小:约3MB(crf=23)

优化方向

  • • 降低字幕框刷新频率(只在字幕变化时重绘背景)
  • • 使用NumPy直接操作像素而非PIL(更底层,速度更快)
  • • 批量处理多段视频

七、封面图设计

7.1 竖屏封面尺寸

平台

推荐尺寸

宽高比

抖音

1080×1920

9:16

视频号

1080×1920

9:16

B站

1920×1080

16:9

技术知识类账号通常采用深色科技风封面,突出专业感。

7.2 PIL封面绘制代码

代码语言:javascript
复制
from PIL import Image, ImageDraw, ImageFont
import os

WIDTH, HEIGHT = 1080, 1920

# 创建深色背景
img = Image.new('RGB', (WIDTH, HEIGHT), color=(5, 8, 15))
draw = ImageDraw.Draw(img)

# 垂直渐变背景
for y inrange(HEIGHT):
    ratio = y / HEIGHT
    r = int(5 + ratio * 20)
    g = int(8 + ratio * 30)
    b = int(15 + ratio * 60)
    draw.line([(0, y), (WIDTH, y)], fill=(r, g, b))

# 装饰线条(顶部)
for i inrange(3):
    draw.rectangle(
        [50 - i*5, 200 - i*5, WIDTH - 50 + i*5, 205 - i*5],
        outline=(0, 150, 255, 100 - i * 25)
    )

# 主标题
font_title = ImageFont.truetype(
    "/System/Library/Fonts/STHeiti Light.ttc", 120
)
font_sub = ImageFont.truetype(
    "/System/Library/Fonts/STHeiti Light.ttc", 52
)
font_tag = ImageFont.truetype(
    "/System/Library/Fonts/STHeiti Light.ttc", 36
)

# 标题文字
title = "eBPF"
bbox = draw.textbbox((0, 0), title, font=font_title)
title_w = bbox[2] - bbox[0]
draw.text(((WIDTH - title_w) // 2, 400), title,
          font=font_title, fill=(0, 200, 255))

# 副标题
draw.text(((WIDTH - 240) // 2, 560), "内核黑科技",
          font=font_sub, fill=(255, 255, 255))

draw.text(((WIDTH - 420) // 2, 640), "从BPF到eBPF的演进史",
          font=font_sub, fill=(200, 200, 210))

# 分割线
draw.line([(200, 750), (WIDTH - 200, 750)],
          fill=(0, 150, 255), width=2)

# 标签
tags = ["Linux内核", "网络监控", "安全追踪", "性能分析"]
tag_y = 900
for tag in tags:
    bbox = draw.textbbox((0, 0), tag, font=font_tag)
    tag_w = bbox[2] - bbox[0]
    pad = 16
    bx = (WIDTH // 2) - (tag_w // 2) - pad
    draw.rounded_rectangle(
        [bx, tag_y - pad, bx + tag_w + pad * 2, tag_y - pad + 60],
        radius=8, fill=(0, 100, 200)
    )
    draw.text(((WIDTH - tag_w) // 2, tag_y), tag,
              font=font_tag, fill=(255, 255, 255))
    tag_y += 90

# 保存
img.save("cover.jpg", "JPEG", quality=95)

7.3 封面设计原则

技术知识类短视频封面设计要点:

  1. 1. 主标题醒目:大字号、高对比度颜色,在信息流缩略图中也能看清
  2. 2. 色系统一:深蓝/深紫背景 + 青色/白色文字 = 科技感标准配色
  3. 3. 标签明确:3-5个关键词标签,帮助算法理解内容分类
  4. 4. 留白充足:上下边缘留足够空白,避免被平台UI遮挡
  5. 5. 中英文混排:技术词汇保留英文,整体视觉更专业

八、动态封面:Ken Burns效果

8.1 什么是Ken Burns

Ken Burns效果是纪录片中常见的缓慢镜头推拉技术(缓慢放大+平移),用静态图片创造出动态感。技术演讲类视频用此效果作为片头,既能抓住观众注意力,又不喧宾夺主。

8.2 生成动态封面

方案一:ffmpeg zoompan(推荐)

代码语言:javascript
复制
ffmpeg -y \
  -loop 1 \
  -i cover.jpg \
  -vf "
    scale=1200:-1,
    zoompan=z='min(zoom+0.0015,1.15)':d=125:
         x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)',
    fps=25
  " \
  -t 5 \
  -c:v libx264 -preset fast -crf 23 \
  -pix_fmt yuv420p \
  intro.mp4

参数说明:

  • scale=1200:-1 — 放大图片分辨率便于zoom操作
  • zoom+0.0015 — 每帧放大0.0015,从1.0到1.15
  • d=125 — 每帧停留125帧后开始缩放
  • x='iw/2-(iw/zoom/2)' — 始终从中心裁剪

方案二:多张封面切换

如果想用多张不同的封面图做轮播:

代码语言:javascript
复制
ffmpeg -y \
  -f concat -safe 0 -i list.txt \
  -vf "zoompan=z='1.0+0.005*t':d=125:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)',fps=25" \
  -t 5 -c:v libx264 intro.mp4

九、视频合并:片头+正片+音轨

9.1 常见问题与解法

视频合并是整个流程中最容易出问题的环节,核心原因是音视频轨道不一致

问题

原因

解法

合并后只有部分有声音

片头无音频轨道

补静音音轨

视频长度对不上

帧率不同

重新统一编码

画面错位/花屏

分辨率不同

重新统一编码

concat报错

编码参数不兼容

用filter_complex而非copy

9.2 标准合并流程

Step 1: 确保片头有音频轨道

如果片头是纯视觉封面,需补静音音轨:

代码语言:javascript
复制
ffmpeg -y \
  -i intro_video.mp4 \
  -f lavfi -i "anullsrc=r=44100:cl=stereo" \
  -t 5 \
  -c:v copy -c:a aac -b:a 128k \
  -shortest \
  intro_with_audio.mp4

anullsrc 生成静音音频源,零成本解决片头无声问题。

Step 2: 统一编码两个视频

代码语言:javascript
复制
# 片头统一编码
ffmpeg -y -i intro_with_audio.mp4 \
  -c:v libx264 -preset fast -crf 23 \
  -r 25 -pix_fmt yuv420p \
  intro_u.mp4

# 正片统一编码
ffmpeg -y -i main_content.mp4 \
  -c:v libx264 -preset fast -crf 23 \
  -r 25 -pix_fmt yuv420p \
  main_u.mp4

Step 3: concat合并

代码语言:javascript
复制
# 创建文件列表
cat > concat_list.txt << 'EOF'
file 'intro_u.mp4'
file 'main_u.mp4'
EOF

# 合并
ffmpeg -y \
  -f concat -safe 0 \
  -i concat_list.txt \
  -c copy \
  final_output.mp4

Step 4: 验证输出

代码语言:javascript
复制
ffprobe -v quiet -show_streams final_output.mp4 \
  | grep -E "codec_type|codec_name|width|height|duration"

正确输出应该同时包含:

  • codec_type=video + codec_name=h264
  • codec_type=audio + codec_name=aac

十、完整脚本封装

将上述所有步骤整合为一个可复用的Shell脚本:

代码语言:javascript
复制
#!/bin/bash
# video-to-short.sh — 技术视频转竖屏短视频

INPUT=$1
OUTPUT=${2:-output.mp4}
SS=${3:-0}
DURATION=${4:-180}

echo"开始处理: $INPUT"
echo"输出文件: $OUTPUT"
echo"截取范围: ${SS}s - $((SS+DURATION))s"

# 1. 提取音频
echo"[1/6] 提取音频..."
ffmpeg -y -i "$INPUT" -ss $SS -t $DURATION \
  -vn -acodec pcm_s16le -ar 16000 -ac 1 \
  audio.wav

# 2. 语音识别 (调用Python脚本)
echo"[2/6] 语音识别..."
python3 whisper_transcribe.py audio.wav > subtitles.srt

# 3. 翻译字幕 (调用Python脚本)
echo"[3/6] 翻译字幕..."
python3 translate_subtitles.py subtitles.srt subtitles_cn.ass

# 4. 竖屏转制
echo"[4/6] 竖屏转制..."
ffmpeg -y -i "$INPUT" -ss $SS -t $DURATION \
  -vf "scale=1080:607:force_original_aspect_ratio=decrease,\
       pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black" \
  -c:v libx264 -preset fast -crf 23 -r 25 -an \
  portrait.mp4

# 5. 字幕烧录
echo"[5/6] 字幕烧录..."
python3 burn_subtitles.py portrait.mp4 subtitles_cn.ass portrait_sub.mp4

# 6. 合并
echo"[6/6] 合并..."
ffmpeg -y -i portrait_sub.mp4 \
  -i "$INPUT" -ss $SS -t $DURATION \
  -map 0:v -map 1:a \
  -c:v copy -c:a aac -b:a 128k -shortest \
"$OUTPUT"

echo "完成! 输出: $OUTPUT"

十一、技术指标总结

11.1 输出规格

参数

推荐值

说明

分辨率

1080×1920

抖音/视频号竖屏标准

帧率

25fps

与原素材一致

视频编码

H264

兼容性最好

视频码率

CRF 22-23

无损质量,约3-5Mbps

音频编码

AAC

全平台兼容

音频码率

128kbps

足够清晰

文件格式

MP4

抖音/视频号首选

11.2 性能参考

环节

耗时(160秒视频)

音频提取

<1秒

Whisper语音识别

30-60秒

字幕翻译(人工)

视段落数而定

ffmpeg竖屏转制

20-30秒

PyAV字幕烧录

30-60秒

视频合并

<5秒

合计

约2-4分钟

11.3 文件大小参考

类型

大小

封面图 (JPEG)

100-200KB

片头视频 (5秒)

200-500KB

正片 (160秒,竖屏+字幕)

3-5MB

最终成品 (167秒)

4-6MB


十二、常见问题排查

Q1: ffprobe显示视频有音频,但转码后没声音 A: 检查是否使用了 -an 参数(禁止音频)。竖屏转制时如果不需要单独处理音频,应去掉该参数。

Q2: Whisper识别准确率低 A: 尝试使用更大的模型(如 small 而非 base);确保音频是16kHz单声道;技术术语可提前建立词汇表做后处理纠正。

Q3: ffmpeg报错 "Invalid argument" A: 通常是输入文件编码参数不兼容,尝试加上 -pix_fmt yuv420p 强制统一像素格式。

Q4: 合并后画面花屏 A: 两个视频的分辨率/帧率/编码参数不一致。用统一编码(不用 -c copy)重新处理两个视频后再合并。

Q5: macOS M芯片上运行Whisper崩溃 A: 设置环境变量 KMP_DUPLICATE_LIB_OK=TRUE;同时设置 OMP_NUM_THREADS=1 避免多线程冲突。


结语

从技术视频素材到抖音/视频号的竖屏短视频,整个流程的核心难点有两个:

  1. 1. 字幕烧录:大多数用户的ffmpeg环境缺少libass/drawtext,PyAV+PIL方案可以无依赖地解决这一问题
  2. 2. 音视频轨道同步:片头无声、分辨率不一致、帧率不匹配是合并失败的三大原因,按本文的分步流程可以有效规避

掌握这套方案后,一个人在1-2小时内就可以完成从素材评估→语音识别→字幕制作→竖屏转制→封面设计→合并输出的全流程。如果有批量素材,可以进一步将各步骤脚本化,实现半自动化流水线生产。

对于安全研究领域的朋友,还可以将这套流程与之前的LLM安全研究结合起来——用garak做内容安全探测,用Whisper做内容提取,用ffmpeg做视觉包装,形成一套AI辅助的安全内容生产管线


本文相关技术栈:ffmpeg 8.x / faster-whisper / PyAV / Pillow / ASS字幕格式

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-05-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、整体技术架构
    • 1.1 流程总览
    • 1.2 工具选型说明
  • 二、素材评估与片段选取
    • 2.1 视频信息探测
    • 2.2 时长与内容预判
    • 2.3 音频轨道检查
  • 三、语音识别:faster-whisper
    • 3.1 为什么用 faster-whisper
    • 3.2 安装
    • 3.3 语音识别代码
    • 3.4 音频提取
    • 3.5 识别结果处理
  • 四、字幕制作:ASS格式
    • 4.1 为什么要用ASS格式
    • 4.2 ASS文件结构
    • 4.3 翻译策略
  • 五、竖屏转制:ffmpeg
    • 5.1 核心思路
    • 5.2 scale+pad 加黑边方案
    • 5.3 音频轨道保留
  • 六、字幕烧录:PyAV + PIL
    • 6.1 为什么不用ffmpeg直接烧录
    • 6.2 PyAV 方案
    • 6.3 完整烧录代码
    • 6.4 性能数据参考
  • 七、封面图设计
    • 7.1 竖屏封面尺寸
    • 7.2 PIL封面绘制代码
    • 7.3 封面设计原则
  • 八、动态封面:Ken Burns效果
    • 8.1 什么是Ken Burns
    • 8.2 生成动态封面
  • 九、视频合并:片头+正片+音轨
    • 9.1 常见问题与解法
    • 9.2 标准合并流程
  • 十、完整脚本封装
  • 十一、技术指标总结
    • 11.1 输出规格
    • 11.2 性能参考
    • 11.3 文件大小参考
  • 十二、常见问题排查
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档