Posts Tagged ‘php’

An automated and rock-solid encoding engine solution for your youtube-like site.

Tuesday, August 5th, 2008

It was some days ago when i was contacted by one of the visitors of this blog in order to create a “black box” solution to solve the video encoding part of a video sharing website similar to youtube.com.

As you maybe know, i have posted some months ago a really basic script to automate some of the tasks, but, what about integration with your actual website framework ? what about queue management, what about multi-server encoding environment ?

Those are all the usual questions when a potential customer calls, so i spent some days developing a complete black-box solution for this issue, using all open-source software, with the following features.

  • Fully automatic video encoding queue management.
  • Support for 1 to n encoding nodes with fully automatic transaction control (still in beta-testing).
  • Support for the following video formats: Windows Media, Quicktime, DV, Avid DV, DivX, xVid and almost any imaginable format (any format supported by mplayer is actually 100% supported)
  • Support for HE-AAC audio (Stereo near-CD quality sound at 48kbps).
  • Fully automatic thumbnail generation in 3 different positions of the video stream, supported output formats are JPG, PNG and GIF.
  • Fully automatic metadata injection, including all the popular “iTunes like” tags.
  • Fully automatic iPod Touch / iPhone compatible stream generation.
  • Support for more than 1 output format (managed from a presets file).
  • Fully automatic Pixel and Display aspect ratio correction for proper video resizing and scaling.
  • Possibility to select how many CPU cores to use for the encoding process (only on multicore cpu’s)

There are a several more features that I’m still developing. If you are interested and you want more details, you can contact me at: diego (at sign here) massanti.com. I don’t bite ;)

Keeping track of your users hardware / Software with Sparkle and PHP.

Tuesday, April 15th, 2008

Some months ago I started using the amazing Sparkle framework to manage the auto-update features on my MediaInfo Mac application, and it wasn’t until today that I decided to update my Sparkle version to the latest one from the SVN and give it a try, mostly because I’m planning to release upcoming MediaInfo Mac builds as 32 and 64bit Universal Binaries, but that’s a different story.

I have found that with the new Sparkle version, you can kindly ask the user if he/she wants to send anonymous information about the system they are using to run your application, like the MacOS version, amount of RAM, number of processors, language, etc. This is specially useful for many reasons (at least for me), first, I’m starting the process of localizing my application, so that gives me a pretty good idea of what languages are the most used so far, and, finally, it will let me now when I could stop caring about Tiger and start a Leopard only version of my Software.

The idea is pretty simple, you have a PHP script that generates the whole AppCast compatible feed, and all the hardware / software information is passed as url parameters, as in feed.php?osVersion=10.2.5&lang=en … etc.

So, by using good old Google, i found a really good script to generate the AppCast feed, but, i also needed a way to store the tracking data into a DB for later usage, and, i came up with this (really barebones, but working) prototype.

So, i did some minor modifications to the original PHP code; first, i changed the way to parse the version number from the file names, so as long as you use the application_name_1.5.zip pattern it will work, please note that you can put as much revision or subversion numbers as you wish: application_name_1.2.3.4.5.zip should also work.

This is the code snippet as i have it working now on my development server:

<?php
// ----------------------------------------------------------- //
// Script to generate an RSS appcast from folder contents
// Version 1.0.1
//
// (cc) Random Sequence 2007, Some Rights Reserved
//
// Licenced under a creative commons Attribution ShareAlike licence
// http://creativecommons.org/licenses/by-sa/3.0/
// ----------------------------------------------------------- //
// REQUIRES PHP 5 or greater
// Tested with APACHE 1 & 2 on Mac OS X, Debian Linux   

// -------------------- BEGIN CONFIG ------------------------- //

$title = "Downloads";           // Used as feed title in feed readers
$description = "File List";     // Used as feed description in feed readers

// these are the types of files to list in the appcast & their MIME Types. Use lower case.
$fileTypes = array( "zip"=>"application/zip",
                    "tgz"=>"application/x-gtar",
                    "tar"=>"application/x-tar",
                    "dmg"=>"application/octet-stream"
                    );

// -------------------- END OF CONFIG ------------------------ //

$appcastHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?>
<rss version=\"2.0\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/sparkle\">
<channel>
    <title>MediaInfo Mac Software Updates Feed</title>
    <link>http://massanti.com/mediainfo</link>
    <description>AppCast compatible feed for MediaInfo Mac Updates</description>
    <language>en</language>
";
$appcastTemplate = "
        <item>
            <guid  isPermaLink=\"false\">*guid*</guid>
            <title>*title*</title>
            <description><![CDATA[*description*]]></description>
            <pubDate>*pubdate*</pubDate>
            <enclosure
                sparkle:version=\"*version*\"
                type=\"*type*\"
                url=\"*url*\"
                length=\"*length*\" />
        </item>";

$appcastFooter = "
</channel>
</rss>";
$dir = getcwd()."/releases";
$files = scandir("$dir");
$etag = sha1(implode("/",$files));
rsort($files);

// support for conditional fetch
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag) {
    header('HTTP/1.1 304 Not Modified');
    exit;
}

$appcast = $appcastHeader;

$link = "http://".$_SERVER["HTTP_HOST"].$_SERVER["PHP_SELF"];

$appcast = str_replace("*link*",$link,$appcast);
$appcast = str_replace("*title*",$title,$appcast);
$appcast = str_replace("*description*",$description,$appcast);

foreach ($files as $file) {
	preg_match("/.*\.([a-z09]{1,3})$/i",$file,$matches);
	if (isset($matches[1]) && isset($fileTypes[strtolower($matches[1])]) !== false) {
		$appcastFile = $appcastTemplate;
		$folderUrl = "http://".$_SERVER["HTTP_HOST"].substr($_SERVER["PHP_SELF"],0,strrpos($_SERVER["PHP_SELF"],"/"))."/releases/";
		$guid = $folderUrl.sha1($file);
		$path_parts = pathinfo($file);
		$title = mb_convert_case( str_replace("_", " ", $path_parts['filename']) , MB_CASE_TITLE, "UTF-8");
		//$description = preg_replace("/^(.*?)([0-9]+[a-z])\.([a-z09]{1,3})$/i","$1",$file);
		$description = getFileDescription("releases/".$file.".html");
		$pubdate = date("D, d M Y H:i:s",filectime("releases/".$file));
		$type = $fileTypes[strtolower($matches[1])];
		$url = $folderUrl.$file;
		$length = filesize("releases/".$file);  
		preg_match("/(\d+\.)+\d+/", $file, $m_version);
		$version = $m_version[0];
		//$version = preg_replace("/^(.*?)([0-9]+[a-z])\.([a-z09]{1,3})$/i","$2",$file);
		$appcastFile = str_replace("*guid*",$guid,$appcastFile);
		$appcastFile = str_replace("*title*",$title,$appcastFile);
		$appcastFile = str_replace("*description*",$description,$appcastFile);
		$appcastFile = str_replace("*version*",$version,$appcastFile);
		$appcastFile = str_replace("*pubdate*",$pubdate,$appcastFile);
		$appcastFile = str_replace("*type*",$type,$appcastFile);
		$appcastFile = str_replace("*url*",$url,$appcastFile);
		$appcastFile = str_replace("*length*",$length,$appcastFile);                                    
		$appcast .= $appcastFile;
	}
}

// Reads a file called filename.html and returns the HTML output

function getFileDescription($fileName) {
	$handle = @fopen("$fileName", "r");
	$rValue = "";
	if ($handle) {
		while (!feof($handle)) {
			$buffer = fgets($handle, 4096);
			$rValue .= $buffer;
		}
		fclose($handle);
	}
	return $rValue;
}

$appcast .= $appcastFooter;
/*"GET /u.php?UUID=F2958FD0-2EC9-40CA-8171-C63D663AAE90&
		osVersion=10.5.4
		cputype=7
		cpu64bit=1
		cpusubtype=4
		model=P5KC
		ncpu=4
		lang=en
		appName=MediaInfo%20Mac&appVersion=0.7.7.50
		cpuFreqMHz=2400
		ramMB=2048 HTTP/1.1" 200 575 "-" "MediaInfo Mac/0.7.7.50 Sparkle/282"
		*/
// Database Functions

$vars = array('osVersion' , 'cputype', 'cpu64bit', 'cpusubtype', 'model', 'ncpu', 'lang', 'appName', 'appVersion', 'cpuFreqMHz', 'ramMB', 'UUID');
$values = array();
$shouldInsert = true;
foreach ($vars as $item) {
	if (isset($_GET[$item])) {
		$values[$item] = html_entity_decode($_GET[$item]);
	} else {
		if ($item != 'cpu64bit') {
			$shouldInsert = false;
		}

	}
}
if (isset($_GET['cpu64bit'])) {
	$values['cpu64bit'] = html_entity_decode($_GET['cpu64bit']);
} else {
	$values['cpu64bit'] = '0';
}

if ($shouldInsert == true) {
	$mysqli = new mysqli("yourDBHost", "yourDBUser", "yourDBPassword", "yourDBName");

	if (mysqli_connect_errno()) {
	    printf("Connect failed: %s\n", mysqli_connect_error());
	    exit();
	}

	$stmt = $mysqli->prepare("INSERT INTO stats (os_version, cpu_type, cpu_64bit, cpu_subtype, model, n_cpu, lang, app_name, app_version, cpu_freq, ram_mb, UUID) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
	$stmt->bind_param('siississsiis', $values['osVersion'], $values['cputype'], $values['cpu64bit'], $values['cpusubtype'], $values['model'], $values['ncpu'], $values['lang'], $values['appName'], $values['appVersion'], $values['cpuFreqMHz'], $values['ramMB'], $values['UUID']);
	$stmt->execute();
	$stmt->close();

}

header("Content-type: application/xml; charset=UTF-8");
header("ETag: $etag");

echo $appcast;
exit;
?>

As usual, suggestions or improvements are welcome ;)

Easy way to get root privileges, part 1.

Friday, March 28th, 2008

Some hours ago i was asking for some help at the IRC channel of a really large hosting company. The IRC channel is not an official support channel for this company, but some admins and company staff are kind enough to be there in order to help people, etc.
And well, the following took place. Keep in mind that i have replaced both real nicknames and also any occurence of the company name for the sake of privacy.
Enjoy :)

[RandomKid] By the way, the cookies **Hosting Company** use are no good :(
[RandomKid] anther time why, I think they can be poisioned.
[RandomKid] Will try safari now.
[HostingCompanyGuy] why not just bookmark your control panel?
[HostingCompanyGuy] RamdonKid: weren't you the same person that thought you could get root access to a server just because of an "insecure" upload form?
[RandomKid] hmmm good idea
[DiegoMax] ...
* RandomKid doesn't think, me knows.
[HostingCompanyGuy] lol
[HostingCompanyGuy] still waiting for you to show me how
[DiegoMax] kids....
(more…)

Serverside video resizing script for ffmpeg or Mencoder

Monday, October 1st, 2007

Some days ago, while working on a project that involves the re-encoding of a lot of videos coming from several sources, aspect ratios, resolutions, etc, i found myself in the situation where i needed to “standarize” somehow all the videos to a prefixed size in order to place them on a fixed space in a web page.
While most linux tools like ffmpeg or mencoder include native functions to scale or resize the video, they don’t care about the aspect ratios or about the fact that most video encoders expect mod16 values for the height and width values.
So my situation was like this:

I needed to make all the videos 480 pixels high, and scale the width proportionally.

That being said, i came up with this PHP script that i call from inside a bash script:

#!/usr/bin/php
<?php
$cmdWidth = 'midentify '.$argv[1];
$finalHeight = $argv[2];
exec($cmdWidth, $output);

foreach ($output as $value) {
    if (strstr($value, "ID_VIDEO_WIDTH")) {
		
		$width = parseResult($value);
    }
    if (strstr($value, "ID_VIDEO_HEIGHT")) {
    	$height = parseResult($value);
    }
}
$frameSize = $width / $height;
$finalWidth = $finalHeight * $frameSize;
echo getMod16(round($finalWidth)) . "x" . getMod16(round($finalHeight)) . "\n";
function parseResult($line) {
	$v1 = explode("=",$line);
	$v20 = $v1[1];
	
	//var_dump($v2);
	return $v20;
}
function getMod16($number){
	while (fmod($number, 16) != 0) {
		$number ++;
	}
	return $number;
}
?>

This script assumes that you have the “midentify” utility (wich comes with mplayer) installed in your path, and expects 2 arguments, being argument 1 the movie you want to resize and argument 2 the actual height you want to reach.
That being said, suppose you have a movie that is 848 pixels width and 480 pixels high and you want to re-scale it so it fits inside a 400 pixels high space, you would call the script like this:

./resizer mymovie.avi 400

The script will output this:

720x400

You can use later this value as an input parameter for ffmpeg for example.

Before somebody asks, the reason because i used PHP instead of BASH, is simply because bash does not supports floats.