我是PHP的新手,绝对是流媒体视频的新手。
我在SQL Server的FileStream列中存储了MP4视频,我想将它们流式传输到HTML5视频。
我已经设法使用以下流类(http://codesamplez.com/programming/php-html5-video-streaming-tutorial(,通过提供'file_put_contents($ filepath,$ queryResult,$ queryResult(来提供临时文件,,'但是必须依靠写作然后清理文件系统中的中间文件是有问题的。
我是否错过了一个关键概念,该概念可以让我直接从数据库流到视频" src",而不必在之间写入文件?
预先感谢!
如果自己创建临时文件,然后删除它。然后,您可以使用PHP的内置 tempfile()
在read-write(w (模式中创建一个带有唯一名称的临时文件,并返回文件句柄。关闭时,文件将自动删除(例如,通过调用fclose((,或者没有剩余的引用对由tmpfile(((或脚本结束时返回的文件句柄。
private function open()
{
if ( !($this->stream = tmpfile()) ) {
fwrite($this->stream, $videodata);
rewind($this->stream);
die('Could not open stream for reading');
}
// now your temp file is ready to be read.
}
所以现在您不承担临时责任。删除PHP将为您处理。如果您需要自定义温度。文件名或获取详细信息您可以使用此http://php.net/manual/en/function.sys-get-temp-dir.php
如果您根本不想使用文件,而宁愿纯粹在内存解决方案中使用,则可以尝试使用内存流
private function open()
{
if ( !($this->stream = fopen('php://memory', 'wb+') ) ) {
fwrite($this->stream, $videodata);
rewind($this->stream);
die('Could not open stream for reading');
}
// now your in-memory file is ready to be read.
}
您可以在存储器流上的fread,fwrite,file_get_contents或使用TCP流将其扔到网络上。但是我必须说,第二个解决方案有点密集,因此不适合流庞大文件。
编写临时文件似乎对我来说是一个临时的解决方法。当您从数据库中获取流并需要将流提供给客户端时,根本不需要存储数据。
您需要做的就是替换链接的示例的部分,该示例从文件中读取(以及Echo的二进制内容到客户端的二进制内容(,然后从SQL读取。
在您的链接示例函数流((中,替换此部分
while(!feof($this->stream) && $i <= $this->end) {
$bytesToRead = $this->buffer;
if(($i+$bytesToRead) > $this->end) {
$bytesToRead = $this->end - $i + 1;
}
$data = fread($this->stream, $bytesToRead);
echo $data;
flush();
$i += $bytesToRead;
}
替换这样的东西:
/* Execute the query. */
$stmt = sqlsrv_query($conn, $tsql, $params);
if( $stmt === false )
{
echo "Error in statement execution.</br>";
die( print_r( sqlsrv_errors(), true));
}
/* Retrieve and display the data.
The return data is retrieved as a binary stream. */
if ( sqlsrv_fetch( $stmt ) )
{
$videostream = sqlsrv_get_field( $stmt, 0,
SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY));
//header("Content-Type: image/jpg");
fpassthru($videostream );
}
else
{
echo "Error in retrieving data.</br>";
die(print_r( sqlsrv_errors(), true));
}
对于后代,这是我的最终结果,似乎不需要临时文件就可以很好地工作。
不确定将查询包含在课堂上并将$ mediafileid传递到班上是否更有意义,但是现在我已经离开了。
其原始信用信息包含整个修改类别:
function ExecutereadMediaSP($MediaFileID){
try{
$connection = ConnectToDB();
ini_set('memory_limit', '-1');
// logs basic info about media viewer. Mainly for a basic hit counter.
LogMediaRequest($connection, $MediaFileID);
//Selects binary data from SQL Server based on MediaFileID. Passes this to Stream Class. May be able to make further improvements later.
$sql = "SELECT ... data from FILESTREAM column based on file ID ...";
$rst = $connection->prepare($sql);
$rst->execute();
$rst->bindColumn(1, $filecontent, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_BINARY);
$row = $rst->fetch(PDO::FETCH_ASSOC);//sql can only return one row due to unique identifier
//Stream file
$stream = new VideoStream($filecontent);
$stream->start();
//Clean up.
$rst->closeCursor();
unset($rst);
$connection = null;
} catch (Exception $e) {
error_log("Error in getting videon".$e->getMessage(),0);
}
}
/**
* VideoStream - PHP class that supports (adaptive) streaming of files
*
* @author Rana
* modified by HazCod to use stream_get_contents and correct session shutoff
* https://github.com/HazCod
* @link http://codesamplez.com/programming/php-html5-video-streaming-tutorial
*/
class VideoStream
{
private $path = "";
private $stream = "";
private $buffer = 102400;
private $start = -1;
private $end = -1;
private $size = 0;
function __construct($filecontent)
{
try{
// Opens file handle resource to replace use of actual file in the file system.
$file_handle = fopen('php://memory', 'r+', false, stream_context_create());
// Writes data from SQL Query to file wrapper.
fwrite($file_handle, $filecontent);
// Moves pointer to beginning of file.
fseek($file_handle, 0);
// Gets info on the "file." Required to get filesize.
$fstat = array();
// gather statistics
$fstat = fstat($file_handle);
//Set File Size for Stream Class.
$this->size = $fstat['size'];
// Define Stream as "File."
$this->stream = $file_handle;
} catch (PDOException $e) {
error_log("Error in getting videon".$e->getMessage(),0);
}
}
/**
* Set proper header to serve the video content
*/
private function setHeader()
{
ob_get_clean();
header("Content-Type: video/mp4");
header("Cache-Control: max-age=2592000, public");
header("Expires: ".gmdate('D, d M Y H:i:s', time()+2592000) . ' GMT');
// header("Last-Modified: ".gmdate('D, d M Y H:i:s', @filemtime($this->path)) . ' GMT' );
$this->start = 0;
$this->end = $this->size - 1;
// header("Accept-Ranges: 0-".$this->end);
if (isset($_SERVER['HTTP_RANGE'])) {
$c_start = $this->start;
$c_end = $this->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 $this->start-$this->end/$this->size");
exit;
}
if ($range == '-') {
$c_start = $this->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 > $this->end) ? $this->end : $c_end;
if ($c_start > $c_end || $c_start > $this->size - 1 || $c_end >= $this->size) {
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $this->start-$this->end/$this->size");
exit;
}
$this->start = $c_start;
$this->end = $c_end;
$length = $this->end - $this->start + 1;
fseek($this->stream, $this->start);
header('HTTP/1.1 206 Partial Content');
header("Content-Length: ".$length);
header("Content-Range: bytes $this->start-$this->end/".$this->size);
}
else
{
header("Content-Length: ".$this->size);
}
}
/**
* close curretly opened stream
*/
private function end()
{
fclose($this->stream);
exit;
}
/**
* perform the streaming of calculated range
*/
private function stream()
{
$i = $this->start;
set_time_limit(0);
while(!feof($this->stream) && $i <= $this->end && connection_aborted() == 0) {
$bytesToRead = $this->buffer;
if(($i+$bytesToRead) > $this->end) {
$bytesToRead = $this->end - $i + 1;
}
$data = stream_get_contents($this->stream, $bytesToRead);
echo $data;
flush();
$i += $bytesToRead;
}
}
/**
* Start streaming video content
*/
function start()
{
session_write_close(); //ensure our session is written away before streaming, else we cannot use it elsewhere
$this->setHeader();
$this->stream();
$this->end();
}
}