Recording live stream from IP camera (MJPEG Compression) - php

I have a live stream from a Tenvis IP camera through http live streaming and its in mjpeg compression.
I am trying to save it to a file, and I have tried using php to do this. my code looks like this:
<?php
$input = fopen("http://xxx.xxx.xxx.xxx:81/videostream.cgi?user=user&pwd=admin&resolution=8");
$output = fopen("video.mpg", "c+");
$end = time() + 60;
do {
fwrite($output, (fread($input, 30000)), 30000);
} while (time() <= $end);
fclose($output);
fclose($input);
echo "<h1>Recording</h1>";
?>
The code I have creates the file but doesn't write anything to it. Any suggestions will be appreciated

According to the Wikipedia page about MJPEG (http://en.wikipedia.org/wiki/Motion_JPEG#M-JPEG_over_HTTP), the MJPEG stream over HTTP is basically a sequence of JPEG frames, accompanied by a special mime-type. In order to capture these and save them to a video file, I am not sure you can simply write the incoming data to an .mpg file and have a working video.
To be honest, I am not quite sure why your script does not write anything at all, but I came across the following page, which, although it is written for specific software, provides examples on how to capture an MJPEG stream and pass it on to a browser:
http://www.lavrsen.dk/foswiki/bin/view/Motion/MjpegFrameGrabPHP
You could try one of their examples, and instead of passing it to the browser, save it to a file. You can see they read one image at a time:
while (substr_count($r,"Content-Length") != 2) $r.=fread($input,512);
$start = strpos($r,'ΓΏ');
$end = strpos($r,$boundary,$start)-1;
$frame = substr("$r",$start,$end - $start);
If this does fix the stream capturing part but not saving it as a video, another option would be to save all frames individually as a JPEG file, then later stitch them together using a tool such as ffmpeg to create a video: Image sequence to video quality
Update
If you decide to take the ffmpeg road, it is also possible to capture the stream using ffmpeg only. See this question for an example.
Hope this helps.

Most of the time, when a camera supports mjpeg, it also supports rtsp and as such you might want to pursue that as a solution for what you are trying to accomplish. With that, its fairly simple to record using an app like VLC.

Related

Streaming a video using PHP and manipulating it using FFMPEG

I use the class in this tutorial to open a mp4 file in PHP and stream it to HTML5 video player and everything works fine. Now I would like to do some manipulations to the stream on the fly. e.g: adding a watermark to the stream. I am not sure if it can be done using ffmpeg command or PHP-FFMpeg.
The class in the mentioned tutorial opens the file (or the range of the file that is needed by the player), into the $data variable:
$data = fread($this->stream, $bytesToRead);
echo $data;
flush();
I guess I need to pass the $data to ffmpeg somehow, add the watermark and then echo() the manipulated $data instead of the original one. But I am not sure if it's possible or not, and if it is, how it can be done? I don't know how to open a variable in ffmpeg instead of a file and how to get the output as a variable, and not a file.
Any help would be appreciated.

Download a percentage of a video with curl

I have some videos that are hosted on S3 (.mp4 and .mov) some of which are rather large (1.2GB+).
I want to get the first frame from each video using the PHP wrapper for FFmpeg but I don't want to have to download the full file first.
What I really want to do is download a certain percentage of the file, something like 2%, so that I can guarantee that I will get the first frame.
I found a way to download 1mb of the file here: https://code.i-harness.com/en/q/c09357
However, it is the following chunk of this code that I don't really understand how it is only downloading 1mb.
function myfunction($ch, $data) {
$length = fwrite($this->fh, $data);
$size=&$this->size;
if($length === FALSE) {
return 0;
} else {
$size += $length;
}
// Downloads 1MB.
return $size < 1024 * 1024 * 1 ? $length : 0;
}
To me that says set the size to be the size of the file and then if the size is less than 1mb return the length, else return 0.
Now, I know it does work because I have run it, but I don't know how it works so that I can convert this into getting the percentage of the file.
Downloading 1 or 2 MB of the file is fine for the smaller files and the mp4 files, however the .mov files fail to get the first frame if it is less than about 20mb and some frames throw a division by zero error when getting the frame, I guess from the above function returning 0.
Could anyone shed some light on how all of this is working please, or even better if you could suggest an improvement?
myfunction is almost certainly set as the CURLOPT_WRITEFUNCTION callback function for curl_exec, and if that function returns 0 (or any number other than the size of $data), then curl will terminate the transfer, and curl_exec will return the CURLE_ABORTED_BY_CALLBACK error code. thus after you've downloaded >=1 mebibyte, curl_exec will stop with the CURLE_ABORTED_BY_CALLBACK error.
What I really want to do is download a certain percentage of the file, something like 2%, so that I can guarantee that I will get the first frame. - depending on the movie encoding, the first mebibyte may not be enough. there are some encoding schemes (as a specific example, .mpeg movies can be encoded this way) where you need a few bytes from the end of the file to render the first frame (iirc for .mpeg it's called the MOOV Atom - on mpeg movies where the MOOV atom is at the end of the file, you need a few bytes from the end of the file to render the first frame. for all streaming-optimized .mpeg movies, the MOOV atom is at the beginning of the file, not the end, and your 1st mebibyte scheme would work, but if it's at the end your scheme won't work unless the whole movie is <1 mebibyte)
a much better approach is to just let ffmpeg deal with it. ffmpeg will know how much data to download, and will attempt to only download the required parts, instead of the whole movie, and you'd need a program like ffmpeg to extract the first frame later anyway.
try
function getFirstFrameAsJpg(string $url):string{
if(file_exists("/dev/null")){
$ret=shell_exec("ffmpeg -i ".escapeshellarg($url)." -f image2pipe -frames 1 -r 1 -c:v:1 jpeg - 2>/dev/null");
}else{
// windows, probably, where /dev/null isn't supported but NUL works the same way.
$ret=shell_exec("ffmpeg -i ".escapeshellarg($url)." -f image2pipe -frames 1 -r 1 -c:v:1 jpeg - 2>NUL");
}
return $ret;
}
it will return the first frame of the video in the url as the binary for a .jpg image. (meaning you can do file_put_contents('image.jpg',getFirstFrameAsJpg($url)); - interestingly, if ffmpeg is not installed, $ret will be NULL, which means if you use strict_types=1, you will get an exception, otherwise you will get an empty string. )
ps before you allow potential hackers to specify the url for this function, make sure to validate that it is indeed a http url, as i did not consider the security implications of letting hackers run getFirstFrameAsJpg("/etc/passwd") or similar.
if you need to download with a bunch of headers, consider setting up a proxy scheme for ffmpeg where ffmpeg is told to download from a unique proxy-url instead, and still let ffmpeg deal with which parts of the movie to download, and make sure to implement the http range header for such a proxy, as ffmpeg will need it if extracting the 1st frame from a movie where the last part of the movie is required to extract the first frame.
(thanks to c_14 # freenode #ffmpeg for the image2pipe command)

Downloading youtubevideos to server with php

So i want to download youtubevideos as mp3 to my server, so i can listen them easily as mp3.
I have some ecperience in PHP, and second to none in youtube API, but if i have understood correctly youtubevideos are loaded in chunks, and 1 of them is the mp3 soundtrack of the video.
If that is incorrect, could you advice other way to do the downloading?
To this point i have managed to do small php script to get the nessessary information to save the audio.
$id = $_POST["A"];
$xmlData = simplexml_load_string(file_get_contents("http://gdata.youtube.com/feeds/api/videos/{$id}?fields=title"));
$title = (string)$xmlData->title;
$bad = array_merge(
array_map('chr', range(0,31)),
array("<", ">", ":", '"', "/", "\\", "|", "?", "*"));
$result = str_replace($bad, "-", $title);
$file = "$result.mp3";
fopen("$file", 'w+') or die('Cannot open file: '."$file");
//echo "http://youtubeinmp3.com/fetch/?video=http://www.youtube.com/watch?v=$id";
$current = file_get_contents("$file");
//$current .= "http://youtubeinmp3.com/fetch/?video=http://www.youtube.com/watch?v=$id";
file_put_contents("$file", $current);
(post A is coming from my html part of code and it includes youtube video ID)
So, in that code i dont exactly use youtube to download the mp3 track...
so it would be great if any of you would know how to download it directly from youtube, and how to save it, because by my logick that code should perfectly work, and it does, untill the saving the audio, it saves about 50 bits and then stops, i dont get any errors or anything so its realy hard to debug.
Also if you notice any errors os better ways to do things in my code pleace notify me.
Also sorry for my bad english i m from finland, and yes sorry for crappy code, this is actualy first practical program i would be going to use :)
Youtube API doesn't provide direct links to audio or video content, only thumbnails. The only way is to parse regular page using regular expression to extract link to media. Thats how all "getfromyoutube" sites doing.
After you get content you can process it with ffmpeg or other tool. I don't think there is separate mp3 track. Normally audio track is embedded to mp4 container.
Yet it is illegal and you shouldn't doing this. e.t.c. e.t.c.
I warned you ;)

How can I handle large files in my PHP-based video converter?

I have a video converter. Here's how it works, you give the URL to the video, it downloads it to the server, then it converts it to mp3. So it works, but the problem is anything over 10 MB (which is only about 30 seconds) crashes the server. I need to know how to upload it in parts, so it doesn't crash the server.
file_put_contents($dest,file_get_contents($url));
The best approach is to download content in chunks. A nice method for doing so can be found in an answer here. In the $callback function parameter, you can pass a method to convert and write bytes being read.
file_get_contents_chunked($url, 4096, function($chunk, &$handle, $iteration) {
file_put_contents($dest, $chunk, FILE_APPEND);
});

Retrieve ID3 info from m4a file in PHP

I've written some PHP code to pull out ID3 tags from mp3 files. The next step is to do the same with .m4a files. From the research I've done it looks like most m4a files do not use ID3 but instead a format using 'atoms'.
Are there any PHP libraries out there that can parse out these 'atoms'? I've seen some C#/C++ ones but haven't been able to find any PHP ones. Any other guides or documentation would be great as well.
This project handles many audio files formats including AAC (M4A is AAC) :
http://getid3.sourceforge.net/
hope this can help
I came across a similar issue not too long ago and had a look around. The simplest and most effective method (in my opinion) is to use the exec command in order to extra media information.
I based my code on a forum post over at longtailvideo http://www.longtailvideo.com/support/forums/jw-player/setup-issues-and-embedding/9448/how-to-get-video-duration-with-ffmpeg-and-php
<?php
$videofile="/var/video/user_videos/partofvideo.avi";
ob_start();
passthru("/usr/bin/ffmpeg -i \"{$videofile}\" 2>&1");
$duration = ob_get_contents();
ob_end_clean();
$search='/Duration: (.*?),/';
$duration=preg_match($search, $duration, $matches, PREG_OFFSET_CAPTURE, 3);
echo $matches[1][0]; <-- Duration
?>
This script can handle anything ffmpeg is prepared to handle (which is a lot!) I know the above example illustrates a video file but, it will work fine with audio also

Categories