如何通过 PHP 代理以 HTTPS 方式流式传输 HTTP MJPG。

huangapple go评论69阅读模式
英文:

How to stream a http mjpg with a php proxy via https

问题

这是您提供的PHP脚本,用于通过HTTP加载MJPEG流并通过HTTPS输出。然而,它只生成一个损坏的图像。

英文:

I have this php script which should load a mjpg stream via HTTP and output it via HTTPS. However, all it produces is a broken image:

<?php
 function proxyMjpegStream($url) {
   $ch = curl_init($url);
   curl_setopt($ch, CURLOPT_HEADER, false);
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
   curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
   curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
   curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
   curl_setopt($ch, CURLOPT_TIMEOUT, 30);
   curl_setopt($ch, CURLOPT_BUFFERSIZE, 8192);

   header("Content-Type: multipart/x-mixed-replace; boundary=myboundary");

   curl_exec($ch);
   curl_close($ch);
 }

 // Get the URL of the MJPEG stream to proxy
 if (isset($_GET['url'])) {
   $mjpegUrl = $_GET['url'];

   // Validate that the URL is a valid HTTP source
   if (filter_var($mjpegUrl, FILTER_VALIDATE_URL) && strpos($mjpegUrl, 'http://') === 0) {
     proxyMjpegStream($mjpegUrl);
     exit;
   }
 }

  // Invalid or missing MJPEG URL parameter
  header("HTTP/1.0 400 Bad Request");
  echo "Invalid MJPEG URL";
?>

答案1

得分: 2

以下是翻译好的部分:

在进行一些研究后,您可以使用以下函数在curl中进行流传输:

curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'streamCallback');

并创建一个回调函数:

function streamCallback($curl, $data) {
    // 处理接收到的数据
    echo $data;

    // 返回处理的数据长度
    return strlen($data);
}

您的代码将正常工作,但是在30秒后,流将结束,因为您设置了curl_setopt($ch, CURLOPT_TIMEOUT, 30);

我建议您使用fopen()来流式传输URL,因为cURL主要设计用于发出HTTP请求以获取静态内容。MJPEG流是动态的,不断发送新帧。

默认情况下,cURL为每个请求设置了超时时间。如果服务器需要更长时间发送帧,请求可能会超时,导致流传输中断或出现错误消息。

您可以使用fopen()函数获得最佳体验。以下是使用fopen()进行流传输的示例:

<?php
$videoUrl = 'http://74.142.49.38:8000/axis-cgi/mjpg/video.cgi';

// 为MJPEG设置适当的标头
header('Content-Type: multipart/x-mixed-replace; boundary=myboundary');

// 不断获取和显示帧
while (true) {
    // 打开到视频流的连接
    $stream = fopen($videoUrl, 'rb');

    // 检查流是否有效
    if (!$stream) {
        die('打开流错误');
    }

    // 读取并显示每一帧
    while ($frame = fread($stream, 4096)) {
        // 显示帧
        echo $frame;

        // 刷新输出缓冲区
        ob_flush();
        flush();
    }

    // 关闭流
    fclose($stream);
}
?>
英文:

After doing some research, you can do a stream in curl with this function :

curl_setopt($ch, CURLOPT_WRITEFUNCTION, &#39;streamCallback&#39;);

and create a callback function :

function streamCallback($curl, $data) {
    // Process the received data
    echo $data;

    // Return the length of the data processed
    return strlen($data);
}

Your code will working fine, but after 30 second your stream will end because you set curl_setopt($ch, CURLOPT_TIMEOUT, 30);

My suggest for stream a url is using fopen() because cURL is primarily designed for making HTTP requests to fetch static content. MJPEG streams are dynamic and continuously sending new frames.

By default, cURL has a timeout set for each request. If the server takes longer to send frames, the request may time out, resulting in interrupted streaming or error messages.

You may use fopen() function for best experience.
This is example using stream with fopen.

&lt;?php
$videoUrl = &#39;http://74.142.49.38:8000/axis-cgi/mjpg/video.cgi&#39;;

// Set the appropriate headers for MJPG
header(&#39;Content-Type: multipart/x-mixed-replace; boundary=myboundary&#39;);

// Continuously fetch and display frames
while (true) {
    // Open a connection to the video stream
    $stream = fopen($videoUrl, &#39;rb&#39;);

    // Check if the stream is valid
    if (!$stream) {
        die(&#39;Error opening stream&#39;);
    }

    // Read and display each frame
    while ($frame = fread($stream, 4096)) {
        // Display the frame
        echo $frame;

        // Flush the output buffer
        ob_flush();
        flush();
    }

    // Close the stream
    fclose($stream);
}
?&gt;

答案2

得分: 1

以下是翻译好的部分:

"Not really an answer to the question, Anas has that covered, but bears mentioning regardless, and doesn't fit inside a comment.

You're going to run into trouble writing code blocks like this:

 // Get the URL of the MJPEG stream to proxy
 if (isset($_GET['url'])) {
   $mjpegUrl = $_GET['url'];

   // Validate that the URL is a valid HTTP source
   if (filter_var($mjpegUrl, FILTER_VALIDATE_URL) && strpos($mjpegUrl, 'http://') === 0) {
     proxyMjpegStream($mjpegUrl);
     exit;
   }
 }

  // Invalid or missing MJPEG URL parameter
  header("HTTP/1.0 400 Bad Request");
  echo "Invalid MJPEG URL";

If you keep deferring your error conditions to the end, and enclosing your non-error conditions in if(){} blocks you run into two problems.

  1. The condition that triggered the error becomes more and more disconnected from where the error message is generated.
  2. Your "happy path" code gets buried further and further into nested if(){} blocks, known as the Arrow Anti-Pattern.

You can reformat something like:

if( good ) {
  if( also good ) {
    do_thing();
    exit();
  } else {
    raise_error('also bad');
  }
}
raise_error('bad');

To:

if( ! good ) {
  raise_error('bad');
}
if( ! also good ) {
  raise_error('also bad');
}
do_thing();

It's not a written-in-stone rule, but keeping it in mind can help avoid writing disjointed or confusing code blocks, or code blocks that eventually extend off the right side of the page."

英文:

Not really an answer to the question, Anas has that covered, but bears mentioning regardless, and doesn't fit inside a comment.

You're going to run into trouble writing code blocks like this:

 // Get the URL of the MJPEG stream to proxy
 if (isset($_GET[&#39;url&#39;])) {
   $mjpegUrl = $_GET[&#39;url&#39;];

   // Validate that the URL is a valid HTTP source
   if (filter_var($mjpegUrl, FILTER_VALIDATE_URL) &amp;&amp; strpos($mjpegUrl, &#39;http://&#39;) === 0) {
     proxyMjpegStream($mjpegUrl);
     exit;
   }
 }

  // Invalid or missing MJPEG URL parameter
  header(&quot;HTTP/1.0 400 Bad Request&quot;);
  echo &quot;Invalid MJPEG URL&quot;;

If you keep deferring your error conditions to the end, and enclosing your non-error conditions in if(){} blocks you run into two problems.

  1. The condition that triggered the error becomes more and more disconnected from where the error message is generated.
  2. Your "happy path" code gets buried further and further into nested if(){} blocks, known as the Arrow Anti-Pattern.

You can reformat something like:

if( good ) {
  if( also good ) {
    do_thing();
    exit();
  } else {
    raise_error(&#39;also bad&#39;);
  }
}
raise_error(&#39;bad&#39;);

To:

if( ! good ) {
  raise_error(&#39;bad&#39;);
}
if( ! also good ) {
  raise_error(&#39;also bad&#39;);
}
do_thing();

It's not a written-in-stone rule, but keeping it in mind can help avoid writing disjointed or confusing code blocks, or code blocks that eventually extend off the right side of the page.

huangapple
  • 本文由 发表于 2023年5月26日 01:27:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76334895.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定