Я написал простой сценарий ретрансляции, который подключается к веб-камере и читает из сокета и выводит эти данные с помощью функции печати. Данные представляют собой данные MJPG с уже настроенными границами. Я просто выводим данные, которые читаются.
Проблема заключается в том, что PHP, похоже, выполняет буферизацию этих данных. Когда я устанавливаю камеру на 1 FPS, питание будет зависеть на 7-8 секунд, а затем быстро отобразит 8 кадров. Если я установил разрешение на огромный размер, камера будет двигаться более или менее 1 кадр в секунду. Я предполагаю, что тогда происходит буферизация (поскольку огромные размеры заполняют буфер быстро, а низкие – нет), и я не могу понять, как отключить эту буферизацию. Кто-нибудь знает, как это сделать?
Код:
ignore_user_abort(false); $boundary = "myboundary"; //Set this so PHP doesn't timeout during a long stream set_time_limit(0); $socketConn = @fsockopen ("192.168.1.6", 1989, $errno, $errstr, 2); if (!$socketConn) exit(); stream_set_timeout($socketConn, 10); fputs ($socketConn, "GET /mjpeg HTTP/1.0\r\n\r\n"); //Setup Header Information header("Cache-Control: no-cache"); header("Cache-Control: private"); header("Pragma: no-cache"); header("Content-type: multipart/x-mixed-replace; boundary=$boundary"); @ini_set('implicit_flush', 1); for ($i = 0; $i < ob_get_level(); $i++) ob_end_flush(); ob_implicit_flush(1); stream_set_blocking($f2, false); //Send data to client while (connection_status() == CONNECTION_NORMAL) { $chunk = fread($socketConn, 128); print $chunk; } fclose($socketConn);
Сделайте две вещи:
Отключите буфер вывода пользовательского пространства, либо …
В глобальном масштабе, либо …
output_buffering
в php.ini или Отключение output_buffering
в вашей конфигурации Apache, используя
php_flag "output_buffering" Off
или просто для сценария, о котором вы заботитесь, либо …
ob_end_flush()
или ob_end_clean()
Кроме того, отключите буфер вывода на уровне сервера столько, сколько вы можете, либо:
ob_implicit_flush()
в начале вашего скрипта или flush()
после каждого выражения echo
или другого оператора, который добавляет вывод в тело ответа Смутно, есть два уровня буферизации, которые могут быть релевантными, и документация по PHP плохо справляется с различием между ними.
Первый уровень обычно упоминается в документах PHP как «выходной буфер». Этот уровень буферизации влияет только на вывод в тело ответа HTTP, а не на заголовки. Вы можете включить буферизацию вывода с помощью ob_start()
и отключить ее с помощью ob_end_flush()
или ob_end_clean()
. Вы также можете запустить все свои скрипты с буферизацией вывода с использованием опции output_buffering
в php.ini.
Значение по умолчанию этого параметра для производственных версий php.ini составляет 4096, что означает, что первые 4096 байтов вывода будут буферизованы в выходном буфере, после чего он будет сброшен и буферизация вывода будет отключена.
Вы можете отключить этот уровень буферизации глобально, установив output_buffering
в Off
в файле php.ini (или используя
php_flag "output_buffering" Off
в вашем Apache config, если вы используете Apache). Кроме того, вы можете отключить его для одного сценария, вызывая ob_end_clean()
или ob_end_flush()
в начале скрипта.
Помимо выходного буфера – это то, что руководство PHP называет как «буфер записи», плюс любая система буферизации, которую имеет ваш веб-сервер. Если вы используете PHP с Apache через mod_php
и не используете mod_gzip
, вы можете вызвать flush()
чтобы очистить их; с другими бэкендами, это тоже может сработать, хотя руководство сдержанно дает заверения:
Описание
void flush ( void )
Сбрасывает буферы записи PHP и любые внутренние ресурсы PHP (CGI, веб-сервер и т. Д.). Это позволяет сделать текущие выходные данные в браузере с несколькими оговорками.
flush () не сможет переопределить схему буферизации вашего веб-сервера и не влияет на буферизацию на стороне клиента в браузере. Он также не влияет на механизм буферизации вывода пользовательского пространства PHP. Это означает, что вам придется вызывать оба ob_flush () и flush (), чтобы очистить выходные выходные буферы, если вы их используете.
Существует также несколько способов, с помощью которых PHP может автоматически вызывать flush()
каждый раз, когда вы echo
отзываете что-либо (или делать что-либо еще, что выводит результат на тело ответа).
Первый – вызов ob_implicit_flush()
. Обратите внимание, что эта функция обманчиво названа; учитывая его префикс ob_
, любой разумный человек ожидает, что он повлияет на «выходной буфер», как и ob_flush
, ob_flush
и т. д. Однако это не так; ob_implicit_flush()
, например flush()
, влияет на выходной буфер на уровне сервера и никак не взаимодействует с выходным буфером, контролируемым другими функциями ob_
.
Второй – глобальное включение неявной промывки, установив флаг implicit_flush
в On
в вашем php.ini. Это эквивалентно вызову ob_implicit_flush()
в начале каждого скрипта. Обратите внимание, что руководство советует против этого, загадочно ссылаясь на «серьезные последствия для производительности» , некоторые из которых я исследую в этом касательном ответе .
Вместо отключения буферизации вывода вы можете просто вызвать flush()
после каждой операции чтения. Это позволяет избежать проблем с конфигурацией сервера и делает ваш скрипт более переносимым.
Мы можем предоставить приведенный ниже код в файле .htaccess для отключения буферизации вывода в PHP
php_flag "output_buffering" off
Буферизация вывода может быть многослойной, и у меня были случаи, когда более ранний код делал несколько уровней. Это очистит их всех.
while (ob_get_level()) ob_end_clean(); // or ob_end_flush() if you want the contents of the buffer.
Я знаю, что этот вопрос немного устарел, но, вернувшись к этому вопросу, вы можете отключить буферизацию вывода на сценарии на основе сценария, например:
if (ob_get_level()) ob_end_clean();
Это должно отключить всю буферизацию вывода для любого сценария, который следует за ней.