skip to Main Content

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";
?>

2

Answers


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

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

    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.

    <?php
    $videoUrl = 'http://74.142.49.38:8000/axis-cgi/mjpg/video.cgi';
    
    // Set the appropriate headers for MJPG
    header('Content-Type: multipart/x-mixed-replace; boundary=myboundary');
    
    // Continuously fetch and display frames
    while (true) {
        // Open a connection to the video stream
        $stream = fopen($videoUrl, 'rb');
    
        // Check if the stream is valid
        if (!$stream) {
            die('Error opening stream');
        }
    
        // 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);
    }
    ?>
    
    
    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search