Cross-Platform Libraries
Cross-Platform client libraries for working with recommendations in the OpenRecommender XML or JSON formats are available in the following languages (all implementations are distributed under Apache 2 license, unless otherwise noted):
Javascript
Asynchronous Javascript And Xml (AJAX) can be paired with HTML5 and CSS3 to provide an incredibly rich experience that was not previously possible in the browser. Use of this method of interacting with OpenRecommender is suggested for those who want to display recommendations for web users that will have access to modern browsers, with the latest browser versions.
Using XML as the default output format, we can parse the OpenRecommender REST Web Service responses as follows:
function loadXML(url) {
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
}
else {
xhttp = new ActiveXObject('Microsoft.XMLHTTP');
}
xhttp.open('GET',url,false);
xhttp.send();
return xhttp.responseXML;
}
var xml = loadXML('recommendations.xml'); //use a proxy to load from Web Service
recommendations = xml.getElementsByTagName('recommendations');
recListXML = document.createElement('ol'); //create an ordered list Element to list the Recommendations
recListXML.setAttribute('id', 'recListXML');
document.getElementById('recommendXML').appendChild(recListXML);
recommendation = xml.getElementsByTagName('recommendation');
for (var i = 0; i < recommendation.length; i++) {
title = xml.getElementsByTagName('title')[i].childNodes[0].nodeValue;
image = xml.getElementsByTagName('image')[i].childNodes[0].nodeValue;
link = xml.getElementsByTagName('link')[i].childNodes[0].nodeValue;
desc = xml.getElementsByTagName('description')[i].childNodes[0].nodeValue;
document.getElementById('recListXML').innerHTML += '<li class="'+((i%2===0)?'even':'odd')+'"><a href="'+link+'" title="'+desc+'"><img src="'+image+'"/>'+title+'</a></li>';
}
Using JSON, we would do roughly the same, however the way to access the data differs slightly because of the structure of the JSON:
function loadJSON(url) {
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
}
else {
xhttp = new ActiveXObject('Microsoft.XMLHTTP');
}
xhttp.open('GET',url,false);
xhttp.send();
return xhttp.responseText;
}
var jsonText = loadJSON('recommendations.json'); //here we could call the Web Service directly using JSONp
var json = eval('(' + jsonText + ')');
recommendations = json.recommendations;
recListJSON = document.createElement('ol'); //create an ordered list Element to list the Recommendations
recListJSON.setAttribute('id', 'recListJSON');
document.getElementById('recommendJSON').appendChild(recListJSON);
recommendation = recommendations.recommendation;
for (var j = 0; j < recommendation.length; j++)
{
title = recommendation[j].title.data;
image = recommendation[j].image.data;
link = recommendation[j].link.data;
desc = recommendation[j].description.data;
document.getElementById('recListJSON').innerHTML += '<li class="'+((j%2===0)?'even':'odd')+'"><a href="'+link+'" title="'+desc+'"><img src="'+image+'"/>'+title+'</a></li>';
}
The steps from this are simple, though the use of pure JS makes the code a bit longer (than using an external library or framework such as jQuery):
- Load Recommendations data (in either XML or JSON format)
- Parse out desired data
- Display data on page, styling and marking up as desired
XSLT

Also worth mentioning in the AJAX category are eXtensible Stylesheet Language Transformations (XSLT). If you are already processing and outputting XML anyway, simply reference to the XSL directly by adding the xml-stylesheet directive to the header, just below any leading XML doctype decalarations:
<?xml-stylesheet type="text/xsl" href="recommendations.xsl"?>
The simplest version of an XSL Transformation looks as follows:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method='html' version='1.0' encoding='UTF-8' indent='yes'/>
<xsl:template match="/">
<html>
<head><title>OpenRecommender - XSLT client</title></head>
<body>
<table border="1">
<tr bgcolor="#9acd32">
<th align="left"><h2>OpenRecommender - Recommendations</h2></th>
</tr>
<xsl:for-each select="recommendations/recommendation">
<tr style="color:green;">
<td>
<a href="{link}">
<img src="{image}" alt="{title}" title="{title}" width="80" height="60"/>
<br/>
<xsl:value-of select="title"/>
</a>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Alternatively, in order to ensure you render a human-readable view of the XML data, you could also use a JavaScript Transformer to apply XSLT via JavaScript (since some browsers such as IE do not support inline XSL Transformations but display raw XML instead).
The same thing can even be done in JSON (using a helper library called JSONT, by Stefan Goessner). While the processing speed and rendering of Transformations will be subject to browser quarks and implementations, it may still be useful where only a few Recommendations are shown.
/* setup JSON data and JSON Template (JSONT) */
recommendationsJSON = eval('(' + loadJSON('recommendations.json') + ')');
recommendationsHTML = { "recommendations": "<ol><br/>{$.recommendation}</ol>",
"recommendations.recommendation[*]": "<li id=\"recommendation_{$.identifier}\"><a href='{$.link.data}'><img src='{$.image.data}' width=\"75\" style=\"vertical-align:middle\" />{$.title.data}</a></li>" };
/* transform JSON on page load */
window.onload = function() { document.getElementById("recommend").innerHTML = jsonT(recommendationsJSON, recommendationsHTML); };
Lastly, if you have full control of the server from which you're hosting your implementation and can schedule a data caching job (CRON, AT, etc), then you can actually see some practical uses of XSLT where the Recommendation files your application serves up generate the XML on the server-side personalized to each user, using the same basic XSL with possibly minor style diffrences. You could build an entire application around a simple XSL template (just beware that the rendering may be highly dependent on browser-specific implementations of XSL standards, however as of the latest browser versions most are pretty good anyway).
jQuery
Using jQuery we can simplify the JavaScript approach above by using several of the library's built-in methods for the AJAX call and XML or JSON parsing, which shortens the code to the following:
$.ajax(
url: 'http://openrecommender.org/schema/XML/recommendations.xml',
method: 'GET',
success: function(data) {
/* parse XML response */
}
});
-OR, using the JSON endpoint-
$.getJSON( function() {
url: 'http://openrecommender.org/schema/JSON/recommendations.json',
method: 'GET',
success: function(data) {
/* parse JSON response */
}
});
With jQuery, all of this is taken care of for you though, so all you'll need to do is call our OpenRecommender plugin with one of four parameters (or none for default behavior):
- url
- format
- thumbnail_width
- thumbnail_height
Simply include the following lines of JavaScript somewhere in your HTML DOM, and you'll be off to the races:
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript" src="jquery.openrecommender.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$().OpenRecommender();
});
</script>
Java
Thanks to the JVM, Java is still one of the most cross-platform programming languages. In addition, most of the core infrastructure for OpenRecommender is powered by Java technologies and open source libraries.
In order to run the entire OpenRecommender project on your own infrastructure, you will require knowledge of how to setup and maintain an up to date JVM, as well as how to compile from source and/or run the binary for the project (depending on which option you choose when downloading). Most of the time you'll only need to run the binaries, but you'll also need to know how to download dependencies.
The project's root Maven POM outlines what dependencies are required, and can manage their download and integration automatically at run-time, when building or checking out the source code.
The use of
import java.io.*;
import java.lang.reflect.*;
import java.net.URL;
import org.json.simple.*;
import org.json.simple.parser.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
public class OpenRecommender {
/* Constructor - sets URL/File path and format */
public OpenRecommender(String filePath, String fileFormat) {
this.filePath = filePath;
this.fileFormat = fileFormat;
}
/* GETTER methods
...
*/
/*
* Invokes the methods of an object using the Reflection api.
*/
public void invokeDomParser() {
//Obtain the Class instance
Class openrecommenderDom = OpenRecommenderDOM.class;
//Get the methods
Method[] methods = openrecommenderDom.getDeclaredMethods();
//Create the object that we want to invoke the methods on
OpenRecommenderDOM openrecommenderDomParser = new OpenRecommenderDOM();
try {
Document xml = openrecommenderDomParser.load();
openrecommenderDomParser.toString(xml);
}
catch (Exception ex) {
ex.printStackTrace();
}
}
public static void main(String args[]) {
// XML parsing example
try {
new OpenRecommender().invokeDomParser();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Here is a little example to run the Java parser from the command-line, compiling in one extra dependency not part of the core Java API, namely the JSON-simple library:
javac OpenRecommender.java -cp json-simple-1.1.jar -d bin cd bin java org/openrecommender/OpenRecommender
Apart from using Java on the Desktop, of course, we can also use an Applet to render Java within the browser; however, the simplest way to display Recommendations on the web via Java would be to output HTML within a JSP or Servlet. Lastly, the new JavaFX platform allow dragging back and forth between browser and desktop!
PHP
PHP can be used to display basic recommendations or perform further server-side computation. Using SimpleXML and the native json_encode, we can also reuse virtually the same parser for both XML and JSON recommendation parsing.
<?php
require_once "OpenRecommender.class.php";
//parsing XML
$openrecommender = new OpenRecommender();
// parsing JSON: $openrecommender = new OpenRecommender("recommendations.json", "json");
//DEBUG (string): $openrecommender->display();
//DEBUG (object): $openrecommender->output();
$recommendations = $openrecommender->getRecommendations();
if (count($recommendations) > 0) {
$i = 1;
foreach ($recommendations as $recommendation) {
echo '<br/>'.$i.'. <a href="'.$openrecommender->getRecommendationLink($recommendation).'" title="'.$openrecommender->getRecommendationDescription($recommendation).'"><img src="'.$openrecommender->getRecommendationImage($recommendation).'" width="75" style="vertical-align:middle"/>'.$openrecommender->getRecommendationTitle($recommendation).'</a><br/>';
$i++;
}
}
?>
Python
Python can be used to display basic recommendations or perform further server-side computation. Processing recommendations is of course carried out on the server-side and can be displayed in the command-line/terminal, or, via a Python CGI script.
In order to run Python scripts within a web server you may need to do some modifications, for example, in Apache Web Server I installed mod_wsgi, then added the following to my "httpd.conf" in the top section where many modules are loaded:
LoadModule wsgi_module modules/mod_wsgi.so
Then add one of the following declarations to where the CGI script handler was called:
# PYTHON via compiler CGI redirection
AddHandler cgi-script .cgi .py
ScriptInterpreterSource Registry-Strict
# PYTHON via "mod_wsgi"
PassEnv PYTHONPATH
SetEnv PYTHONUNBUFFERED 1
#AddHandler wsgi-script .py
The last line should be uncommented to force Python to be interpreted by mod_wsgi, which is the preferred method, while I had better luck with it on a Linux/Unix system than on my Windows 7 64-bit system, where the first method worked
Displaying recommendations in a web-accessible (CGI) file:
#!c:/Python27/python.exe -u
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import sys
import cgitb
import cgi
import urllib
import OpenRecommender
cgitb.enable()
DEBUG = 0 #1 to turn debugging on, 0 to turn it off
def main():
#Store url, encoding and format passed with URL parameters calling this script (if any)
params = cgi.FieldStorage()
url = params.getvalue('url')
format = params.getvalue('f')
encoding = params.getvalue('e')
errors = []
if (url is None) or (len(url) == 0):
url = "http://openrecommender.org/schema/XML/recommendations.xml"
errors.append("<br/>** WARNING - Missing URL (using default) **")
if (format is None) or (len(format) == 0):
format = "text/html"
errors.append("<br/>** WARNING - Missing Format (using default) **")
if (encoding is None) or (len(encoding) == 0):
encoding = "utf-8"
errors.append("<br/>** WARNING - Missing Encoding (using default) **")
# HTTP Response Header
print("Content-Type: "+format+";charset="+encoding)
print("")
#perform lookup to get remote content
# urllib.urlopen(url).read()
python_style = '<style>img{border:0;vertical-align:middle} a{color:#fff} ul{list-style-type:none} li{background:#5a9fd4;border-bottom:1px dashed #fff} li:hover{background:#ffd43b} li:hover a{color:#5a9fd4}</style>'
print(python_style)
if len(sys.argv) >= 3:
if (sys.argv[1].find("json") != -1) or (sys.argv[2].lower() == "json") or (url.find("json") != -1) or (format.find("json") != -1):
print(OpenRecommender.openrecommenderJSON(sys.argv[1]))
else:
print(OpenRecommender.openrecommenderXML(sys.argv[1]))
elif len(sys.argv) == 2:
if sys.argv[1].find("json") != -1 or (url.find("json") != -1) or (format.find("json") != -1):
print(OpenRecommender.openrecommenderJSON(sys.argv[1]))
else:
print(OpenRecommender.openrecommenderXML(sys.argv[1]))
else:
print(OpenRecommender.openrecommenderJSON("recommendations.json"))
errors.append(" <hr/>ERROR: Could not read Recommendations file, please call 'index.py RECOMMENDATIONS' from command-line. Defaulting to sample recommendations.")
if DEBUG:
print("".join(errors))
if __name__ == '__main__':
main()
Perl
Perl can be used to display basic recommendations or perform further server-side computation. The benefit of Perl is that it is installed by default on most Unix and/or Linux-based server environments and works with Apache Web Server, Microsoft IIS and many other platforms.
The following summarizes how to process XML and JSON in Perl:
#!/usr/bin/perl
#UNIX
#########################
#!C:\xampp\perl\bin\perl.exe
#-OR-
#!C:/Perl64/bin/perl.exe
#WINDOWS
#########################
use OpenRecommender; # import all functions in OpenRecommener module
use CGI; # Common Gateway Interface, for making the script accessible within a browser...
my $cl = $ARGV[0]; #command-line
my $qp = CGI::param('url'); #query parameter
#default to some URL, try to use query parameter first, then command-line parameter
my $url = (defined($qp) && !($qp eq '')) ? $qp : (defined($cl) && !($cl eq '')) ? $cl : 'http://openrecommender.org/schema/XML/recommendations.xml';
#####################################################################
## JSON - local file, accessed via command-line or script/job scheduler
if (defined($ARGV[0]) && defined($url) && $url =~ m/json/i) {
print openrecommenderJSON($url); # 'recommendations.json'
}
#####################################################################
## XML - local file, accessed via command-line or script/job scheduler
else {
print openrecommenderXML($url); # 'recommendations.xml'
}
NOTE:
The Perl parser adds HTML markup by default, but provides access methods (getters) to get the data and format at it anyway you like.
C#
C# and ASP.net can be used to display basic recommendations or perform further server-side computation of the results. The best approach is to use the latest XML schema to generate your parser dynamically using the XSD Tool provided by Microsoft''s .NET Framework.
A basic parser would look something like this:
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.ServiceModel.Web;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using OpenRecommender; //our XSD-generated parser
namespace OpenRecommender
{
class Program
{
static void Main(string[] args)
{
string recommend = "";
try
{
TextReader reader = new StreamReader("OpenRecommender.xml");
XmlSerializer serializer = new XmlSerializer(typeof(recommendations));
recommendations rec = (recommendations)serializer.Deserialize(reader);
/// For each RECOMMENDATION...
recommendation[] recommendationList = rec.recommendation;
foreach(recommendation recommendation in recommendationList)
{
/// Get each RECOMMENDATION's: attributes as metadata
string identifier = recommendation.identifier;
string from = recommendation.sender;
string to = recommendation.receiver;
string duration = recommendation.title.duration;
/// get each RECOMMENDATION's: TITLE, IMAGE, LINK, DESCRIPTION
recommend += "<li><a href=\""+recommendation.link.Value+"\" title=\""+recommendation.description.Text[0]+"\"><img src=\""+recommendation.image.Value+"\" /> "+recommendation.title.Text[0]+"</a>";
recommend += "("+duration+") From: "+from+" | To: "+to+"</li>";
}
reader.Close();
}
catch(Exception e)
{
recommend = e.Message;
}
System.Console.WriteLine(recommend);
}
}
You would then utilize the parser as follows:
echo creating object bindings to schemas xsd OpenRecommender.xsd /classes /namespace:OpenRecommender echo building program to demonstrate xml serialization csc /out:OpenRecommender.exe /target:exe index.cs OpenRecommender.cs echo -- running OpenRecommender.exe OpenRecommender
In addition to outputting HTML, you could output raw text, convert to JSON, or simply translate to a different XML format that your server-side system expects. One other useful example that I'll briefly demonstrate is using the output within a Silverlight application, for rich output and native support for rich interactions on Browser with the Silverlight plugin installed, or, a Windows Phone. The following lets you embed a "recommendation gallery", powered by a modified version of the excellent XML Image Gallery:
<script type="text/javascript" src="Silverlight.js"></script>
<div id="silverlightRecommendations">
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="ClientBin/XmlImageGallery.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="3.0.40624.0" />
<param name="autoUpgrade" value="true" />
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/>
</a>
</object>
<iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe>
</div>
C++
C++ can be used for processing or caching recommendations, for example for integrating at an Operating System level, from the command-line or in a desktop application with a GUI.
The C++ client is made possible by the excellent Lightweight C++ XML Parser by Dr. Ir. Frank Vanden Berghen.
Here is a snippet of it in action
#include// to get "printf" function #include // to get "free" function #include // for testing, output to console #include "include/xmlParser.h" //link to XML Parser lib using namespace std; int main(int argc, char **argv) { cout << "OpenRecommender v1.0" << endl; // this open and parse the XML file: XMLNode xMainNode = XMLNode::openFileHelper("recommendations.xml"); // this gets ROOT node " " XMLNode recommendations = xMainNode.getChildNode("recommendations"); int n = recommendations.nChildNode("recommendation"); // this prints the "coefficient" value for all the "NumericPredictor" tags: for (int i=0; i < n; i++) { XMLNode recommendation = recommendations.getChildNode("recommendation",i); printf("\n%i).RECOMMENDATION_ID: %i\n",i+1,atoi(recommendation.getAttribute("identifier"))); XMLNode title = recommendation.getChildNode("title"); printf("TITLE: '%s'\n", title.getText()); printf("@lang: '%s'\n", title.getAttribute("language")); XMLNode image = recommendation.getChildNode("image"); printf("IMAGE: '%s'\n", image.getText()); printf("@format: '%s'\n", image.getAttribute("format")); XMLNode link = recommendation.getChildNode("link"); printf("LINK: '%s'\n", link.getText()); printf("@mobile: '%s'\n", link.getAttribute("mobile")); XMLNode description = recommendation.getChildNode("description"); printf("DESC: '%s'\n", description.getText()); printf("@rating: '%s'\n", description.getAttribute("rating")); } return 0; }
It's really as simple as that. THANK YOU DR. BERGHEN!!!
Building and running both can be done from the command-line, but for simplicity's sake I'd recommend using an IDE like CodeBlocks which takes care of building for multiple platforms, using multiple compilers, for you. The command-line would look roughly as follows though:
> g++ OpenRecommender.cpp xmlParser.cpp -o OpenRecommender -lm > ./OpenRecommender
For JSON, we can do roughly the same using the official libjson parser, which is one of the best performing and most complete C++ JSON libraries.
...
The command-line for the JSON parser would look roughly as follows:
> g++ OpenRecommender.cpp libjson.cpp -o OpenRecommender -lm > ./OpenRecommender
If C++ is your language of choice, then you can take this cross-platform client for a test spin below...
DEMO -OR- Download
C
C can be used for processing or caching recommendations on the server-side, or, displaying within a terminal or console window. The easy-to-use EzXML is used for consuming and parsing the XML data, however depending on your usage (frequency of calls and memory available for program) you may wish to use a more powerful library that can do a bit more with less resources (or perform complex tasks such as validation and schema checking), such as libxml.
#include#include "ezxml.h" /** * Simplistic single MAIN method example */ int main(void) { ezxml_t recommendations = ezxml_parse_file("recommendations.xml"), recommendation, title, image, link, desc; const char *id, *sender, *receiver; for (recommendation = ezxml_child(recommendations, "recommendation"); recommendation; recommendation = recommendation->next) { id = ezxml_attr(recommendation, "identifier"); sender = ezxml_attr(recommendation,"sender"); receiver = ezxml_attr(recommendation,"receiver"); title = ezxml_child(recommendation, "title"); image = ezxml_child(recommendation, "image"); link = ezxml_child(recommendation, "link"); desc = ezxml_child(recommendation, "description"); printf("\n--RECOMMENDATION %s %s %s\n", id, sender, receiver); printf("----TITLE: %s\n%s | %s | %s\n", title->txt, ezxml_attr(title,"duration"), ezxml_attr(title,"language"), ezxml_attr(title,"playlist")); printf("----IMAGE: %s\n%s | %s | %s\n", image->txt, ezxml_attr(image,"creator"), ezxml_attr(image,"publisher"), ezxml_attr(image,"format")); printf("----LINK: %s\n%s | %s | %s\n", link->txt, ezxml_attr(link,"format"), ezxml_attr(link,"shortlink"), ezxml_attr(link,"mobile")); printf("----DESC: %s\n%s | %s \n", desc->txt, ezxml_attr(desc,"rating"), ezxml_attr(desc,"review"), ezxml_attr(desc,"tags")); } ezxml_free(recommendations); return 0; }
In order to compile and run an executable (".exe" file), you would use the following commands:
> gcc OpenRecommender.c ezxml.c -o OpenRecommender -lm > ./OpenRecommender
Meanwhile, the JSON format is likely best processed using the lightweight, cross-platform (but non-validating) cJSON parser library. A simple parser looks roughly as follows:
...
Again, building and running the C JSON parserclient can be done as follows:
> gcc OpenRecommender.c cJSON.c -o OpenRecommender -lm > ./OpenRecommender
Since C is pretty platform-specific, I haven't tried to put together a web demo, but you should be able to get up and running fairly quickly just by downloading the package and entering the commands described here via command-line.
Objective-C
Objective-C can be used to reach iOS devices such as iPhone, iPad and iPod Touch with a mobile experience like any other native application, or, to build an app sold in the Apple App Store.
/* Hello World in Objective-C. ** Since the standard implementation is identical to K&R C, ** a version that says hello to a set of people passed on ** the command line is shown here. */ #include#include int main(int argc,char **argv) { id set = [Set new]; argv++;while (--argc) [set add:[String str:*argv++]]; [set do:{ :each | printf("hello, %s!\n",[each str]); }]; return 0; }
Actual implementation coming soon...
Ruby
Ruby enables the use of a number of HTTP, XML, JSON and other useful web development utilities. Adding the Rails framework results in a Ruby on Rails deployment platform that makes it easy to use your Ruby application on the web. You can also simply add Ruby as a CGI script handler for ".rb" files.
Parser examples for both the JSON and XML formats are included, and can be used as follows:
require './OpenRecommender.rb' url = "http://openrecommender.org/schema/XML/recommendations.xml" openrecommender = OpenRecommender.new puts "Content-type: " + format puts "" puts "" print openrecommender.openrecommenderXML(url) puts ""
Flash/Flex
Flash (AS3) is still a useful fallback when HTML5 multimedia, JavaScript or CSS3 features are missing from a given browser, and in addition can be extended to support multiple platforms through the use of Adobe AIR.
// Hello World in ActionScript 3. Place code in the first frame Actions. var t:TextField=new TextField(); t.text="Hello World!"; addChild(t);
Finally, you can use this within a Flex project as follows:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml">
<mx:Label text="Hello World"/>
</mx:Application>


Javascript
jQuery
PHP
Python
Perl
C++
Objective-C
Ruby
Flash





























![FFmpeg is a computer program that can record, convert and stream digital audio and video in numerous formats.[1] FFmpeg is a command line tool that is composed of a collection of free software / open source libraries. FFmpeg](http://openrecommender.org/images/FFMPEG.png)







GeoNames