Mjpeg Frame Grabber PHP scripts

Introduction

This topic is actually a refactoring of some good inputs from MjpegProxyGrab topic.

It is for the moment two scripts in PHP that can grab a simple frame from the Motion mjpeg stream.

Detailed Description

All the mjpeg frame grabbers do this

  • Browser connects to the PHP script on Apache server. The PHP program is made to it appears to be a graphics file.
  • The PHP program connects to the Motion webcam port
  • Motion starts streaming by sending an mjpeg header followed by the picture frames. Mjpeg is simply a stream of jpegs seperated by a boundary string and short header.
  • The PHP program receives the first picture frame of the mjpeg stream and disconnects right after.
  • The PHP program send the jpeg picture only to the client browser with headers like it was a jpeg file that was fetched from the Apache server.
  • The PHP program terminates.

Program 1

Oviously having a C program (like nph-proxygrab from MjpegProxyGrab) is good for performance reasons, but here is a 10 line php script that amounts to the same thing:

<?
set_time_limit(0);
$fp = fsockopen ("127.0.0.1", 8080, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)<br>\n";
} else {
    fputs ($fp, "GET / HTTP/1.0\r\n\r\n");
    while ($str = trim(fgets($fp, 4096)))
       header($str);
    fpassthru($fp);
    fclose($fp);
}
?>
You may need to change the port 8080 to one for your system.

-- MikeLees - 09 Oct 2004

Program 2

mjgrab-php

grabs one picture from mjpeg stream

<?

$camurl="http://127.0.0.1:8080/";

$boundary="\n--";

$f = fopen($camurl,"r") ;

   if(!$f)
   {
        //**** cannot open
        echo "error";
   }
    else
  {
        //**** URL OK
         while (substr_count($r,"Content-Length") != 2) $r.=fread($f,512);

         $start = strpos($r,'');
         $end   = strpos($r,$boundary,$start)-1;
         $frame = substr("$r",$start,$end - $start);

         header("Content-type: image/jpeg");
         echo $frame;

   }


fclose($f);

?>

You may need to change the port and ip to one for your system.

-- TWikiGuest - 25 Apr 2005

Program 3

This might be a more useful image grabber than the one listed above: it can use GD. Be sure to check your /etc/php.ini file. I've found that if you have display_errors = On you may have problems which the code attempts to handle nicely. Some systems default to display_errors = On while some (Fedora) do not, be sure to check yours first. Otherwise you will receive errors like this Warning: fopen(): php_hostconnect:.

Peter's PHP alternative below is interesting. It shows an alternative picture (could be an unavailable picture) when the Motion stream is not available -- KennethLavrsen - 21 May 2005

<?

$camurl="http://127.0.0.1:8080/";

$boundary="\n--";

$f = fopen($camurl,"r");

if( ! $f ) {
 //**** Failed opening the socket
 if( function_exists( 'imagecreatetruecolor' ) && $img = imagecreatetruecolor( 320, 200 ) ) {
  // Display an image if we have GD
  $logo = imagecreatefromgif( "http://domain/not-available-screen.gif" );
  $font = 1;

  header( "Content-type: image/jpeg" );

  imagealphablending( $img, 1 );
  imagealphablending( $logo, 1 );

  $img_w = imagesx( $img );
  $img_h = imagesy( $img );
  $logo_w = imagesx( $logo );
  $logo_h = imagesy( $logo );

  $bgc = imagecolorallocate( $img, 255, 255, 255 );
  $tc = imagecolorallocate( $img, 0, 0, 0 );
  $dc = imagecolorallocate( $img, 255, 0, 0 );

  imagefill( $img, 0, 0, $bgc );

  imagerectangle( $img, 0, 0, $img_w-1, $img_h-1, $dc );
  imageline( $img, 0, 0, $img_w-1, $img_h-1, $dc );
  imageline( $img, 0, $img_h-1, $img_w-1, 0, $dc );

  imagecopy( $img, $logo, 0, 0, 0, 0, $logo_w, $logo_h );
  imagestring( $img, $font, 5, $logo_h, "Error accessing $camurl", $tc );

  $date = time();
  $date = date( 'Ymd-His (O T)', $date );
  imagestring( $img, $font, 5, $logo_h + imagefontheight( $font ) + 1, $date, $tc );

  imagejpeg( $img );

  imagedestroy( $img );
  imagedestroy( $logo );
 } else {
  // Display an error if we do not have GD
  header( "Content-type: text/html" );
  echo "<html><body><h1>Error</h1>Error accessing $camurl";
  echo "</body></html>";
 }
} else {
 //**** URL OK
 // Transfer the image...
 while (substr_count($r,"Content-Length") != 2) $r.=fread($f,512);

 $start = strpos($r,'');
 $end   = strpos($r,$boundary,$start)-1;
 $frame = substr("$r",$start,$end - $start);

 header("Content-type: image/jpeg");
 echo $frame;
}

fclose($f);

?>

-- PeterS - 20 May 2005

Installation and Users Guide

You need Apache with PHP support to use the programs. From your webpage you call the program inside an <IMG src="blabla.php"> HTML tag.

Comments and Bug Reports


Feel free to add more PHP scripts that grabs an image from the Motion mjpeg stream.

-- KennethLavrsen - 21 Dec 2005

Program 2 isn't working correctly - I get something, but it's not a valid jpeg image frown

-- RomanGaufman - 23 Jul 2008

Nor does Program 3 actually - only Program 1 is working correctly but of course not too helpful as firefox doesn't seem to open the stream if you close it and open it again -- only if firefox is restarted frown

-- RomanGaufman - 23 Jul 2008

To get program 2 to work, replace the line: $start = strpos($r,''); with $start = strpos($r,"\xff");

and it will work.

if you are using php-cgi, don't use Program 1, it will stay in the process forever...

-- CalvinTee - 24 Aug 2008

Program 2 + Socket from Program 1

<?

$ip=127.0.0.1;

$port=8081;

$timeout=1; //sec

$fp = fsockopen ($ip, $port, $errno, $errstr, $timeout);

if (!$fp) { echo "$errstr ($errno)\n"; } else {

fputs ($fp, "GET / HTTP/1.0\r\n\r\n"); // delete?

while (substr_count($r,"Content-Length") = 2) $r.=fgets($fp,512);

$start = strpos($r,"\xff");

$end = strpos($r,"\n--",$start)-1;

$frame = substr($r,$start,$end-$start);

header('Content-type: image/jpeg');

echo $frame;

fclose($fp);

?>
 

-- OstroukhovZamir - 03 Mar 2009

Note that some camera's use different boundary strings and that not all camera's send the Content-length string. If this is the case the Content-type can also be used to detect the frame start/end.

This website lists a few boundary strings: http://www.jpegcameras.com

-- Ded3D44 - 09 Oct 2009

<? $camurl = "http://127.0.0.1:8081/"; $boundary = "\n--"; $r=""; $fpr = ""; $fp = fopen($camurl, "r"); $start_jpg = 0xFFD8; $counter = 0; if(!$fp) { //**** cannot open echo "error"; } else { //**** URL OK while ($counter = 2) { if (substr_count($r,"Content-Length") == 1) { $counter ++; } $r = fread($fp,512); $fpr = $fpr.$r; } $start = strpos($fpr,$start_jpg)-1; $end = strpos($fpr,$boundary,$start); $frame = substr("$fpr",$start,$end - $start); header("Content-type: image/jpeg"); echo $frame; } fclose($fp); ?>

//Raspberry Pi + apache2 + php5 + motion

//Regards //Maciek

-- MaciejS - 20 May 2014

<? $camurl = "http://127.0.0.1:8081/"; $boundary = "\n--"; $r=""; $fpr = ""; $counter = 0; $start_jpg = 0xFFD8; $fp = fopen($camurl, "r"); if(!$fp) { //**** cannot open echo "error"; } else { //**** URL OK while ($counter = 2) { if (substr_count($r,"Content-Length") == 1) { $counter ++; } $r = fread($fp,512); $fpr = $fpr.$r; } $start = strpos($fpr,$start_jpg)-1; $end = strpos($fpr,$boundary,$start); $frame = substr("$fpr",$start,$end - $start); header("Content-type: image/jpeg"); echo $frame; } fclose($fp); ?>

-- MaciejS - 20 May 2014

RelatedProjectsForm edit

ProjectSummary PHP scrips for grabbing a single image from a Motion mjpeg stream
ProjectStatus Stable
ReleaseVersion N/A
ProjectSubmitter KennethLavrsen
Topic revision: r6 - 20 May 2014, MaciejS
 
Motion - Mjpeg Frame Grab PHP
Copyright 1999-2014 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Please do not email Kenneth for support questions (read why). Use the Support Requests page or join the Mailing List.
This website only use harmless session cookies. See Cookie Policy for details. By using this website you accept the use of these cookies.