背景

在看抖音网页版的时候,发现抖音的视频是这种形式的:blob:http://asset.chenbobo.top/3d300215-bbcc-4e85-a766-c1568b70a338,但是当访问 http://asset.chenbobo.top/3d300215-bbcc-4e85-a766-c1568b70a338时,却发现,这个是一个 404,这是为了防止自己的网页被爬虫爬取实现的一个技术,后端返回前端的并不是一个 mp4 的 url,而是一个 .m3u8 的地址,通过 HLS(HTTP Live Streaming) 进行加载

流程

在一个短视频平台中,用户上传的视频可能是各种不同的格式,为了统一管理,一般来说,都会将用户上传的视频转换为统一格式,比如:mp4,同时对用户上传的视频进行分片,这样就实现了加载功能,并对用户的视频生成不同的清晰度,这样就是实现了切换清晰度的功能。

技术

FFmpeg

简介

  • FFmpeg 是一个开源的多媒体处理工具套件,它包含很多命令行工具,最著名的是 ffmpeg 命令,用于音视频转码、剪辑、格式转换、切片、合成等操作

  • 它的功能非常强大,支持几乎所有主流音视频格式和编解码器。

官方最主流的用法是通过命令行终端执行,比如:

ffmpeg -i input.mp4 -c:v libx264 output.mp4
  • 这是最直接、最灵活、也最普遍的使用方式。

安装

  1. 安装 EPEL 和 RPM Fusion 仓库

sudo dnf install epel-release -y
sudo dnf install https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm -y
  1. 安装 FFmpeg

sudo dnf install ffmpeg ffmpeg-devel -y
  1. 验证安装

ffmpeg -version

使用

  1. 将一个输入视频转码

ffmpeg -i input.mp4 -c:v libx264 -preset fast -crf 23 output.mp4

命令

含义

ffmpeg

启动 ffmpeg 命令行工具

-i input.mp4

指定输入文件 input.mp4

-c:v libx264

视频编码器使用 libx264

,即 H.264 编码(兼容性好,广泛使用)

-preset fast

编码速度预设为 fast

(影响转码速度与压缩率)

-crf 23

常量质量控制(CRF),数值越小画质越好,文件越大;默认值是 23

output.mp4

输出文件名

  1. 生成多个分辨率

# 720p
ffmpeg -i input.mp4 -vf scale=-2:720 -c:v libx264 -preset fast -crf 23 output_720p.mp4

# 480p
ffmpeg -i input.mp4 -vf scale=-2:480 -c:v libx264 -preset fast -crf 23 output_480p.mp4

# 360p
ffmpeg -i input.mp4 -vf scale=-2:360 -c:v libx264 -preset fast -crf 23 output_360p.mp4
  • -vf scale=-2:720 表示保持宽高比,把高度设置为 720,宽度自动调整为 2 的倍数(ffmpeg 要求)

  • 适合用于多码率视频适配播放器分辨率切换

  1. 切片成 HLS(实现在线播放、秒切分辨率)

ffmpeg -i input.mp4 \
  -profile:v baseline -level 3.0 -start_number 0 \
  -hls_time 5 -hls_list_size 0 -f hls output.m3u8
  • 生成 output.m3u8 索引文件,和一堆 .ts 视频切片

  • 可配合 Nginx 播放

  • 想做多分辨率自适应 HLS,我也可以教你

  1. 分离音视频轨道

  • 拆分音频:

ffmpeg -i input.mp4 -vn -acodec copy audio.aac
  • 拆分视频(无声):

ffmpeg -i input.mp4 -an -vcodec copy video.mp4
  1. 给视频加水印

ffmpeg -i input.mp4 -i watermark.png -filter_complex "overlay=10:10" output.mp4
  • overlay=10:10 表示将图片放在左上角 (10px, 10px)

  1. 裁剪视频时间段

ffmpeg -ss 00:00:05 -i input.mp4 -t 10 -c copy output_clip.mp4
  • -ss:起始时间

  • -t:持续时间(10 秒)

  • -c copy:不转码,快速剪切

  1. 合并多个视频

  1. 生成一个 file_list.txt:

file 'part1.mp4'
file 'part2.mp4'
file 'part3.mp4'
  1. 合并

ffmpeg -f concat -safe 0 -i file_list.txt -c copy merged.mp4

Demo

项目结构:

/var 
  /www
    /assets
      /videos
        index.html
        /outputs
        /scripts
          transcode.sh
  1. 新建脚本 /scripts/transcode.sh

#!/bin/bash

# 参数:输入文件路径(MP4)
INPUT=$1
BASENAME=$(basename "$INPUT" .mp4)
OUTPUT_DIR="../outputs/$BASENAME"

mkdir -p "$OUTPUT_DIR"

# 生成多个分辨率的 HLS 文件
for RES in 360 480 720; do
  ffmpeg -i "$INPUT" \
    -vf scale=-2:$RES \
    -c:a aac -ar 48000 -c:v libx264 -profile:v main -crf 20 -sc_threshold 0 \
    -g 48 -keyint_min 48 \
    -hls_time 4 -hls_playlist_type vod \
    -b:v 1000k \
    -hls_segment_filename "$OUTPUT_DIR/${RES}p_%03d.ts" \
    "$OUTPUT_DIR/${RES}p.m3u8"
done

# 产生切片
cat > "$OUTPUT_DIR/master.m3u8" <<EOF
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
360p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=854x480
480p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
720p.m3u8
EOF

echo "✅ 转码完成:$OUTPUT_DIR"
  1. 编写 nginx 配置

server {
    listen 80;
    server_name asset.chenbobo.top;

    location /assets/ {
        alias /var/www/assets/videos/;
        autoindex on;
    }

    location /videos/ {
        alias /var/www/assets/videos/outputs/;
        add_header Cache-Control no-cache;
        autoindex on;
        types {
            application/vnd.apple.mpegurl m3u8;
            video/mp2t ts;
        }
    }
}
  1. 执行脚本

./transcode.sh ./${filename}.mp4

这会在 outputs/ 中生成一个 ${filename} 文件夹,并在其中有很多文件

  1. 编写 html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>HLS 手动切换清晰度</title>
  </head>
  <body>
    <h2>HLS 视频播放器 - 手动清晰度选择</h2>

    <video id="video" controls width="640" height="360"></video>
    <br>
    <label for="qualitySelect">清晰度:</label>
    <select id="qualitySelect">
      <option value="-1">自适应</option>
    </select>

    <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
    <script>
      const video = document.getElementById('video');
      const qualitySelect = document.getElementById('qualitySelect');

      if (Hls.isSupported()) {
        const hls = new Hls();
        hls.loadSource('http://asset.chenbobo.top/videos/IMG_8633.MOV/master.m3u8'); // ← 这里替换为你的实际地址
        hls.attachMedia(video);

        hls.on(Hls.Events.MANIFEST_PARSED, function () {
          const levels = hls.levels;

          levels.forEach((level, i) => {
            const option = document.createElement('option');
            option.value = i;
            option.text = `${level.height}p`;
            qualitySelect.appendChild(option);
          });

          qualitySelect.addEventListener('change', () => {
            const level = parseInt(qualitySelect.value, 10);
            hls.currentLevel = level;
          });
        });
      } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
        // iOS Safari 原生支持 HLS
        video.src = 'http://asset.chenbobo.top/videos/IMG_8633.MOV/master.m3u8'; // ← 替换为实际地址
      }
    </script>
  </body>
</html>
  1. 然后访问:http://asset.chenbobo.top/assets/ 即可看到效果