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.
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
--
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
--
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