使用tus.js与tus-php上传视频后,播放器无法流式加载求助
解决tus.js + tus-php上传视频后无法流式播放的问题
问题根源
无法流式播放通常源于两个核心问题:
- 服务器未支持HTTP Range请求(播放器需要分段加载视频的能力)
- 视频文件的moov atom(元数据块)位于文件末尾,播放器必须加载完整文件才能解析播放信息
解决方案
1. 配置服务器支持HTTP Range请求
Nginx配置示例
如果使用Nginx托管静态视频文件,在对应server块中添加:
location ~* \.(mp4|m4v)$ { mp4; mp4_buffer_size 1m; mp4_max_buffer_size 5m; add_header Accept-Ranges bytes; add_header Content-Type video/mp4; }
这段配置开启Nginx的mp4模块,支持Range请求并返回正确响应头。
Apache配置示例
启用mod_headers和mod_expires模块后,在.htaccess或虚拟主机配置中添加:
<FilesMatch "\.(mp4|m4v)$"> Header set Accept-Ranges bytes Header set Content-Type video/mp4 </FilesMatch>
2. 前置视频的moov atom
很多视频的元数据(moov)在文件末尾,导致播放器必须加载完整个文件才能开始播放。可在上传完成后用ffmpeg自动处理:
在tus上传完成的回调中(比如Laravel路由处理逻辑里)执行:
// 假设上传后的视频路径为$videoPath $outputPath = str_replace('.mp4', '_stream.mp4', $videoPath); exec("ffmpeg -i {$videoPath} -movflags faststart {$outputPath}"); // 替换原文件或更新数据库路径 rename($outputPath, $videoPath);
-movflags faststart参数会把moov atom移到文件开头,让播放器快速解析播放信息。
3. 在Laravel中处理视频请求的Range逻辑
如果通过Laravel路由返回视频(而非直接静态托管),需手动处理Range请求:
Route::get('/storage/videos/{filename}', function ($filename) { $path = public_path('storage/videos/' . $filename); if (!file_exists($path)) { abort(404); } $filesize = filesize($path); $mime = mime_content_type($path); $range = request()->header('Range'); if ($range) { list($start, $end) = explode('-', substr($range, 6)); $start = (int)$start; $end = $end ? (int)$end : $filesize - 1; $length = $end - $start + 1; $file = fopen($path, 'rb'); fseek($file, $start); $content = fread($file, $length); fclose($file); return response($content, 206) ->header('Content-Type', $mime) ->header('Content-Length', $length) ->header('Content-Range', "bytes {$start}-{$end}/{$filesize}") ->header('Accept-Ranges', 'bytes'); } return response()->file($path, [ 'Accept-Ranges' => 'bytes', 'Content-Type' => $mime, ]); });
这段代码会处理客户端的Range请求,返回分段视频数据,实现流式播放。
4. 调整tus-php的上传完成处理
在Laravel路由中处理tus请求时,添加上传完成回调触发视频处理:
Route::any('/tus', function () { $server = app('tus-server'); // 监听上传完成事件 $server->on('finish', function ($file) { $videoPath = public_path('storage/videos/' . $file->getName()); // 调用ffmpeg处理逻辑 $outputPath = str_replace('.mp4', '_stream.mp4', $videoPath); exec("ffmpeg -i {$videoPath} -movflags faststart {$outputPath}"); rename($outputPath, $videoPath); }); $server->serve(); return response()->json(); });
内容的提问来源于stack exchange,提问作者islam öçelik
相关产品推荐
相关产品推荐

