HTML5视频和部分范围的HTTP请求 [英] HTML5 video and partial range HTTP requests
问题描述
我正在尝试修改自定义网络服务器应用以使用HTML5视频。
I'm trying to modify a custom web server app to work with HTML5 video.
它提供基本<的HTML5页面;视频>
标签,然后它需要处理实际内容的请求。
It serves a HTML5 page with a basic <video>
tag and then it needs to handle the requests for actual content.
到目前为止我能让它工作的唯一方法是将整个视频文件加载到内存中,然后在一个响应中将其发回。这不是一个实用的选择。我想逐件服务:发送回来,比方说,100 kb,等待浏览器请求更多。
The only way I could get it to work so far is to load the entire video file into the memory and then send it back in a single response. It's not a practical option. I want to serve it piece by piece: send back, say, 100 kb, and wait for the browser to request more.
我看到一个带有以下标题的请求:
I see a request with the following headers:
http_version = 1.1
request_method = GET
Host = ###.###.###.###:##
User-Agent = Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0) Gecko/20100101 Firefox/16.0
Accept = video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5
Accept-Language = en-US,en;q=0.5
Connection = keep-alive
Range = bytes=0-
我试图发回部分内容回复:
I tried to send back a partial content response:
HTTP/1.1 206 Partial content
Content-Type: video/mp4
Content-Range: bytes 0-99999 / 232725251
Content-Length: 100000
我还收到了一些GET请求,如下所示
I get a few more GET requests, as follows
Cache-Control = no-cache
Connection = Keep-Alive
Pragma = getIfoFileURI.dlna.org
Accept = */*
User-Agent = NSPlayer/12.00.7601.17514 WMFSDK/12.00.7601.17514
GetContentFeatures.DLNA.ORG = 1
Host = ###.###.###.###:##
(没有迹象表明浏览器需要文件的任何特定部分。)无论我发送回浏览器,视频都不会播放。
(with no indication that the browser wants any specific part of the file.) No matter what I send back to the browser, the video does not play.
如上所述,如果我尝试在同一个HTTP数据包中一次发送整个230 MB文件,相同的视频将正常播放。
As stated above, the same video will play correctly if I try to send the entire 230 MB file at once in the same HTTP packet.
有没有办法通过部分内容请求很好地完成这一切?我正在使用Firefox进行测试,但它最终需要与所有浏览器一起使用。
Is there any way to get this all working nicely through partial content requests? I'm using Firefox for testing purposes, but it needs to work with all browsers eventually.
推荐答案
我知道这是一个旧的问题,但如果它有帮助你可以尝试我们在代码库中使用的以下模型。
I know this is an old question, but if it helps you can try the following "Model" that we use in our code base.
class Model_DownloadableFile {
private $full_path;
function __construct($full_path) {
$this->full_path = $full_path;
}
public function get_full_path() {
return $this->full_path;
}
// Function borrowed from (been cleaned up and modified slightly): http://stackoverflow.com/questions/157318/resumable-downloads-when-using-php-to-send-the-file/4451376#4451376
// Allows for resuming paused downloads etc
public function download_file_in_browser() {
// Avoid sending unexpected errors to the client - we should be serving a file,
// we don't want to corrupt the data we send
@error_reporting(0);
// Make sure the files exists, otherwise we are wasting our time
if (!file_exists($this->full_path)) {
header('HTTP/1.1 404 Not Found');
exit;
}
// Get the 'Range' header if one was sent
if (isset($_SERVER['HTTP_RANGE'])) {
$range = $_SERVER['HTTP_RANGE']; // IIS/Some Apache versions
} else if ($apache = apache_request_headers()) { // Try Apache again
$headers = array();
foreach ($apache as $header => $val) {
$headers[strtolower($header)] = $val;
}
if (isset($headers['range'])) {
$range = $headers['range'];
} else {
$range = false; // We can't get the header/there isn't one set
}
} else {
$range = false; // We can't get the header/there isn't one set
}
// Get the data range requested (if any)
$filesize = filesize($this->full_path);
$length = $filesize;
if ($range) {
$partial = true;
list($param, $range) = explode('=', $range);
if (strtolower(trim($param)) != 'bytes') { // Bad request - range unit is not 'bytes'
header("HTTP/1.1 400 Invalid Request");
exit;
}
$range = explode(',', $range);
$range = explode('-', $range[0]); // We only deal with the first requested range
if (count($range) != 2) { // Bad request - 'bytes' parameter is not valid
header("HTTP/1.1 400 Invalid Request");
exit;
}
if ($range[0] === '') { // First number missing, return last $range[1] bytes
$end = $filesize - 1;
$start = $end - intval($range[0]);
} else if ($range[1] === '') { // Second number missing, return from byte $range[0] to end
$start = intval($range[0]);
$end = $filesize - 1;
} else { // Both numbers present, return specific range
$start = intval($range[0]);
$end = intval($range[1]);
if ($end >= $filesize || (!$start && (!$end || $end == ($filesize - 1)))) {
$partial = false;
} // Invalid range/whole file specified, return whole file
}
$length = $end - $start + 1;
} else {
$partial = false; // No range requested
}
// Determine the content type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$contenttype = finfo_file($finfo, $this->full_path);
finfo_close($finfo);
// Send standard headers
header("Content-Type: $contenttype");
header("Content-Length: $length");
header('Content-Disposition: attachment; filename="' . basename($this->full_path) . '"');
header('Accept-Ranges: bytes');
// if requested, send extra headers and part of file...
if ($partial) {
header('HTTP/1.1 206 Partial Content');
header("Content-Range: bytes $start-$end/$filesize");
if (!$fp = fopen($this->full_path, 'r')) { // Error out if we can't read the file
header("HTTP/1.1 500 Internal Server Error");
exit;
}
if ($start) {
fseek($fp, $start);
}
while ($length) { // Read in blocks of 8KB so we don't chew up memory on the server
$read = ($length > 8192) ? 8192 : $length;
$length -= $read;
print(fread($fp, $read));
}
fclose($fp);
} else {
readfile($this->full_path); // ...otherwise just send the whole file
}
// Exit here to avoid accidentally sending extra content on the end of the file
exit;
}
}
然后你可以这样使用它:
You then use it like this:
(new Model_DownloadableFile('FULL/PATH/TO/FILE'))->download_file_in_browser();
它将处理发送部分文件或完整文件等,并且对我们来说效果很好还有很多其他情况。希望它有所帮助。
It will deal with sending part of the file or the full file etc and works well for us in this and lots of other situations. Hope it helps.
这篇关于HTML5视频和部分范围的HTTP请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!