PHP NginX - 输出缓冲 - 视频流

PHP NginX - 输出缓冲 - 视频流

(因为它包含一些编码,而且还涉及 nginx 配置,所以我认为最好将它放在 serverfault 中)。

我的服务器中有一些视频文件存储在文件系统内的文件夹中,我使用 PHP 读取视频文件并使用 NginX 作为 Web 服务器将其直接发送给用户。

大多数视频文件都是直播我使用 FFmpeg 生成,但我有一些电影也一样。

直播文件:它们被分成几个片段,我用 php 读取 m3u8 文件,得到 *.ts 文件,然后我使用 PHP 对它们进行流式传输,而 FFmpeg 仍在后台运行。

影片文件:只有一个静态文件

我对 nginx/php 配置有一些疑问。

我的 NginX 配置如下:

server {
    listen 80;
    index index.php index.html index.htm;
    root /var/www;
    server_tokens off;
    chunked_transfer_encoding off;


    location ~ \.php$ {    
        try_files $uri =404;
        fastcgi_index index.php;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
    }
}

对于每个从我的服务器读取视频文件的客户端,我都会记录连接,并且我可以预测他是否仍然在线,或者不在线使用

PHP 中的 connection_aborted() 函数(简而言之,如果 php 脚本仍在运行)

现在的问题是:

NginX 默认fastcgi_buffering开启;当我向客户端提供电影时,这给我带来了问题。当我想提供实时流媒体文件时,这没问题,因为我希望有一些缓冲区来减少 PHP 读取实时流媒体文件内容时发生延迟的可能性。

但在电影中,它只是将整个电影(即使它有 2gb)直接解析到缓冲区,并且无法预测客户端是否收到响应。php 脚本在一秒钟内结束,然后 nginx 将电影提供给客户端,因此我之前谈到的连接日志在一秒钟内就结束了。

如果我转身fastcgi_buffering 关闭;一切都按我想要的方式运行,但是我发现实时流媒体服务存在一些滞后。

最好的办法是fastcgi_buffering开启;在直播和fastcgi_buffering 关闭;在电影里。但我真的不知道该怎么做。

我试过ob_implicit_flush( true );但我认为这对 NginX 也不起作用。事实上,我无法使用任何刷新()功能等

流式 PHP 文件使用以下技术将视频文件发送到客户端

<?php

# $video_file can be either a live stream or movie file.

$bytes = 0;

$stream = fopen( $video_file, "rb" );

while ( ! feof( $stream ) && ClientConnected() )
{
    $response = stream_get_line( $stream, 8192 );
    $bytes += strlen( $response );
    echo $response;
}

fclose( $stream );

/*
    $bytes have been sent
    In movie files the bytes directly goes to the filesize of movie file if fastcgi_buffering is on. 
*/

function ClientConnected()
{
    if ( connection_status() != CONNECTION_NORMAL || connection_aborted() )
    {
        return false;
    }

    return true;
}
?>

答案1

这里的问题是 PHP 不了解流式传输所需的客户端连接状态、缓冲区等。

nginxfastcgi_buffer*选项仅对 nginx 有意义,它们仅指定通过 FastCGI 接口传输的数据的 nginx 输入缓冲区大小。

如果通过 FastCGI 传输的输入(在您的情况下为流数据)大于使用指令分配的内存缓冲区,则 nginx 会将输出存储到磁盘上的临时文件中。

您可以尝试在 PHP 端实现手动延迟,但由于您不了解客户端流媒体状态,因此无法准确实现延迟。

如果你的视频是用 MPEG4 编码的,我建议你使用ngx_http_mp4_module。这直接在 nginx 内部实现流式视频。

答案2

我可能有点迟到了,但万一它能帮助到其他人:正如本指南中解释的那样https://www.jeffgeerling.com/blog/2016/streaming-php-disabling-output-buffering-php-apache-nginx-and-varnish 你可以通过设置标头来指示 Nginx 针对此请求禁用其缓冲区

header('X-Accel-Buffering: no');

https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffering

相关内容