以前有写过当时不是完全理解和一些技术解决方案,当然在有了AI之后大部分有了解决方案,有的时候也不是很靠谱

之前看到一些视频网站的video src是blob:的方式播放视频,当时想抓取还抓去不了,也没有全部解决。

后来通过ai找到的答案虽然可以实现,但是blob的方式肯本解决不了大文件500MB以上的视频。后端转为二进制的输出肯本不可能适应大流量访问,因为等待时间太长进程会卡死,如果是实时的那种blob网址,估计就大神和十分了解底层api的人能做吧。

后来机缘巧合想抓取 财经会员 的视频,看到他们的m3u8的文件发现个脑洞。

当年的我经验尚浅脑洞思维还是不够灵巧。一般m3u8的内容是切片的视频文件 xxxxxxx.ts 这种文件。感觉后台PHP处理切片麻烦,自己做加密的话和权限认证做不好,那时的网上资料也很少,所以直接用了第三方(因为有cdn)。

HLS方式

m3u8是HLS的组成部分,HTTP Live Streaming (HLS) 是由苹果公司开发的一种流媒体传输协议。

下面是某个国内大厂视频服务的m3u8文件

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:11
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.000000,
playlist.f3_0.ts?start=0&end=1341379&type=mpegts
#EXTINF:10.000000,
playlist.f3_0.ts?start=1341380&end=2630683&type=mpegts
#EXTINF:10.000000,
playlist.f3_0.ts?start=2630684&end=4114003&type=mpegts
#EXTINF:10.000000,
playlist.f3_0.ts?start=4114004&end=5602775&type=mpegts
#EXTINF:10.000000,
playlist.f3_0.ts?start=5602776&end=6930995&type=mpegts
.....
.....
#EXTINF:9.916667,
playlist.f3_0.ts?start=2315748656&end=2316805779&type=mpegts
#EXT-X-ENDLIST

文件每一行的解释:
#EXTM3U

这是 M3U8 文件的标识符。它表示这是一个扩展 M3U(M3U8)播放列表文件。
#EXT-X-VERSION:3

这行表示使用的协议版本是 3。M3U8 文件格式有多个版本,这个指示器表明该文件使用的是版本 3 的格式和功能。
#EXT-X-TARGETDURATION:11

目标持续时间。此标签表示播放列表中任何单个片段的最大持续时间,以秒为单位。在这个例子中,最大片段长度为 11 秒。这有助于播放器准备缓冲和处理时序。
#EXT-X-MEDIA-SEQUENCE:0

媒体序列号。这个标签指示播放列表中第一个媒体片段的序列号。在这里,它从 0 开始。这有助于播放器识别和同步不同的片段。
#EXTINF:10.000000,

片段持续时间信息。这个标签后面的数字表示下一个媒体片段的持续时间(以秒为单位)。在这个例子中,第一个片段的长度为 10 秒。
playlist.f3_0.ts?start=0&end=1341379&type=mpegts

这是第一个媒体片段的 URL。播放器将从这个位置下载片段并进行播放。start=0&end=1341379&type=mpegts 可能是 URL 参数,指定片段在源文件中的开始和结束字节位置及其类型(MPEG-TS)。

#EXTINF:9.916667,
playlist.f3_0.ts?start=2315748656&end=2316805779&type=mpegts

#EXT-X-ENDLIST
表示播放列表结束的标识


http://vod2.myqcloud.com/1b87576bvodcq1258344707/8ea8bdaf1253642697045959931/playlist.f3.m3u8


http://vod2.myqcloud.com/1b87576bvodcq1258344707/8ea8bdaf1253642697045959931/playlist.f3_0.ts?start=0&end=1341379&type=mpegts

我觉得的有意思的点,start是文件指针大小的起始点,end是要读区文件大小的结束点。这样它就节约了服务器资源,我当时怎么没有想到

PHP代码例子

<?php
// 检查必要的参数是否存在
if (isset($_GET['start']) && isset($_GET['end']) && isset($_GET['type'])) {
  $filePath = '01.mp4'; // 视频文件的真实路径
  $start = intval($_GET['start']); // 起始字节位置
  $end = intval($_GET['end']); // 结束字节位置
  $type = $_GET['type']; // 文件类型

  if (file_exists($filePath)) {
    header("Content-Type: video/$type");
    header("Accept-Ranges: bytes");
    header("Content-Length: " . ($end - $start));
    header("Content-Range: bytes $start-$end/" . filesize($filePath));
    header("Content-Disposition: inline;");

    // 打开文件
    $file = fopen($filePath, 'rb');

    // 跳转到文件中的起始位置
    fseek($file, $start);

    // 读取并输出文件内容
    echo fread($file, $end - $start + 1);

    // 关闭文件
    fclose($file);
  } else {
    // 文件不存在
    http_response_code(404);
    echo "File not found.";
  }
} else {
  // 参数不完整
  http_response_code(400);
  echo "Invalid parameters.";
}


访问可以是:http://localhost/index.php?start=0&end=4341379&type=mp4
页面就可以做鉴权操作了

直接引入PHP文件

通过`HTTP_RANGE头的方式

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Video  from PHP</title>
  <style>
  </style>
</head>
<body>
<video controls id="videoPlayer">
  <!-- 提供一个默认的视频源 -->
  <source src="default-video.mp4" type="video/mp4">
  Your browser does not support the video tag.
</video>

<script>

  document.addEventListener('DOMContentLoaded', function () {
    var video = document.getElementById('videoPlayer');
    video.src = 'video.php'; // PHP脚本的URL
  });

</script>
</body>
</html>

后端代码

<?php
$memoryBefore = memory_get_usage();
$path = 'xxxxx.mp4'; // 替换成你的视频文件路径

if (!file_exists($path)) {
  header("HTTP/1.1 404 Not Found");
  exit();
}

$size = filesize($path);
$fp = fopen($path, 'rb');

if ($fp === false) {
  header("HTTP/1.1 500 Internal Server Error");
  exit();
}

$start = 0;
$end = $size - 1;
$length = $size;
if (isset($_SERVER['HTTP_RANGE'])) {
  $c_start = $start;
  $c_end = $end;

  list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
  if (strpos($range, ',') !== false) {
    header('HTTP/1.1 416 Requested Range Not Satisfiable');
    header("Content-Range: bytes $start-$end/$size");
    exit();
  }
  if ($range == '-') {
    $c_start = $size - substr($range, 1);
  } else {
    $range = explode('-', $range);
    $c_start = $range[0];

    $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $c_end;
  }
  $c_end = ($c_end > $end) ? $end : $c_end;
  if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
    header('HTTP/1.1 416 Requested Range Not Satisfiable');
    header("Content-Range: bytes $start-$end/$size");
    exit();
  }
  $start = $c_start;
  $end = $c_end;
  $length = $end - $start + 1;

  fseek($fp, $start);
  header('HTTP/1.1 206 Partial Content');
}

header("Content-Type: video/mp4");
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: $length");
set_time_limit(0); // Reset time limit for large files

$buffer = 1024 * 8;
while (!feof($fp) && ($p = ftell($fp)) <= $end) {
  if ($p + $buffer > $end) {
    $buffer = $end - $p + 1;
  }
  echo fread($fp, $buffer);
  flush(); // Flush system output buffer
}
$memoryPeak = memory_get_peak_usage();
fclose($fp);
file_put_contents('loadVideo.txt',"时间:".date('Y-m-d H:i:s'). " 内存使用:".(($memoryPeak - $memoryBefore) /1024/1024)."MB"."\n",FILE_APPEND);

exit();
Last modification:May 22, 2024
如果觉得我的文章对你有用,请随意赞赏