以前有写过当时不是完全理解和一些技术解决方案,当然在有了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();