Linux技术内容创作实战:从视频制作到文章生成的高效流程分享

(首发地址:学习日记 https://www.learndiary.com/2024/04/speed-video-blog-making/

作为淘宝网“学习日记小店”的 Linux 服务提供者,我在此分享一下如何高效制作 Linux 技术视频及配套文章的心得体会。我采用 Deepin 20.9 Linux 操作系统,搭配 i7-4770 处理器和一块 Nvidia P106-100 矿卡作为硬件环境。视频演示:【Linux技术内容创作实战:从视频制作到文章生成的高效流程分享】https://www.bilibili.com/video/BV1AJ4m1L7BU/

首先,录制视频时我选用 SimpleScreenRecorder 或 Deepin 内置的截图录屏工具。以制作《开源项目生存现况 - xz投毒事件引发的思考与GNU tar维护挑战》视频为例(见后参考链接1),原始视频时长8分19秒。视频录制完成后,我会在脚本 xz-tar.sh(见后附件1)中使用 whisper-ctranslate2 命令行工具(基于更快、资源占用较低的 faster-whisper 项目,见参考链接2和3)进行语音转字幕。我在 Subs AI(见参考链接4)、一个集成了 whisper 和多种 whisper 变种的工具中测试过多种语音转字幕软件。经过测试,我选择了 faster-whisper 的命令行客户端 whisper-ctranslate2,因为它不仅转写质量好、速度快、GPU 显存占用较低,即便使用 CPU 计算也能有效控制内存消耗。示例视频转写用了1分10秒(GPU)。

处理视频字幕后,我发现部分字幕可能出现提前于语音但会同步结束的现象,为此编写了 adjust_time.sh(见后附件2)脚本来修正超过一定时长(这里为10秒)的字幕时间轴,确保字幕与语音同步。完成初步机器转写后,还需人工逐句核对。

完成字幕校对后,利用bash脚本 xz-tar.sh 的第二部分使用 ffmpeg 把字幕烧录进视频,然后对视频进行加速处理。因为 Nvidia P106-100 矿卡不具备视频编码加速功能,所以我这里 ffmpeg 全程依赖 CPU 进行编码。示例视频这部分处理用了4分39分。

视频处理完毕后,使用 SMPlayer 提取视频封面底图,再用 GIMP 编辑视频封面,我这里就是添加点内容提要之类。

在等待视频处理的同时,我会准备文章内容,通过 extract_text.sh(见后附件3)脚本去除字幕文件中的序号、时间戳及空白行,得到纯文本内容。然后,在内容前面写上简短的要求,随后将其提交至“通义千问”,借助 AI 辅助生成文章初稿。

然后人工对文章进行补充和修改,进一步完善文章细节。整个生产视频和文章的流程结束。

感谢大家的观看。欢迎交流、批评和指正。

附件:

1. 脚本 xz-tar.sh

#!/bin/bash

if [[ $1 -eq 1 ]]; then
# 提取声音
ffmpeg -i xz-tar.mp4 xz-tar.wav

# 将原声 xz-tar.wav 用 whisper-ctranslate2 生成字幕
. ~/.bashrc
conda activate whisper
time \
    whisper-ctranslate2 \
    --model large-v3 \
    --output_format srt \
    --device cuda \
    --compute int8_float32 \
    --language zh \
    --vad_filter True \
    --local_files_only True \
    --condition_on_previous_text False \
    --word_timestamps True \
    xz-tar.wav
exit 0
fi

#人工校对字幕

if [[ $1 -eq 2 ]]; then
# 把字幕烧录进原视频并加速视频
time \
    (ffmpeg -y \
    -i xz-tar.mp4 \
    -vf subtitles=xz-tar.srt:force_style='Fontsize=26\,FontName=FZYBKSJW--GB1-0\,PrimaryColour=&H00aaff' \
    -b:v 14000k -b:a 256k \
    -async 1 \
    xz-tar_srt.mp4 && \
    ffmpeg -y \
    -i xz-tar_srt.mp4 \
    -filter_complex "[0:v]setpts=0.8*PTS[v];[0:a]atempo=1.25[a]" \
    -b:v 14000k -b:a 256k \
    -map "[v]" -map "[a]" \
    -async 1 \
    xz-tar_srt_speed.mp4)

exit 0
fi

2. 脚本 adjust_time.sh

#!/bin/bash
# 这个脚本用于修正字幕段开始时间过于提前但结束时间正确的情况。
# 方法是根据字幕段的字数,估算对应的正确的持续时间,
# 以此来修正字幕段开始时间。
# 估算默认标准:
# 达到多少毫秒开始修正,FIX_TIME=10000
# 4汉字/秒,英文字符为1个字节,汉字3个字节,1个字节时长,BYTE_TIME=83

FIX_TIME=10000
BYTE_TIME=83

[[ ! -f ${1}.orig ]] && cp ${1} ${1}.orig -v
total_subs=$(($(wc -l ${1} | cut -d ' ' -f 1)/4)) # 总的字幕条数
echo "总的字幕条数:${total_subs}"
sub=1
while [[ ${sub} -le ${total_subs} ]]; do
  #echo "处理第${sub}条字幕"
  line=$((sub*4-3))
  time_line=$((sub*4-2))
  text_line=$((sub*4-1))
  time_str=$(head -n ${time_line} ${1} | tail -n 1)
  #echo ${time_str}
  start_time_str=$(echo "${time_str}" | cut -d ' ' -f 1)
  end_time_str=$(echo "${time_str}" | cut -d ' ' -f 3)
  #echo "${start_time_str}+${end_time_str}"
  start_h=$(echo "${start_time_str}" | cut -d ':' -f 1 | awk '{print $0+0}')
  start_m=$(echo "${start_time_str}" | cut -d ':' -f 2 | awk '{print $0+0}')
  start_s=$(echo "${start_time_str}" | cut -d ':' -f 3 | cut -d ',' -f 1 | awk '{print $0+0}')
  start_ms=$(echo "${start_time_str}" | cut -d ':' -f 3 | cut -d ',' -f 2 | awk '{print $0+0}')
  start_in_ms=$((start_h*3600000+start_m*60000+start_s*1000+start_ms))
  #echo "${start_h}/${start_m}/${start_s}/${start_ms}:${start_in_ms}"
  end_h=$(echo "${end_time_str}" | cut -d ':' -f 1 | awk '{print $0+0}')
  end_m=$(echo "${end_time_str}" | cut -d ':' -f 2 | awk '{print $0+0}')
  end_s=$(echo "${end_time_str}" | cut -d ':' -f 3 | cut -d ',' -f 1 | awk '{print $0+0}')
  end_ms=$(echo "${end_time_str}" | cut -d ':' -f 3 | cut -d ',' -f 2 | awk '{print $0+0}')
  end_in_ms=$((end_h*3600000+end_m*60000+end_s*1000+end_ms))
  #echo "${end_h}/${end_m}/${end_s}/${end_ms}:${end_in_ms}"
  duration=$((end_in_ms-start_in_ms))
  #echo "${duration}ms"
  if [[ ${duration} -ge ${FIX_TIME} ]]; then
    echo "第${sub}条:${duration}ms"
    text_str=$(head -n ${text_line} ${1} | tail -n 1)
    sub_in_byte=$(($(echo "${text_str}" | wc -c)-1))
    echo "'${text_str}' bytes: ${sub_in_byte}"
    estimate_duration=$((sub_in_byte*BYTE_TIME))
    echo "估算时间:${estimate_duration}ms"
    if [[ ${estimate_duration} -ge ${duration} ]]; then
      echo "估算时间大于等于原时间"
      let sub=sub+1
      continue
    fi
    new_start_in_ms=$((end_in_ms-estimate_duration))
    new_start_h=$((new_start_in_ms/3600000))
    new_start_m=$(((new_start_in_ms-new_start_h*3600000)/60000))
    new_start_s=$(((new_start_in_ms-new_start_h*3600000-new_start_m*60000)/1000))
    new_start_ms=$((new_start_in_ms-new_start_h*3600000-new_start_m*60000-new_start_s*1000))
    new_start_time_str=$(printf '%02d:%02d:%02d,%03d' ${new_start_h} ${new_start_m} ${new_start_s} ${new_start_ms})
    echo "原开始时间是"${start_time_str}",新的开始时间是'${new_start_time_str}'"
    sed -i "s/^${start_time_str}/${new_start_time_str}/" ${1}
  fi
  let sub=sub+1
done

3. 脚本 extract_text.sh

#!/bin/bash
# 这个脚本用于提取srt字幕文件里的文字。

text_name=$(basename ${1} .srt).txt
total_subs=$(($(wc -l ${1} | cut -d ' ' -f 1)/4)) # 总的字幕条数
echo "总的字幕条数:${total_subs}"
sub=1
while [[ ${sub} -le ${total_subs} ]]; do
  #echo "处理第${sub}条字幕"
  text_line=$((sub*4-1))
  text_str=$(head -n ${text_line} ${1} | tail -n 1)
  echo "${text_str}" >> ${text_name}
  let sub=sub+1
done

参考链接:

  1. 开源项目生存现况 - xz投毒事件引发的思考与GNU tar维护挑战 https://www.learndiary.com/2024/04/xz-tar/
  2. whisper-ctranslate2 https://github.com/Softcatala/whisper-ctranslate2
  3. faster-whisper https://github.com/SYSTRAN/faster-whisper
  4. Subs AI https://github.com/abdeladim-s/subsai

发表评论

电子邮件地址不会被公开。 必填项已用*标注