<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2134600781755905611</id><updated>2011-11-23T03:30:06.929+01:00</updated><category term='video codecs'/><category term='Misc'/><category term='travels'/><category term='society'/><category term='spam'/><category term='security'/><category term='web design'/><category term='politics'/><category term='programming'/><category term='electronics'/><category term='computer'/><title type='text'>Truths or lies - decide yourself</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>55</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-3192267255310955146</id><published>2011-10-22T21:58:00.002+02:00</published><updated>2011-10-22T22:49:39.354+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computer'/><title type='text'>On demand audio streaming with icecast</title><content type='html'>The project goal was to make the impossible happen: Turn an icecast streaming server into an audio-on-demand server.&lt;br /&gt;&lt;br /&gt;The background is, that I bought a NAS, which is basically a PC with Atom CPU. After erasing the firmware and installing Ubuntu Server on a 10 TB Raid 5 system, I was thinking what else I could do with the box.&lt;br /&gt;&lt;br /&gt;Live streaming via icecast to my Wifi-radio worked for &lt;a href="http://hirntier.blogspot.com/2008/09/music-from-everywhere-everywhere.html"&gt;some time&lt;/a&gt; now, but this needs a running PC with a soundcard. What I had in my mind, was different:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It should run exclusively on the NAS, no need to switch on a PC&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It should support an arbitrary number of playlists, each one corresponding to an icecast URL.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The upstream mechanism should work &lt;em&gt;on demand&lt;/em&gt; because encoding many mp3 streams in parallel overloads the Atom CPU.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The current song should be shown in the display of the radio&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Song titles&lt;/b&gt;&lt;br /&gt;For the last requirement I added live metadata updating to the API for gmerlin broadcasting plugins. After learning, that Vorbis streams with changing song titles make my radio reboot, I wrote an MP3 broadcasting plugin (with libshout and lame). It seems that later firmware versions for the radio fix the vorbis problem, but the firmware update requires a windows software.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Commandline recorder&lt;/b&gt;&lt;br /&gt;The recording and broadcast architecture for gmerlin was already working reliably, so I wrote a plugin, which takes a gmerlin album (=playlist), shuffles the tracks and makes them available as if it record from a soundcard. In addition, I wrote a commandline recorder, which could be started from a script. There is one script for starting a broadcast:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;$cat start_broadcast.sh&lt;br /&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;&lt;br /&gt;BITRATE=320&lt;br /&gt;NAME="NAS $1"&lt;br /&gt;STATION_DIR="/nas/Stations/lists/"&lt;br /&gt;PASSWORD="secret"&lt;br /&gt;&lt;br /&gt;AUDIO_OPT='do_audio=1:plugin=i_audiofile{album='$STATION_DIR$1':shuffle=1}'&lt;br /&gt;VIDEO_OPT="do_video=0"&lt;br /&gt;METADATA_OPT="metadata_mode=input"&lt;br /&gt;ENC_OPT='audio_encoder=b_lame{server=nas_ip:mount=/'$1':password='$PASSWORD':name='$NAME':cbr_bitrate='$BITRATE'}'&lt;br /&gt;&lt;br /&gt;gmerlin-record -aud $AUDIO_OPT -vid $VIDEO_OPT -m $METADATA_OPT -enc "$ENC_OPT" -r 2&amp;gt;&amp;gt; /dev/null &amp;gt;&amp;gt; /dev/null &amp;amp;&lt;br /&gt;echo $! &amp;gt; $1.pid&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;If you call the script with &lt;code&gt;start_broadcast.sh foo&lt;/code&gt;, it will load the album &lt;code&gt;/nas/Stations/lists/foo&lt;/code&gt; and send the stream to the icecast server, which will make it available under &lt;code&gt;nas.ip:8000/foo&lt;/code&gt;. In addidion, the PID of the process will be written to &lt;code&gt;./foo.pid&lt;/code&gt; so it can be stopped later.&lt;br /&gt;&lt;br /&gt;The foo broadcast can be stopped with &lt;code&gt;stop_broadcast.sh foo&lt;/code&gt;, where the&lt;br /&gt;script looks like:&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;#!/bin/sh&lt;br /&gt;kill -9 `cat $1.pid`&lt;br /&gt;rm -f $1.pid&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Icecast configuration&lt;/b&gt;&lt;br /&gt;No critical options had to be changed in the icecast configuration, except queue-size, which was doubled to 1048576 because it's better for 320 kbps streams.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Icecast stats in awk friendly format&lt;/b&gt;&lt;br /&gt;For the on-demand meachism described below, we also need to get the&lt;br /&gt;running channels and connected clients from the server ideally in an awk friendly&lt;br /&gt;format. This is done by getting the server statistics in xml format and process it&lt;br /&gt;with &lt;code&gt;xsltproc&lt;/code&gt;, a small commandline tool which comes with libxml2:&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;$cat get_stats.sh&lt;br /&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;wget --user=admin --password=secret -O - http://127.0.0.1:8000/admin/stats.xml 2&amp;gt; /dev/null | \&lt;br /&gt;xsltproc stats.xsl - | cut -b 2-&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;If you have two channels foo (1 listener) and bar (2 listeners) it will output&lt;br /&gt;&lt;code&gt;&lt;br /&gt;foo 1&lt;br /&gt;bar 2&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The transformation file &lt;code&gt;stats.xsl&lt;/code&gt; looks like:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&amp;gt;&lt;br /&gt;&amp;lt;xsl:output method="text"/&amp;gt;&lt;br /&gt;&amp;lt;xsl:template match="/"&amp;gt;&lt;br /&gt; &amp;lt;xsl:for-each select="icestats/source"&amp;gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="@mount"/&amp;gt;&lt;br /&gt;    &amp;lt;xsl:text&amp;gt; &amp;lt;/xsl:text&amp;gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="listeners"/&amp;gt;&lt;br /&gt;    &amp;lt;xsl:text&amp;gt;&lt;br /&gt;&amp;lt;/xsl:text&amp;gt;&lt;br /&gt; &amp;lt;/xsl:for-each&amp;gt;&lt;br /&gt;&amp;lt;/xsl:template&amp;gt;&lt;br /&gt;&amp;lt;/xsl:stylesheet&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;b&gt;On demand mechanism&lt;/b&gt;&lt;br /&gt;Now since we have commands for starting, stopping and querying channels, we can start a channel when the first listener connects and stop it after the last listener disconnected. Since icecast doesn't support on demand streaming, we must trick it into doing so. The idea is to put a second http server in front of the icecast server, which handles the connection requests, starts the channel (if necessary) and then does a http redirect to the real icecast url. The icecast server runs on port 8000, the redirection server (to which the listeners connect) runs on port 8001. The redirection server can be built simply within shell scripts using the netcat (traditional) utility. The server script is simple:&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;$cat server.sh&lt;br /&gt;#!/bin/sh&lt;br /&gt;&lt;br /&gt;cd /nas/mmedia/Stations&lt;br /&gt;&lt;br /&gt;while true; do&lt;br /&gt;nc.traditional -l -p 8001 -c ./handle.sh&lt;br /&gt;done&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Whenever a TCP connection on port 8001 arrives, the following handler script is executed:&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;$cat handle.sh&lt;br /&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;# Read request, path and protocol&lt;br /&gt;read REQ URLPATH PROTO&lt;br /&gt;# Read header variables&lt;br /&gt;while true; do&lt;br /&gt;read VAR VAL&lt;br /&gt;if test "x$VAL" = "x"; then&lt;br /&gt;break&lt;br /&gt;fi&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;# Reject anything but GET requests&lt;br /&gt;if test "x$REQ" != "xGET"; then&lt;br /&gt;echo -e "HTTP/1.1 400 Bad Request\r\n\r\n"&lt;br /&gt;exit&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;# Remove leading "/"&lt;br /&gt;FILE=`echo $URLPATH | cut -b 2-`&lt;br /&gt;&lt;br /&gt;# Close unused streams&lt;br /&gt;./clean.sh $FILE&lt;br /&gt;&lt;br /&gt;# Check if we are broadcasting already&lt;br /&gt;RESULT=`./query_station.sh $FILE`&lt;br /&gt;if test "x$RESULT" = "x"; then&lt;br /&gt;./start_broadcast.sh $FILE 2&amp;gt;&amp;gt; /dev/null &amp;amp;&lt;br /&gt;sleep 1&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;# Send redirection header&lt;br /&gt;URL="http://nas_ip:8000/$FILE"&lt;br /&gt;echo -e "HTTP/1.1 307 Temporary Redirect\r\nLocation: $URL\r\n\r\n"&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Here we use 2 additional scripts. &lt;code&gt;clean.sh&lt;/code&gt; stops all streams with zero listeners except the one, which was given as commandline argument.&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;#!/bin/sh&lt;br /&gt;./get_stats.sh | awk -v NAME=$1 '($1 != NAME) &amp;amp;&amp;amp; ($2 == 0) { system("./stop_broadcast.sh " $1) }'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;code&gt;query_station.sh&lt;/code&gt; lists just the number of listeners of the given station:&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;#!/bin/sh&lt;br /&gt;./get_stats.sh | awk -v NAME=$1 '$1 == NAME { print $2 }'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;b&gt;Energy saving mode&lt;/b&gt;&lt;br /&gt;When we just use the radio, the NAS must be switched on manually. The PCs do that automatically with wake-on-lan. The NAS detects, when it is no longer needed and switches off automatically then. This is done by querying the TCP connections to IP addresses other than localhost. If we don't have any external connections for more than 30 minutes, we switch off. The following script can be interesting for many other applications as well. Simply start it during booting:&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;#!/bin/sh&lt;br /&gt;# Switch off after this time&lt;br /&gt;THRESHOLD=1800&lt;br /&gt;# Delay between 2 checks&lt;br /&gt;DELAY=60&lt;br /&gt;&lt;br /&gt;DATE_START=`date +%s`&lt;br /&gt;&lt;br /&gt;while :&lt;br /&gt;do&lt;br /&gt;  CONNECTIONS=`netstat -tn | grep tcp | grep -v " 127\." | wc -l`&lt;br /&gt;  DATE_NOW=`date +%s`&lt;br /&gt;&lt;br /&gt;  if test "x$CONNECTIONS" = "x0"; then&lt;br /&gt;    DATE_DIFF=`echo "$DATE_NOW - $DATE_START - $THRESHOLD" | bc`&lt;br /&gt;    if test $DATE_DIFF -gt "0"; then&lt;br /&gt;      poweroff&lt;br /&gt;      exit&lt;br /&gt;    fi&lt;br /&gt;  else&lt;br /&gt;    DATE_START=$DATE_NOW&lt;br /&gt;  fi&lt;br /&gt;  sleep $DELAY&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Mission accomplished.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-3192267255310955146?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/3192267255310955146/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=3192267255310955146' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/3192267255310955146'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/3192267255310955146'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2011/10/on-demand-audio-streaming-with-icecast.html' title='On demand audio streaming with icecast'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-2170779750556244439</id><published>2010-12-09T01:31:00.003+01:00</published><updated>2010-12-09T01:34:40.360+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>New prereleases</title><content type='html'>Lots of bugs have been fixed after the last prereleases, so here are&lt;br /&gt;new ones:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gmerlin.sourceforge.net/gmerlin-dependencies-20101209.tar.bz2"&gt;http://gmerlin.sourceforge.net/gmerlin-dependencies-20101209.tar.bz2&lt;/a&gt;&lt;br /&gt;&lt;a href="http://gmerlin.sourceforge.net/gmerlin-all-in-one-20101209.tar.bz2"&gt;http://gmerlin.sourceforge.net/gmerlin-all-in-one-20101209.tar.bz2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The good news is, that no new features were added so the code can stabilize better.&lt;br /&gt;&lt;br /&gt;Please test this and report any problems.&lt;br /&gt;&lt;br /&gt;The final gmerlin release is expected by the end of the year&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-2170779750556244439?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/2170779750556244439/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=2170779750556244439' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/2170779750556244439'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/2170779750556244439'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2010/12/new-prereleases.html' title='New prereleases'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-8721323887558709240</id><published>2010-09-18T17:09:00.000+02:00</published><updated>2010-09-18T17:10:51.465+02:00</updated><title type='text'>gmerlin prereleases</title><content type='html'>Some time has gone since the last prereleases, and a lot of bugs have been&lt;br /&gt;fixed since then. So here is another round:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gmerlin.sourceforge.net/gmerlin-dependencies-20100918.tar.bz2"&gt;http://gmerlin.sourceforge.net/gmerlin-dependencies-20100918.tar.bz2&lt;/a&gt;&lt;br /&gt;&lt;a href="http://gmerlin.sourceforge.net/gmerlin-all-in-one-20100918.tar.bz2"&gt;http://gmerlin.sourceforge.net/gmerlin-all-in-one-20100918.tar.bz2&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-8721323887558709240?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/8721323887558709240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=8721323887558709240' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/8721323887558709240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/8721323887558709240'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2010/09/gmerlin-prereleases.html' title='gmerlin prereleases'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-7528114094304732791</id><published>2010-08-07T02:10:00.006+02:00</published><updated>2010-08-07T02:30:34.651+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Gmerlin configuration improvements</title><content type='html'>Up to now, gmerlins configuration philosophy was simple: Export all user settable parameters as possible to the frontends, no matter now important they are. There are 2 reasons for that:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;As a developer, I don't like to decide which configuration options are &lt;i&gt;important&lt;/i&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;As a user I (personally) want to have full control over all program-and plugin settings. Nothing annoys me more in other applications than features, which could easily achieved by the backend, but they are not supported in the frontend.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;The downside of this approach is simple: For an average user, the gmerlin applications are way too complicated. And of course, for me it's also annoying to tweak that many parameters all the time. Now, since I reach a one-zero version, It's time to look at such usability issues.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;A little look behind the GUI&lt;/b&gt;&lt;br /&gt;A configuration dialog can contain of multiple nested sections. If you have more than one section, you see a tree structure on the right, which lets you select the &lt;i&gt;section&lt;/i&gt;. A &lt;i&gt;section&lt;/i&gt; contains all the configuration widgets you can see at the same time. Therefore the code must always distinguish if an action is for a section or for the whole dialog.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Factory defaults&lt;/b&gt;&lt;br /&gt;Most configuration sections now have a button &lt;i&gt;Restore factory defaults&lt;/i&gt;. It does, what the name suggests. You can use this if you think you messed something up.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Presets&lt;/b&gt;&lt;br /&gt;Some configuration sections support presets. You can save all parameters into a file and load them again after. In some situations, presets are per &lt;i&gt;section&lt;/i&gt;. In this case you see the preset menu below the parameter widgets. If the presets are global for the whole dialog window, you see the menu below the tree view. The next image shows a single-section dialog with the preset menu next to the restore button.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/TFypEaKKC8I/AAAAAAAAAJk/7IeIq9_kH_o/s1600/cfg_deint.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 246px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/TFypEaKKC8I/AAAAAAAAAJk/7IeIq9_kH_o/s400/cfg_deint.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5502458737946397634" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The next image shows a dialog with multiple sections. The preset menu is for the whole dialog, the restore button is for the section only.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/TFymsBJnPhI/AAAAAAAAAJc/0H-uxKaFldQ/s1600/cfg_cropscale.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 226px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/TFymsBJnPhI/AAAAAAAAAJc/0H-uxKaFldQ/s400/cfg_cropscale.png" alt="" id="BLOGGER_PHOTO_ID_5502456119893114386" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The presets are designed such, that multiple applications can share them. E.g. an encoding setup configured in the transcoder can be reused in the recorder etc. Presets are available for:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;All plugins (always global for the whole plugin)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Whole encoding setups&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Filter chains&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;There is no reason, not to support presets for other configurations as well. Suggestions are welcome.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-7528114094304732791?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/7528114094304732791/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=7528114094304732791' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7528114094304732791'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7528114094304732791'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2010/08/gmerlin-configuration-improvements.html' title='Gmerlin configuration improvements'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_KslCmtD4qm8/TFypEaKKC8I/AAAAAAAAAJk/7IeIq9_kH_o/s72-c/cfg_deint.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-7759416445390248383</id><published>2010-08-03T20:43:00.002+02:00</published><updated>2010-08-03T20:47:44.390+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Gmerlin prereleases</title><content type='html'>gmerlin prereleases can be downloaded here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gmerlin.sourceforge.net/gmerlin-dependencies-20100803.tar.bz2"&gt;http://gmerlin.sourceforge.net/gmerlin-dependencies-20100803.tar.bz2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gmerlin.sourceforge.net/gmerlin-all-in-one-20100803.tar.bz2"&gt;http://gmerlin.sourceforge.net/gmerlin-all-in-one-20100803.tar.bz2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Highlights of this development iteration:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://hirntier.blogspot.com/2010/05/processing-compressed-streams-with.html"&gt;Pass-through of compressed streams in the transcoder&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Native matroska demuxer with sample accurate seeking in webm files&lt;br /&gt;&lt;/li&gt;&lt;li&gt; VP8 decoding via ffmpeg&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://hirntier.blogspot.com/2010/08/getting-serious-with-sample-accuracy.html"&gt;Lots of work on precise timing and sample accurate seeking&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Presets in the GUI configuration (will blog later about that)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The gmerlin package reaches version one-zero&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Please test this as much as possible and report any problems.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-7759416445390248383?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/7759416445390248383/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=7759416445390248383' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7759416445390248383'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7759416445390248383'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2010/08/gmerlin-prereleases.html' title='Gmerlin prereleases'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-6926273215526744600</id><published>2010-08-01T14:30:00.008+02:00</published><updated>2010-08-01T14:53:34.134+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Getting serious with sample accuracy</title><content type='html'>gmerlin-avdecoder has a sample accurate seek API for some time now. What was missing was a test to prove that seeking happens really with sample accuracy.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Test tool&lt;/b&gt;&lt;br /&gt;The strictest test if a decoder library can seek with sample accuracy is to seek to a position, decode a video frame or a bunch of audio samples. Compare these with the frame/samples you get if you decode the file from the beginning. Of course, the timestamps must also be identical. A tool, which does this, is in &lt;code&gt;tests/seektest.c&lt;/code&gt;. I noticed, that video streams easily pass this test, usually even if no sample accurate access was requested. That's probably because I thought, that video streams are more difficult. So I put more brainload into them. Therefore I'll concentrate on audio streams in this post.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Audio codec details&lt;/b&gt;&lt;br /&gt;When seeking in video streams, you have keyframes, which tell you where decoding of a stream can be resumed after a seek. It's sometimes difficult to implement this, but at least you always know what to do.&lt;br /&gt;&lt;br /&gt;The naive approach for audio streams is to assume, that all blocks (e.g. 1152 samples for mp3) can be decoded independently. Unfortunately, reality is a bit more cruel:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Bit reservoir&lt;/i&gt;&lt;br /&gt;This is a mechanism, which allows to make pseudo VBR in a CBR stream. If a frame can be encoded with fewer bits than allocated for the frame, it can leave the remaining bits to a subsequent (probably more complex) frame. The downside of this trick is, that after a seek, the next frame might need bits from previous frames to be fully decoded.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Oberlapping transform&lt;/i&gt;&lt;br /&gt;Most audio compression techniques work in the frequency-domain, so between the audio signal and the compression stage, there is some kind of fft-like transform.&lt;br /&gt;&lt;br /&gt;Now, for reasons beyond this post, &lt;a href="http://en.wikipedia.org/wiki/Overlap-add_method"&gt;overlapping&lt;/a&gt; transforms are used by some codecs. This means, that for decoding the first samples of a compressed block, you need the last samples of the previous block. The image below shows one channel of an AAC stream for the case that the overlapping was ignored when seeking. You see that the beginning of the frame is not reconstructed properly, because the previous frame is missing.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/TFVp1Zecb7I/AAAAAAAAAJE/hXqlWB01yD0/s1600/overlap.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 306px;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/TFVp1Zecb7I/AAAAAAAAAJE/hXqlWB01yD0/s400/overlap.png" alt="" id="BLOGGER_PHOTO_ID_5500418885995491250" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Both the bit reservoir and the overlapping can be boiled down to a single number, which tells how many sample &lt;i&gt;before&lt;/i&gt; the actual seek point the decoder must restart decoding. This number is set by the codec during initialization, and it's used when we seek with sample accuracy.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Mysterious liba52 behavior&lt;/i&gt;&lt;br /&gt;Even if sample accuracy was achieved, the AC3 streams (which are on DVDs or in AVCHD files) don't achieve bit exactness. The image below shows, that there is no time shift between the signals (which means that gmerlin-avdecoder seeks correctly), but the values are not exactly the same.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/TFVqbtvBptI/AAAAAAAAAJM/tGYI5Sn5Kyk/s1600/memory.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 306px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/TFVqbtvBptI/AAAAAAAAAJM/tGYI5Sn5Kyk/s400/memory.png" alt="" id="BLOGGER_PHOTO_ID_5500419544268777170" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;First I blamed the AC3 dynamic range control for this behavior. Dynamic range compressors always have some kind memory across several frames. But even after disabling DRC, the difference was still there. I would really be curious if that's a principal property of AC3 being non-deterministic or if it's a liba52 bug.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusions&lt;/b&gt;&lt;br /&gt;The table below lists all audio codecs, which were taken into consideration. They represent a huge percentage of all files found in the wild. The next important codecs are the uncompressed ones, but these are always sample accurate.&lt;br /&gt;&lt;table border="1"&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Compression     &lt;/td&gt;&lt;td&gt;Library    &lt;/td&gt;&lt;td&gt;Overlap&lt;/td&gt;&lt;td&gt;Bit reservoir &lt;/td&gt;&lt;td&gt;Bit exact&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;MPEG-1, layer II &lt;/td&gt;&lt;td&gt;libmad    &lt;/td&gt;&lt;td&gt;-      &lt;/td&gt;&lt;td&gt;-             &lt;/td&gt;&lt;td&gt;+&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;MPEG-1, layer III&lt;/td&gt;&lt;td&gt;libmad    &lt;/td&gt;&lt;td&gt;+      &lt;/td&gt;&lt;td&gt;+             &lt;/td&gt;&lt;td&gt;+&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;AAC              &lt;/td&gt;&lt;td&gt;faad2     &lt;/td&gt;&lt;td&gt;+      &lt;/td&gt;&lt;td&gt;? (assumed -) &lt;/td&gt;&lt;td&gt;+&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;AC3              &lt;/td&gt;&lt;td&gt;liba52    &lt;/td&gt;&lt;td&gt;+      &lt;/td&gt;&lt;td&gt;? (assumed -) &lt;/td&gt;&lt;td&gt;- (see image above)&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Vorbis           &lt;/td&gt;&lt;td&gt;libvorbis &lt;/td&gt;&lt;td&gt;+      &lt;/td&gt;&lt;td&gt;-             &lt;/td&gt;&lt;td&gt;+&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;Obtaining the information summarized here was a very painful process with web researches and experiments. The documentation of the decoder libraries regarding sample accurate and bit exact seeking is &lt;i&gt;extremely&lt;/i&gt; sparse if not non-existing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-6926273215526744600?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/6926273215526744600/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=6926273215526744600' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/6926273215526744600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/6926273215526744600'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2010/08/getting-serious-with-sample-accuracy.html' title='Getting serious with sample accuracy'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_KslCmtD4qm8/TFVp1Zecb7I/AAAAAAAAAJE/hXqlWB01yD0/s72-c/overlap.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-2180701064861468863</id><published>2010-05-01T02:19:00.004+02:00</published><updated>2010-05-01T02:41:54.867+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Processing compressed streams with gmerlin</title><content type='html'>As I already mentioned, a main goal of this development cycle is to read compressed streams on the input side and write compressed streams on the encoding side. It's a bit of work, but it's definitely worth it because it offers enormous possibilities:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Lossless transmultiplexing from one container to another&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Adding/removing streams of a file without recompressing the other streams.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Lossless concatenation of compressed files&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Changing metadata of files (i.e. mp3/vorbis tagging)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Quicktime has some codecs, which correspond to image formats (png, jpeg, tiff, tga). Supporting compressed frames can convert single images to quicktime movies and back&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In some cases broken files can be fixed as well&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;General approach&lt;/b&gt;&lt;br /&gt;To limit the possibilities of creating broken files, we are a bit strict about the&lt;br /&gt;codecs we support for compressed I/O. This means, that with the new feature you cannot automatically transfer all compressed streams. For compressed I/O the following conditions&lt;br /&gt;must be met:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The precise codec must be known to gavl. While for decoding it never matters if we have MPEG-1 or MPEG-2 video (libmpeg2 decodes both), for compressed I/O it must be known.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;For some codecs, we need other parameters like the bitrate or if the stream contains B-frames or field pictures.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Each audio packet must consist of an independently decompressable frame and we must know, how many uncompressed samples are contained.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;For each video packet, we must know the pts, how long the frame will be displayed and if it's a keyframe.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Compression support in gavl&lt;/b&gt;&lt;br /&gt;For transferring compressed packets, we need 2 data structures:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;An info structure, which describes the compression format (i.e. the codec). The actual codec is an enum (similar to ffmpegs CodecID), but other parameters can be required as well (see above).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;A structure for a data packet.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Both of these are in gavl in a new header file &lt;a href="http://gmerlin.cvs.sourceforge.net/viewvc/gmerlin/gavl/include/gavl/compression.h?view=markup"&gt;&lt;code&gt;gavl/compression.h&lt;/code&gt;&lt;/a&gt;. Gavl itself &lt;b&gt;never&lt;/b&gt; messes around with the contents of compressed packets, if just provides some housekeeping functions for packets and compression definitions. The definitions were moved here, because it's the only common dependency of gmerlin and gmerlin-avdecoder and I didn't want to define that twice.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;gmerlin-avdecoder&lt;/b&gt;&lt;br /&gt;There are 2 new functions for getting the compression format of A/V streams:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;int bgav_get_audio_compression_info(bgav_t * bgav, int stream,&lt;br /&gt;                               gavl_compression_info_t * info)&lt;br /&gt;&lt;br /&gt;int bgav_get_video_compression_info(bgav_t * bgav, int stream,&lt;br /&gt;                               gavl_compression_info_t * info)&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;They can be called after the track was selected with &lt;code&gt;bgav_select_track()&lt;/code&gt;. If the demuxer doesn't meet the above goals for a stream it's tried with a &lt;a href="http://hirntier.blogspot.com/2009/02/elementary-stream-parsing.html"&gt;parser&lt;/a&gt;. If there is no parser for this stream, compressed output fails and the functions return 0.&lt;br /&gt;&lt;br /&gt;If you decided to read compressed packets from a stream, pass &lt;code&gt;BGAV_STREAM_READRAW&lt;/code&gt; to &lt;code&gt;bgav_set_audio_stream()&lt;/code&gt; or &lt;code&gt;bgav_set_video_stream()&lt;/code&gt;. Then you can read compressed packets with:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;int bgav_read_audio_packet(bgav_t * bgav, int stream, gavl_packet_t * p);&lt;br /&gt;&lt;br /&gt;int bgav_read_video_packet(bgav_t * bgav, int stream, gavl_packet_t * p);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;There is a small commandline tool &lt;code&gt;bgavdemux&lt;/code&gt;, which writes the compressed packets to raw files, but only if the compression supports a raw format. This is e.g. not the case for vorbis or theora.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;libgmerlin&lt;/b&gt;&lt;br /&gt;In the gmerlin library, the new feature shows up mainly in the &lt;a href="http://gmerlin.cvs.sourceforge.net/viewvc/gmerlin/gmerlin/include/gmerlin/plugin.h?view=markup"&gt;plugin API&lt;/a&gt;. The input plugin (&lt;code&gt;bg_input_plugin_t&lt;/code&gt;) got 4 new functions, which have the identical meaning as their counterparts in gmerlin-avdecoder:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;int (*get_audio_compression_info)(void * priv, int stream,&lt;br /&gt;                              gavl_compression_info_t * info);&lt;br /&gt;&lt;br /&gt;int (*get_video_compression_info)(void * priv, int stream,&lt;br /&gt;                              gavl_compression_info_t * info);&lt;br /&gt;&lt;br /&gt;int (*read_audio_packet)(void * priv, int stream, gavl_packet_t * p);&lt;br /&gt;&lt;br /&gt;int (*read_video_packet)(void * priv, int stream, gavl_packet_t * p);&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;On the encoding side, there are 6 new functions, which are used for querying if compressed writing is possible, adding compressed A/V tracks and writing compressed A/V packets:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;int (*writes_compressed_audio)(void * priv,&lt;br /&gt;                           const gavl_audio_format_t * format,&lt;br /&gt;                           const gavl_compression_info_t * info);&lt;br /&gt;&lt;br /&gt;int (*writes_compressed_video)(void * priv,&lt;br /&gt;                           const gavl_video_format_t * format,&lt;br /&gt;                           const gavl_compression_info_t * info);&lt;br /&gt;&lt;br /&gt;int (*add_audio_stream_compressed)(void * priv, const char * language,&lt;br /&gt;                               const gavl_audio_format_t * format,&lt;br /&gt;                               const gavl_compression_info_t * info);&lt;br /&gt;&lt;br /&gt;int (*add_video_stream_compressed)(void * priv,&lt;br /&gt;                               const gavl_video_format_t * format,&lt;br /&gt;                               const gavl_compression_info_t * info);&lt;br /&gt;&lt;br /&gt;int (*write_audio_packet)(void * data, gavl_packet_t * packet, int stream);&lt;br /&gt;&lt;br /&gt;int (*write_video_packet)(void * data, gavl_packet_t * packet, int stream);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;b&gt;gmerlin-transcoder&lt;/b&gt;&lt;br /&gt;In the gmerlin transcoder you have a configuration for each A/V stream:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/S9t2sSmwiqI/AAAAAAAAAI8/avs-D_f2N1g/s1600/transcode_compressed.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 301px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/S9t2sSmwiqI/AAAAAAAAAI8/avs-D_f2N1g/s400/transcode_compressed.png" alt="" id="BLOGGER_PHOTO_ID_5466093076024953506" border="0" /&gt;&lt;/a&gt;The options for the stream can be "transcode", "copy (if possible)" or "forget". Copying of a stream is possible if the following conditions are met:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The source can deliver compressed packets&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The encoder can write compressed packets of that format&lt;br /&gt;&lt;/li&gt;&lt;li&gt;No subtitles are blended onto video images&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;All filters are however completely ignored. You can configure any filters you want, but when you choose to copy the stream, none of them will be applied.&lt;br /&gt;&lt;br /&gt;If a stream cannot be copied, it will be transcoded.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;libquicktime&lt;/b&gt;&lt;br /&gt;Another major project was support in libquicktime. It's a bit nasty because libquicktime codecs do tasks, which should actually be done by the (de)multiplexer. In practice this means that compressed streams have to be enabled for each codec &lt;b&gt;and&lt;/b&gt; container separately. The public API is in &lt;a href="http://libquicktime.cvs.sourceforge.net/viewvc/libquicktime/libquicktime/include/quicktime/compression.h?view=markup"&gt;compression.h&lt;/a&gt;. It was modeled after the functions in libgmerlin, but the definition of the compression (&lt;code&gt;lqt_compression_info_t&lt;/code&gt;) is slightly different because inside libquicktime we can't use gavl.&lt;br /&gt;&lt;br /&gt;I made a small tool &lt;code&gt;lqtremux&lt;/code&gt;. It can either be called with a single file as an argument, in which case all A/V streams are exported to separate quicktime files. If you pass more than one file on the commandline, the last file is considered the output file and all tracks of all other files are multiplexed into the output file. Note that lqtremux is a pretty dumb application, which was written mainly as a demonstration and testbed for the new functionality. In particular you cannot copy some tracks while transcoding others. For more sophisticated tasks use gmerlin-transcoder or write your own tool.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Status and TODO&lt;/b&gt;&lt;br /&gt;Most major codecs and containers work, although not all of them are heavily tested. Therefore I cannot guarantee, that files written that way will be compatible with all other decoders. Future work will be testing, fixing and supporting more codecs in more containers. Of course any help (like bugreports or compatibility testing on windows or OSX) is highly appreciated.&lt;br /&gt;&lt;br /&gt;With this feature my A/V pipelines are ready for a 1.x version now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-2180701064861468863?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/2180701064861468863/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=2180701064861468863' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/2180701064861468863'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/2180701064861468863'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2010/05/processing-compressed-streams-with.html' title='Processing compressed streams with gmerlin'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_KslCmtD4qm8/S9t2sSmwiqI/AAAAAAAAAI8/avs-D_f2N1g/s72-c/transcode_compressed.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-1899246040842368960</id><published>2010-03-20T15:59:00.009+01:00</published><updated>2010-03-21T00:52:27.889+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='video codecs'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>libtheora vs. libschrödinger vs. x264</title><content type='html'>Doing a comparison of lossy codecs was always on my TODO list. That's mostly because of the codec-related noise I read (or skip) on mailing lists and propaganda for the royality free codecs with semi-technical arguments but without actual numbers. A while ago I made some quick and dirty PSNR tests, where x264 turned out to be the clear winner. But recently we saw new releases of libtheora and libschrödinger with improved encoding quality, so maybe now is the time to investigate things a bit deeper.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Specification vs implementation&lt;/b&gt;&lt;br /&gt;With non-trivial compression techniques (and all techniques I tried are non-trivial) you must make a difference between a &lt;i&gt;specification&lt;/i&gt; and an &lt;i&gt;implementation&lt;/i&gt;. The &lt;i&gt;specification&lt;/i&gt; defines how the compressed bitstream looks like, and suggests how the data can be compressed. I.e. it specifies if motion vectors can be stored with subpixel precision or if B-frames are possible. The &lt;i&gt;implementation&lt;/i&gt; does the actual work of compressing the data. It has a large degree of freedom e.g. it lets you choose between several motion estimation methods or techniques for quantization or rate control. If you fully read and understood all specifications, you can make a rough estimation, which specification allows more powerful compression. But if you want numbers, you can only compare &lt;i&gt;implementations&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;This implies, that statements like &lt;i&gt;"Dirac is better than H.264"&lt;/i&gt; (or vice versa) are inherently idiotic.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Candidates&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;libschroedinger-1.0.9&lt;br /&gt;&lt;/li&gt;&lt;li&gt;libtheora-1.1.0&lt;br /&gt;&lt;/li&gt;&lt;li&gt;x264 git version from 2010-03-19&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Rules of the game&lt;/b&gt;&lt;br /&gt;If compression algorithms are completely different, it's not easy to find comparable codec parameters. Some codecs are very good for VBR encoding but suck when forced to CBR. Some codecs are optimized for low-bitrate, others are work better at higher bitrates. Therefore I decided for very simple test rules:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;All codec settings are set to their defaults found in the sourcecode of the libraries. This leaves the decision of good parameters to the developers of the libraries. I upgraded the codec parameters in libquicktime and the gmerlin encoding plugins for the newest library versions.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The only parameter, which is changed, corresponds to the global quality of the compression (all libraries have such a parameter). Multiple files are encoded with different quality settings.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;From the encoded files the average bitrate is calculated and the &lt;a href="http://hirntier.blogspot.com/2010/01/video-quality-characterization.html"&gt;quality&lt;/a&gt; (PSNR and MSSIM) is plotted as a function of the average bitrate.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Footage&lt;/b&gt;&lt;br /&gt;Some lossless sequences in y4m format can be downloaded from the &lt;a href="http://media.xiph.org/video/derf/"&gt;xiph&lt;/a&gt; site. I wanted a file, which has fast global motion as well as slower changing parts. Also the uncompressed size shouldn't be too large to keep the transcoding- and analysis time at a reasonable level. Therefore I decided to use the &lt;a href="http://media.xiph.org/video/derf/y4m/foreman_cif.y4m"&gt;foreman&lt;/a&gt;. Of course for a better estimation you would need much more and longer sequences. Feel free to repeat the experiment with other files and tell about the results.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Analysis tool&lt;/b&gt;&lt;br /&gt;I wrote a small tool &lt;code&gt;gmerlin_vanalyze&lt;/code&gt;, which is called with the original and encoded files as only arguments. It will then output something like:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;0 33385 47.137417 0.992292&lt;br /&gt;1 17713 45.936294 0.989990&lt;br /&gt;2 17693 45.998659 0.990233&lt;br /&gt;3 17361 46.008802 0.990297&lt;br /&gt;4 19253 46.144632 0.990582&lt;br /&gt;5 19005 46.179699 0.990648&lt;br /&gt;&lt;br /&gt;....&lt;br /&gt;&lt;br /&gt;295 24454 45.174282 0.993100&lt;br /&gt;296 23841 44.653152 0.992318&lt;br /&gt;297 20966 43.848303 0.991013&lt;br /&gt;298 13941 41.996157 0.987494&lt;br /&gt;299 11682 41.852630 0.987321&lt;br /&gt;# Average values&lt;br /&gt;# birate      PSNR      SSIM&lt;br /&gt;# 4941941.26  46.075177 0.991434&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Each line consists of:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Frame number&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Compressed size of this frame in bytes&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Luminance PSNR in dB of this frame&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Mean SSIM of this frame&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;The summary at the end consists of the total video bitrate (bits/second) as well as PSNR and SSIM averaged over all frames.&lt;br /&gt;&lt;br /&gt;You can get this tool if you upgrade gavl, gmerlin and gmerlin-avdecoder from CVS. It makes use of a brand new feature (extracting compressed frames), which is needed for the video bitrate calculation (i.e. without the overhead from the container).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Results&lt;/b&gt;&lt;br /&gt;See below for the PSNR and MSSIM results for the 3 libraries.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/S6TniYXb3AI/AAAAAAAAAIs/RsQxR0xfPxo/s1600-h/psnr.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 299px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/S6TniYXb3AI/AAAAAAAAAIs/RsQxR0xfPxo/s400/psnr.png" alt="" id="BLOGGER_PHOTO_ID_5450736026867457026" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/S6ToAm0OG6I/AAAAAAAAAI0/ArX0UgVOOuE/s1600-h/ssim.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 302px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/S6ToAm0OG6I/AAAAAAAAAI0/ArX0UgVOOuE/s400/ssim.png" alt="" id="BLOGGER_PHOTO_ID_5450736546142362530" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;The quality vs. bitrate differences are surprisingly small. While x264 still wins, the royality free versions lag behind by just 2-3 dB of PSNR. Also surprising is that libtheora and libschroedinger are so close together given the fact, that Dirac has e.g. B-frames, while theora has just I- and P-frames. Depending on your point of view, this is good news for libtheora or bad news for libschroedinger&lt;br /&gt;&lt;br /&gt;Another question is of course, if this comparison is completely fair. A further project could now be to take the codecs and tweak single parameters to check, how the quality can be improved. Also one might add other criteria like encoding-/decoding speed as well. Making tests with different types of footage would also give more insight.&lt;br /&gt;&lt;br /&gt;To summarize that, I don't state that these numbers are the final wisdom. But at least they are numbers, and neither propaganda nor marketing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-1899246040842368960?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/1899246040842368960/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=1899246040842368960' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1899246040842368960'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1899246040842368960'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2010/03/libtheora-vs-libschrodinger-vs-x264.html' title='libtheora vs. libschrödinger vs. x264'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_KslCmtD4qm8/S6TniYXb3AI/AAAAAAAAAIs/RsQxR0xfPxo/s72-c/psnr.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-8174975882801365170</id><published>2010-02-06T14:54:00.003+01:00</published><updated>2010-02-06T15:17:46.254+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>AVCHD timecode update</title><content type='html'>After &lt;a href="http://hirntier.blogspot.com/2009/08/avchd-timecodes-revealed.html"&gt;this&lt;/a&gt; post I got some infos from a &lt;a href="http://metadatamadness.blogspot.com/"&gt;cat&lt;/a&gt;, which helped me to understand the AVCHD metadata format much better. This post summarizes, what I currently know.&lt;br /&gt;&lt;br /&gt;AVCHD metadata are stored in an SEI message of type 5 (user data unregistered). These messages start with a GUID (indicating the type of the data). The rest is not specified in the H.264 spec. For AVCHD metadata the data structure is as follows:&lt;br /&gt;&lt;br /&gt;1. The 16 byte GUID, which consists of the bytes&lt;br /&gt;&lt;br /&gt;&lt;code&gt;0x17 0xee 0x8c 0x60 0xf8 0x4d 0x11 0xd9 0x8c 0xd6 0x08 0x00 0x20 0x0c 0x9a 0x66&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;2. 4 bytes&lt;br /&gt;&lt;br /&gt;&lt;code&gt;0x4d 0x44 0x50 0x4d&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;which are "MDPM" in ASCII.&lt;br /&gt;&lt;br /&gt;3. One byte, which specifies the number of tags to follow&lt;br /&gt;&lt;br /&gt;4. Each tag begins with one byte specifying the tag type followed by 4 bytes of data.&lt;br /&gt;&lt;br /&gt;The date and time are stored in tags &lt;code&gt;0x18&lt;/code&gt; and &lt;code&gt;0x19&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Tag &lt;code&gt;0x18&lt;/code&gt; starts with an unknown byte. I saw values between &lt;code&gt;0x02&lt;/code&gt; and &lt;code&gt;0xff&lt;/code&gt; in various files. It seems however that it has a constant value for all frames in a file. The 3 remaining bytes are the year and the month in BCD coding (&lt;code&gt;0x20 0x09 0x08&lt;/code&gt; means August 2009).&lt;br /&gt;&lt;br /&gt;The 4 bytes in tag &lt;code&gt;0x19&lt;/code&gt; are the day, hour, minute and second (also BCD coded).&lt;br /&gt;&lt;br /&gt;There are more informations stored in this SEI message, check &lt;a href="http://owl.phy.queensu.ca/%7Ephil/exiftool/TagNames/M2TS.html#SEI"&gt;here&lt;/a&gt; for a list.&lt;br /&gt;&lt;br /&gt;If you want to make further research on this, you can download gmerlin-avdecoder from CVS, open the file &lt;code&gt;lib/parse_h264.c&lt;/code&gt; and uncomment the following line (at the very beginning):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;// #define DUMP_AVCHD_SEI&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Then you can use &lt;code&gt;bgavdump&lt;/code&gt; on your files. It will decode the first 10 frames from the file. If you want to decode e.g. 100 frames, use&lt;br /&gt;&lt;br /&gt;&lt;code&gt;bgavdump -nf 100 your_file.mts&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-8174975882801365170?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/8174975882801365170/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=8174975882801365170' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/8174975882801365170'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/8174975882801365170'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2010/02/avchd-timecode-update.html' title='AVCHD timecode update'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-870070304035467951</id><published>2010-01-27T23:24:00.014+01:00</published><updated>2010-01-28T00:49:03.096+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Video quality characterization techniques</title><content type='html'>When developing video processing algorithms and tuning them for quality, one needs proper measurement facilities, otherwise one will end up doing voodoo. This post introduces two prominent methods for calculating the differences of two images (the "original" and the "reproduced" one) and get a value, which allows to estimate, how well the images coincide.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;PSNR&lt;/b&gt;&lt;br /&gt;The most prominent method is the &lt;a href="http://en.wikipedia.org/wiki/PSNR"&gt;PSNR&lt;/a&gt; (peaked signal-to-noise ratio). It is based on the idea, that the reproduced image consists of the original plus a "noise signal". The noise level can be characterized by the &lt;a href="http://en.wikipedia.org/wiki/Signal-to-noise_ratio"&gt;signal-to-noise ratio&lt;/a&gt; and is usually given in dB. Values below 0 dB mean that the noise power is larger than the signal. For identical images (zero noise), the PSNR is infinite.&lt;br /&gt;&lt;br /&gt;Advantage is, that it's a well established method and the calculation is extremely simple (see &lt;a href="http://en.wikipedia.org/wiki/PSNR"&gt;here&lt;/a&gt; for the formula). Disadvantage is, that it is a purely mathematical calculation of the noise power, while the human psychovisual system is completely ignored.&lt;br /&gt;&lt;br /&gt;Thus, one can easily to make 2 images, which have different types of compression artifacts (i.e. from different codecs) and have the similar PSNR compared to the original. But one looks much better than the other. Therefore, current opinion among specialists is, that PSNR can be used or optimizing &lt;b&gt;one&lt;/b&gt; codec, while it fails for comparing &lt;b&gt;different&lt;/b&gt; codecs. Unfortunately, many codec comparisons in the internet still use PSNR.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;SSIM&lt;/b&gt;&lt;br /&gt;SSIM (structural similarity) was fist suggested by Zhou Wang et.al. in the paper "Image Quality Assessment: From Error Visibility to Structural Similarity" (IEEE Transactions on image processing, Vol. 13, No. 4, April 2004, &lt;a href="http://www.cns.nyu.edu/pub/eero/wang03-reprint.pdf"&gt;PDF&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;The paper is very well written and I recommend anyone, who is interested, to read it. In short: The structural similarity is composed of 3 values:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Luminance comparison&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Contrast comparison&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Structure comparison&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;All these components are normalized such that they are 1.0 for identical images. The SSIM index is the product of the 3 components (optionally raised by an exponent). Obviously the product will be normalized as well.&lt;br /&gt;&lt;br /&gt;A difference to PSNR is, that the SSIM index for a pixel is calculated by taking the surrounding pixels into account. It calculates some characteristic numbers known from statistics: The mean value, the standard deviation and correlation coefficient.&lt;br /&gt;&lt;br /&gt;One problem with the SSIM is, that the algorithm has some free parameters, which are slightly different in each implementation. Therefore you should be careful when comparing your results with numbers coming from a different routine. I took the parameters from the original paper, i.e. K1 = 0.01, K2 = 0.03 and an 11x11 Gaussian window with a standard deviation of 1.5 pixels.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Implementations&lt;/b&gt;&lt;br /&gt;Both methods are available in gavl (SSIM only in CVS for now), but their APIs are slightly different. To calculate the PSNR, use:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;void gavl_video_frame_psnr(double * psnr,&lt;br /&gt;                         const gavl_video_frame_t * src1,&lt;br /&gt;                         const gavl_video_frame_t * src2,&lt;br /&gt;                         const gavl_video_format_t * format);&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;The &lt;code&gt;src1&lt;/code&gt;, &lt;code&gt;src2&lt;/code&gt; and &lt;code&gt;format&lt;/code&gt; arguments are obvious. The result (already in dB) is returned in &lt;code&gt;psnr&lt;/code&gt;  for each component. The order is RGB(A), Y'CbCr(A) or Gray(A) depending on the pixelformat. PSNR can be calculated for all pixelformats, but usually one will use a Y'CbCr format and take only the value for the Y' component. In all my tests the PSNR values for chrominance were much higher, so the luminance PSNR is the most pessimistic (i.e. most honest) value.&lt;br /&gt;&lt;br /&gt;For SSIM you can use:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;int gavl_video_frame_ssim(const gavl_video_frame_t * src1,&lt;br /&gt;                        const gavl_video_frame_t * src2,&lt;br /&gt;                        gavl_video_frame_t * dst,&lt;br /&gt;                        const gavl_video_format_t * format);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;The arguments &lt;code&gt;src1&lt;/code&gt;, &lt;code&gt;src2&lt;/code&gt; and &lt;code&gt;format&lt;/code&gt; are the same as for PSNR. The pixelformat however &lt;b&gt;must&lt;/b&gt; be &lt;code&gt;GAVL_GRAY_FLOAT&lt;/code&gt;, implying that only the luminance is taken into account. This decision was made after the experiences with PSNR. The SSIM indices for each pixel is then returned in &lt;code&gt;dst&lt;/code&gt;, which must be created with the same format. The MSSIM (mean SSIM) for the whole image can then be obtained by averaging the SSIM values over all pixels. The function returns 1 if the SSIM could be calculated or 0 if the pixelformat was not &lt;code&gt;GAVL_GRAY_FLOAT&lt;/code&gt; or the image is smaller than the 11x11 window.&lt;br /&gt;&lt;br /&gt;It never matters which image is passed in &lt;code&gt;src1&lt;/code&gt; and which in &lt;code&gt;src2&lt;/code&gt; because both algorithms are symmetric.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Example&lt;/b&gt;&lt;br /&gt;Below you see 11 Lena images compressed with libjpeg at quality levels from 0 to 100 along with their PSNR, SSIM and file size:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/S2DCGOu3DPI/AAAAAAAAAHM/_Xo91lvXYi4/s1600-h/lena_000.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/S2DCGOu3DPI/AAAAAAAAAHM/_Xo91lvXYi4/s400/lena_000.jpg" alt="" id="BLOGGER_PHOTO_ID_5431554562898463986" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div align="center"&gt;Quality: 0, PSNR: 23.54 dB, SSIM: 0.6464, Size: 2819 bytes&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_KslCmtD4qm8/S2DC3W0wGAI/AAAAAAAAAHU/pVGfFi6wRjo/s1600-h/lena_010.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://4.bp.blogspot.com/_KslCmtD4qm8/S2DC3W0wGAI/AAAAAAAAAHU/pVGfFi6wRjo/s400/lena_010.jpg" alt="" id="BLOGGER_PHOTO_ID_5431555406884247554" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div align="center"&gt;Quality: 10, PSNR: 29.84 dB, SSIM: 0.8473, Size: 4305 bytes&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_KslCmtD4qm8/S2DC_o83wyI/AAAAAAAAAHc/X7KaHZbdz_o/s1600-h/lena_020.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://4.bp.blogspot.com/_KslCmtD4qm8/S2DC_o83wyI/AAAAAAAAAHc/X7KaHZbdz_o/s400/lena_020.jpg" alt="" id="BLOGGER_PHOTO_ID_5431555549189096226" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div align="center"&gt;Quality: 20, PSNR: 32.74 dB, SSIM: 0.9084, Size: 5890 bytes&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/S2DDHptI9MI/AAAAAAAAAHk/h_-gOeqEo90/s1600-h/lena_030.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/S2DDHptI9MI/AAAAAAAAAHk/h_-gOeqEo90/s400/lena_030.jpg" alt="" id="BLOGGER_PHOTO_ID_5431555686830503106" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div align="center"&gt;Quality: 30, PSNR: 34.38 dB, SSIM: 0.9331, Size: 7376 bytes&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/S2DDOqGSnTI/AAAAAAAAAHs/LECo17_BEAQ/s1600-h/lena_040.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/S2DDOqGSnTI/AAAAAAAAAHs/LECo17_BEAQ/s400/lena_040.jpg" alt="" id="BLOGGER_PHOTO_ID_5431555807195077938" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div align="center"&gt;Quality: 40, PSNR: 35.44 dB, SSIM: 0.9460, Size: 8590 bytes&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_KslCmtD4qm8/S2DDU4yauWI/AAAAAAAAAH0/uY6i9Pd9mcg/s1600-h/lena_050.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://4.bp.blogspot.com/_KslCmtD4qm8/S2DDU4yauWI/AAAAAAAAAH0/uY6i9Pd9mcg/s400/lena_050.jpg" alt="" id="BLOGGER_PHOTO_ID_5431555914217470306" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div align="center"&gt;Quality: 50, PSNR: 36.31 dB, SSIM: 0.9549, Size: 9777 bytes&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/S2DDd_h4YRI/AAAAAAAAAH8/vBOXF2h6wlk/s1600-h/lena_060.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/S2DDd_h4YRI/AAAAAAAAAH8/vBOXF2h6wlk/s400/lena_060.jpg" alt="" id="BLOGGER_PHOTO_ID_5431556070645981458" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div align="center"&gt;Quality: 60, PSNR: 37.16 dB, SSIM: 0.9612, Size: 11101 bytes&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/S2DDk86TXCI/AAAAAAAAAIE/t9qDXs6Wr3k/s1600-h/lena_070.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/S2DDk86TXCI/AAAAAAAAAIE/t9qDXs6Wr3k/s400/lena_070.jpg" alt="" id="BLOGGER_PHOTO_ID_5431556190202190882" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div align="center"&gt;Quality: 70, PSNR: 38.34 dB, SSIM: 0.9688, Size: 13034 bytes&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/S2DDuWWbVhI/AAAAAAAAAIM/h9vEXgYL-gM/s1600-h/lena_080.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/S2DDuWWbVhI/AAAAAAAAAIM/h9vEXgYL-gM/s400/lena_080.jpg" alt="" id="BLOGGER_PHOTO_ID_5431556351649863186" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div align="center"&gt;Quality: 80, PSNR: 40.00 dB, SSIM: 0.9768, Size: 16410 bytes&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/S2DD0_hoxqI/AAAAAAAAAIU/mBWpZ8q1qLw/s1600-h/lena_090.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/S2DD0_hoxqI/AAAAAAAAAIU/mBWpZ8q1qLw/s400/lena_090.jpg" alt="" id="BLOGGER_PHOTO_ID_5431556465781950114" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div align="center"&gt;Quality: 90, PSNR: 43.06 dB, SSIM: 0.9863, Size: 24308 bytes&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/S2DD8b8JP7I/AAAAAAAAAIc/-6s9WdvLlqI/s1600-h/lena_100.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 256px; height: 256px;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/S2DD8b8JP7I/AAAAAAAAAIc/-6s9WdvLlqI/s400/lena_100.jpg" alt="" id="BLOGGER_PHOTO_ID_5431556593668407218" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div align="center"&gt;Quality: 100, PSNR: 58.44 dB, SSIM: 0.9993, Size: 94169 bytes&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;With these numbers I made a plot, which shows the PSNR and SSIM as a function of the JPEG quality:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/S2DJ83CrWiI/AAAAAAAAAIk/bfovE72YaxE/s1600-h/quality.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 306px;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/S2DJ83CrWiI/AAAAAAAAAIk/bfovE72YaxE/s400/quality.png" alt="" id="BLOGGER_PHOTO_ID_5431563198013332002" border="0" /&gt;&lt;/a&gt;The JPEGs have the most visible differences for qualities between 0 and 40. In this range the SSIM curve has the largest gradient. Above 40 (where the visual quality doesn't change much), the SSIM becomes more or less linear and reaches almost 1 for the best quality.&lt;br /&gt;&lt;br /&gt;The PSNR curve is a bit misleading. It has the steepest gradient for the highest quality. This is understandable because PSNR would become infinite for the lossless (perfect quality) case. It has however not much to do with the subjective impression. PSNR however is better for fine-tuning codecs at very high quality levels. That's because the PSNR values will change more strongly, while SSIM will always be almost one.&lt;br /&gt;&lt;br /&gt;Now I have the proper tools to make a comparison of different video codecs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-870070304035467951?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/870070304035467951/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=870070304035467951' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/870070304035467951'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/870070304035467951'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2010/01/video-quality-characterization.html' title='Video quality characterization techniques'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_KslCmtD4qm8/S2DCGOu3DPI/AAAAAAAAAHM/_Xo91lvXYi4/s72-c/lena_000.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-1398375532728835557</id><published>2010-01-24T02:53:00.004+01:00</published><updated>2010-01-24T03:14:02.911+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Disabling the X11 screensaver from a client</title><content type='html'>One of the most annoying experiences when watching videos with friends on cold winter evenings is, when the screensaver starts. Media players therefore need a way to switch that off by one or several means.&lt;br /&gt;&lt;br /&gt;The bad news is, that there is no &lt;i&gt;official&lt;/i&gt; method for such a trivial task, which works on &lt;b&gt;all&lt;/b&gt; installations. In addition, there is the energy saving mode, which has nothing to do with the screensaver, and must thus be disabled separately.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Xlib method&lt;/b&gt;&lt;br /&gt;You get the screensaver status with &lt;code&gt;XGetScreenSaver()&lt;/code&gt;, disable it with &lt;code&gt;XSetScreenSaver()&lt;/code&gt; and restore it after video playback. Advantage is, that this method is core X11. Disadvatage is, that it never works.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Old gnome method&lt;/b&gt;&lt;br /&gt;Older gnome versions had a way to ping the screensaver by executing the command:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;gnome-screensaver-command --poke &amp;gt; /dev/null 2&amp;gt; /dev/null&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Actually pinging the screensaver (which resets the idle timer) is a better method, because it restores the screensaver even if the player got killed (or crashed). The bad news is, that starting with some never gnome version (don't know exactly which), this stopped working. To make things worse, the command is still available and even gives zero return code, it's just a noop.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;KDE&lt;/b&gt;&lt;br /&gt;I never owned a Linux installation with KDE. But with a little help from a friends, I found a method. My implementation however is so ugly, that I won't show it here :)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The holy grail: Fake key events&lt;/b&gt;&lt;br /&gt;After the old gnome variant stopped working for me, I finally found the XTest extension. It was developed to test XServers. I abuse it to send fake key events, which are handled indentically to real keystrokes. They will reset the idle counters of all screensaver variants, and will also disable the energy saving mode. &lt;br /&gt;&lt;br /&gt;Also it's a ping approach (with the advantage described above). But it works with an X11 protocol request instead of forking a subprocess, so the overhead will be much smaller. The documentation for the XTest extension is from 1992, so I expect it to be present on all installations, which are sufficiently new for video playback.&lt;br /&gt;&lt;br /&gt;Here is how I implemented it:&lt;br /&gt;&lt;br /&gt;1. Include &lt;code&gt;&amp;lt;X11/extensions/XTest.h&amp;gt;&lt;/code&gt;, link with &lt;code&gt;-lXtst&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;2. Test for presence of the XTest extension with &lt;code&gt;XTestQueryExtension()&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;3. Get the keycode of the left shift key with &lt;code&gt;XKeysymToKeycode()&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;4. Each 40 seconds, I press the key with&lt;br /&gt;&lt;br /&gt;&lt;code&gt;XTestFakeKeyEvent(dpy, keycode, True, CurrentTime);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;5. One video frame later, I release the key with&lt;br /&gt;&lt;br /&gt;&lt;code&gt;XTestFakeKeyEvent(dpy, keycode, False, CurrentTime);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The one frame delay was done to make sure, that the press and release events will arrive with different timestamps. I don't want to know, what happens if press- and release-events for a key have identical timestamps.&lt;br /&gt;&lt;br /&gt;This method will hopefully work forever, no matter what crazy ideas the desktop developers get in the future. Also, this is one more reason &lt;i&gt;not&lt;/i&gt; to use any GUI toolkit for video playback. If you use Xlib and it's extensions, you have full access to all available features of X. When using a toolkit, you have just the features the toolkit developers think you deserve.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-1398375532728835557?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/1398375532728835557/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=1398375532728835557' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1398375532728835557'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1398375532728835557'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2010/01/disabling-x11-screensaver-from-client.html' title='Disabling the X11 screensaver from a client'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-7700898086920120939</id><published>2010-01-19T00:54:00.002+01:00</published><updated>2010-01-19T01:00:37.220+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Gmerlin release on the horizon</title><content type='html'>New gmerlin prereleases are here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gmerlin.sourceforge.net/gmerlin-dependencies-20100119.tar.bz2"&gt;http://gmerlin.sourceforge.net/gmerlin-dependencies-20100119.tar.bz2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gmerlin.sourceforge.net/gmerlin-all-in-one-20100119.tar.bz2"&gt;http://gmerlin.sourceforge.net/gmerlin-all-in-one-20100119.tar.bz2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Changes since the last public release are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Great &lt;a href="http://hirntier.blogspot.com/2009/10/major-gmerlin-player-upgrade.html"&gt;player&lt;/a&gt; simplification. Most changes are internal, but the user should notice much faster seeking. Also the GUI player now updates the video window while the seek-slider is moved.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Simplification of the plugin configuration: In many places, where we had an extra dialog for  configuring plugins, it was merged with the rest of the configuration.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;A new &lt;a href="http://hirntier.blogspot.com/2009/12/introducing-gmerlin-recorder.html"&gt;recorder application&lt;/a&gt; which records audio (with OSS, Pulseaudio, Alsa, Jack and ESound) and video (with V4L1, V4L2 or the new &lt;a href="http://hirntier.blogspot.com/2009/11/x11-grabbing-howto.html"&gt;X11 grabber&lt;/a&gt;). Output can be written into files or &lt;a href="http://hirntier.blogspot.com/2009/12/flash-free-live-web-video-solution.html"&gt;broadcasted&lt;/a&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;A new encoding frontend (used by the recorder and transcoder), which allows more consistant and unified configuration of encoding setups.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The video window of the GUI player has now a gmerlin icon and is grouped together with the other windows.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Like always: Tons of fixes, optimizations and smaller cleanups in all packages. Got another 18% speedup when building AVCHD indexes for example.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;This will be the last gmerlin release of the 0.3.X series. There are just 2 major features left, which keep me from releasing 1.0.0:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Support for &lt;b&gt;compressed A/V frames&lt;/b&gt; in the architecture. This should allow lossless transmultiplexing with the transcoder.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Support for configuration presets: This will make using gmerlin applications much easier. The presets can be shared among application, i.e. once you found a good encoding preset, you can use it both in the transcoder and the recorder.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Please test it as much as you can and sent problem reports to the gmerlin-general list.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-7700898086920120939?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/7700898086920120939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=7700898086920120939' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7700898086920120939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7700898086920120939'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2010/01/gmerlin-release-on-horizon.html' title='Gmerlin release on the horizon'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-4801358158930905110</id><published>2009-12-14T23:43:00.007+01:00</published><updated>2009-12-15T00:34:10.778+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web design'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Flash-free live web video solution</title><content type='html'>Some time ago, we all knew that video on the web was equivalent to the proprietary Flash technology. Also I used to say, that there might be political or psychological reasons for using Ogg/Theora, but never technical ones.&lt;br /&gt;&lt;br /&gt;Well, the conditions have changed recently so it's time to bring an update on this.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;HTML 5 video tag&lt;/b&gt;&lt;br /&gt;The HTML 5 draft supports a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag for embedding a video into a webpage as a native html element (e.g. without a plugin). Earlier versions of the draft even recommended that browsers should support Ogg/Theora as a format for the video. The Ogg/Theora recommendation was then removed and a lot of discussion was started around this. &lt;a href="http://en.wikipedia.org/wiki/Use_of_Ogg_formats_in_HTML5"&gt;This&lt;/a&gt; wikipedia article summarizes the issue. Nevertheless, there are a number of browsers supporting OggTheora video out of the box, among these is Firefox-3.5.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Cortado plugin&lt;/b&gt;&lt;br /&gt;In a different development, the &lt;a href="http://www.theora.org/cortado"&gt;Cortado&lt;/a&gt; java applet for theora playback was written. It is GPL licensed and you can just &lt;a href="http://downloads.xiph.org/releases/cortado/cortado_latest.jar"&gt;download&lt;/a&gt; it and put it into your webspace.&lt;br /&gt;&lt;br /&gt;Now the cool thing about the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag is, that browsers, which don't know about it, will display the contents between &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;/video&amp;gt;&lt;/code&gt;, so you can include the applet code there. Researching a bit about the best way to do this, I read that the (often recommended) &lt;code&gt;&amp;lt;applet&amp;gt;&lt;/code&gt; mechanism is not valid html. A better solution is &lt;a href="http://hsivonen.iki.fi/test/moz/video-fallback-validation/object-type.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Webmasters side&lt;/b&gt;&lt;br /&gt;Now if you have the live-stream at &lt;code&gt;http://192.168.2.2:8000/stream.ogg&lt;/code&gt; your html page will look like:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&amp;lt;meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&amp;lt;h1&amp;gt;Test video stream&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;lt;video tabindex="0"&lt;br /&gt;src="http://192.168.2.2:8000/stream.ogg"&lt;br /&gt;controls="controls"&lt;br /&gt;alt="Test video stream"&lt;br /&gt;title="Test video stream"&lt;br /&gt;width="320"&lt;br /&gt;height="240"&lt;br /&gt;autoplay="true"&lt;br /&gt;loop="nolooping"&amp;gt;&lt;br /&gt;&amp;lt;object type="application/x-java-applet"&lt;br /&gt;   width="320" height="240"&amp;gt;&lt;br /&gt;&amp;lt;param name="archive" value="cortado.jar"&amp;gt;&lt;br /&gt;&amp;lt;param name="code" value="com.fluendo.player.Cortado.class"&amp;gt;&lt;br /&gt;&amp;lt;param name="url" value="http://192.168.2.2:8000/stream.ogg"&amp;gt;&lt;br /&gt;&amp;lt;param name="autoplay" value="true"&amp;gt;&lt;br /&gt;&amp;lt;a href="http://192.168.2.2:8000/stream.ogg"&amp;gt;Test video stream&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;lt;/object&amp;gt;&lt;br /&gt;&amp;lt;/video&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Note that for live-streams the "autoplay" option should be given. If not, firefox will try to load the first image (automatically anyway) to show it in the video widget. Then it will stop downloading the live-stream until you click start. Pretty obvious that this will mess up live-streaming.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Server side&lt;/b&gt;&lt;br /&gt;For live streaming I just installed the &lt;a href="http://www.icecast.org/"&gt;icecast&lt;/a&gt; server, which came with my ubuntu. I just changed the passwords in &lt;code&gt;/etc/icecast/icecast.xml&lt;/code&gt;, enabled the server in &lt;code&gt;/etc/default/icecast2&lt;/code&gt; and started it with &lt;code&gt;/etc/init.d/icecast2 start&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Upstream side&lt;/b&gt;&lt;br /&gt;There are lots of programs for streaming to icecast servers, most of them use &lt;a href="http://icecast.org/download.php"&gt;libshout&lt;/a&gt;. I decided to create a new family of plugins for gmerlin: Broadcasting plugins. They have the identical API as encoder plugins (used by the transcoder or the recorder). The only difference is, that they don't produce regular files and must be realtime capable.&lt;br /&gt;&lt;br /&gt;Using libshout is extremely simple:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Create a shout instance with &lt;code&gt;shout_new()&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Set parameters with the &lt;code&gt;shout_set_*()&lt;/code&gt; functions&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Call &lt;code&gt;shout_open()&lt;/code&gt; to actually open the connection to the icecast server&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Write a valid Ogg/Theora stream with &lt;code&gt;shout_send()&lt;/code&gt;. Actually I took my already existing Ogg/Theora encoder plugin and replaced all &lt;code&gt;fwrite()&lt;/code&gt; calls by &lt;code&gt;shout_send()&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;See below for the libshout configuration of the gmerlin Ogg broadcaster.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_KslCmtD4qm8/SybCnRxWEXI/AAAAAAAAAG8/K9KHnTUfm-s/s1600-h/libshout_config.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 367px;" src="http://4.bp.blogspot.com/_KslCmtD4qm8/SybCnRxWEXI/AAAAAAAAAG8/K9KHnTUfm-s/s400/libshout_config.png" alt="" id="BLOGGER_PHOTO_ID_5415229582000656754" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Of course, some minor details were left out in my overview, read the libshout documentation for them. As upstream client, I use my new &lt;a href="http://hirntier.blogspot.com/2009/12/introducing-gmerlin-recorder.html"&gt;recorder application&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The result&lt;/b&gt;&lt;br /&gt;See below a screenshot from firefox while it plays back a live stream:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/SybIPqCAfWI/AAAAAAAAAHE/OGQv5i35yJA/s1600-h/ff_shot.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 342px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/SybIPqCAfWI/AAAAAAAAAHE/OGQv5i35yJA/s400/ff_shot.png" alt="" id="BLOGGER_PHOTO_ID_5415235773265902946" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;b&gt;Open Issues&lt;/b&gt;&lt;br /&gt;The live-video experiment went extremely smooth. I discovered however some minor issues, which could be optimized away:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Firefox doesn't recognize live-streams (i.e. streams with infinite duration) properly. It displays a seek-slider which always sticks at the very end. Detecting a http stream as live can easily be done by checking the &lt;code&gt;Content-Length&lt;/code&gt; field of the http response header.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The theora encoder (1.1.1 in my case) might be faster than the 1.0.0 series, but it's still way too slow. Live encoding of a 320x240 stream is possible on my machine but 640x480 isn't.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The cortado plugin has a loudspeaker icon, but no volume control (or it just doesn't work with my Java installation)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Other news&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;With the video switched off I can send audio streams to my wlan radio. This makes my older &lt;a href="http://hirntier.blogspot.com/2008/09/music-from-everywhere-everywhere.html"&gt;solution&lt;/a&gt; (based on ices2) obsolete.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The whole thing of course works with prerecorded files as well. In this case, you can just put the files into your webspace and your normal webserver will deliver them. No icecast needed.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-4801358158930905110?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/4801358158930905110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=4801358158930905110' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4801358158930905110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4801358158930905110'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/12/flash-free-live-web-video-solution.html' title='Flash-free live web video solution'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_KslCmtD4qm8/SybCnRxWEXI/AAAAAAAAAG8/K9KHnTUfm-s/s72-c/libshout_config.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-5263983195033529019</id><published>2009-12-06T18:08:00.004+01:00</published><updated>2009-12-09T10:27:23.565+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Introducing Gmerlin recorder</title><content type='html'>Gmerlin-recorder is a small application, which records audio and video from hardware devices. It was written as a more generic application, which should eventually replace camelot. See below for a screenshot:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/SxvlkbFqqFI/AAAAAAAAAG0/q8Y-tEQEAzQ/s1600-h/recorder_shot.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 398px;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/SxvlkbFqqFI/AAAAAAAAAG0/q8Y-tEQEAzQ/s400/recorder_shot.png" alt="" id="BLOGGER_PHOTO_ID_5412171791125358674" border="0" /&gt;&lt;/a&gt;As sources, we support:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Audio devices via OSS, Alsa, Esound, Pulseaudio and Jack&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Webcams via V4L and V4L2&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://hirntier.blogspot.com/2009/11/x11-grabbing-howto.html"&gt;X11 grabbing&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;For monitoring you see the video image in the recorder window. For the audio you have a vumeter. With recorded streams you can do lots of stuff:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Filter the streams using gmerlin A/V filters&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Write the streams to files using gmerlin encoder plugins&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Send the encoded stream to an &lt;a href="http://www.icecast.org"&gt;icecast&lt;/a&gt; server (will be described in detail in another blog post)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Save snapshots to images either manually or automatically&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;With all these features combined in the right way, you can use gmerlin-recorder for a large number of applications:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Make vlogs with your webcam and a microphone&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Digitize analog music&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Make a video tutorial for your application&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Start your broadcasting station&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Send music from your stereo to your WLAN radio&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Make stop-motion movies&lt;br /&gt;&lt;/li&gt;&lt;li&gt;...&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Until the recorder can fully replace camelot, we need the following features, which are not there yet:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Fullscreen video display&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Forwarding of the video stream via vloopback&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-5263983195033529019?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/5263983195033529019/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=5263983195033529019' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/5263983195033529019'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/5263983195033529019'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/12/introducing-gmerlin-recorder.html' title='Introducing Gmerlin recorder'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_KslCmtD4qm8/SxvlkbFqqFI/AAAAAAAAAG0/q8Y-tEQEAzQ/s72-c/recorder_shot.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-4554148166867151598</id><published>2009-11-15T14:17:00.004+01:00</published><updated>2009-11-15T14:51:24.309+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>X11 grabbing howto</title><content type='html'>One gmerlin plugin I always wanted to program was an X11 grabber. It continuously grabs either the entire root window, or a user defined rectangular area of it. It is realized as a gmerlin video recorder plugin, which means that it behaves pretty much like a webcam.&lt;br /&gt;&lt;br /&gt;Some random notes follow.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Transparent grab window&lt;/b&gt;&lt;br /&gt;The rectangular area is defined by a &lt;i&gt;grab window&lt;/i&gt;, which consists &lt;i&gt;only&lt;/i&gt; of a frame (drawn by the window manager). The window itself must be completely transparent such that mouse clicks are sent to the window below. This is realized using the X11 Shape extension:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;XShapeCombineRectangles(dpy, win,&lt;br /&gt;                     ShapeBounding,&lt;br /&gt;                     0, 0,&lt;br /&gt;                     (XRectangle *)0,&lt;br /&gt;                     0,&lt;br /&gt;                     ShapeSet,&lt;br /&gt;                     YXBanded);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;The grab window can be moved and resized to define the grabbing area. This is more convenient than entering coordinates manually. After a resize, the plugin must be reopened because the image size of a gmerlin video stream cannot change within the processing loop.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Make the window sticky and always on top&lt;/b&gt;&lt;br /&gt;Depending on the configuration the grab window can always be on top of the other windows. There is also a &lt;i&gt;sticky&lt;/i&gt; option making the window appear on all desktops. This is done with the collowing code, which must be called each time &lt;i&gt;before&lt;/i&gt; the window is mapped:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;if(flags &amp;amp; (WIN_ONTOP|WIN_STICKY))&lt;br /&gt;{&lt;br /&gt;Atom wm_states[2];&lt;br /&gt;int num_props = 0;&lt;br /&gt;Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);&lt;br /&gt;if(flags &amp;amp; WIN_ONTOP)&lt;br /&gt; {&lt;br /&gt; wm_states[num_props++] =&lt;br /&gt;   XInternAtom(dpy, "_NET_WM_STATE_ABOVE", False);&lt;br /&gt; }&lt;br /&gt;if(flags &amp;amp; WIN_STICKY)&lt;br /&gt; {&lt;br /&gt; wm_states[num_props++] =&lt;br /&gt;   XInternAtom(dpy, "_NET_WM_STATE_STICKY", False);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;XChangeProperty(dpy, win, wm_state, XA_ATOM, 32,&lt;br /&gt;               PropModeReplace,&lt;br /&gt;               (unsigned char *)wm_states, num_props);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Grabbing methods&lt;/b&gt;&lt;br /&gt;The grabbing itself is done on the root window only. This will grab all other windows inside the grab area. The easiest method is &lt;code&gt;XGetImage&lt;/code&gt;, but that allocates a new image with each call. &lt;code&gt;malloc()/free()&lt;/code&gt; cycles within the processing loop should be avoided whenever possible. &lt;code&gt;XGetSubImage()&lt;/code&gt; allows to pass an allocated image. Much better of course is &lt;code&gt;XShmGetImage()&lt;/code&gt;. It was roughly 3 times faster than &lt;code&gt;XGetSubImage()&lt;/code&gt; in my tests.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Coordinate correction&lt;/b&gt;&lt;br /&gt;If parts of the grabbing rectangle are outside the root window, you'll get a &lt;code&gt;BadMatch&lt;/code&gt; error (usually exiting the program), no matter which function you use for grabbing. You must handle this case and correct the coordinates to stay within the root window.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mouse cursor&lt;/b&gt;&lt;br /&gt;Grabbing works for everything displayed on the screen (including XVideo overlays) except the mouse cursor. It must be obtained and drawn "manually" onto the grabbed image. Coordinates are read with &lt;code&gt;XQueryPointer()&lt;/code&gt;. The cursor image can be obtained if the XFixes extension is available. First we request cursor change events for the whole screen with&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;XFixesSelectCursorInput(dpy, root,&lt;br /&gt;                    XFixesDisplayCursorNotifyMask);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;If the cursor changed &lt;i&gt;and&lt;/i&gt; is within the grabbing rectangle we get the image with&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;im = XFixesGetCursorImage(dpy);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;The resulting cursor image is then converted to a &lt;code&gt;gavl_overlay_t&lt;/code&gt; and blended onto the grabbed image with a &lt;code&gt;gavl_overlay_blend_context_t&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Not done (yet)&lt;/b&gt;&lt;br /&gt;Other grabbing programs deliver images only when something has changed (resulting in a variable framerate stream). This can be achieved with the XDamage extension. Since the XDamage extension is (like many other X11 extensions) poorly documented, I didn't bother to implement this yet.&lt;br /&gt;&lt;br /&gt;One alternative is to use gmerlins decimate video filter, which compares the images in memory. The result will be the same, but CPU usage will be slightly increased.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-4554148166867151598?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/4554148166867151598/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=4554148166867151598' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4554148166867151598'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4554148166867151598'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/11/x11-grabbing-howto.html' title='X11 grabbing howto'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-477616669853725471</id><published>2009-10-31T16:27:00.003+01:00</published><updated>2009-10-31T16:35:15.347+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>gavl color channels</title><content type='html'>When programming something completely unrelated, I stumbled across a missing feature in gavl: Extract single color channels from a video frame into a grayscale frame. The inverse operation is to insert a color channel from a grayscale frame into a video frame (overwriting the contents of that channel). This allows you to assemble an image from separate color planes.&lt;br /&gt;&lt;br /&gt;Both were implemented with a minimalistic API (1 enum and 3 functions), which works for all 35 pixelformats. First of all we have an enum for the channel definitions:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;typedef enum&lt;br /&gt;{&lt;br /&gt;GAVL_CCH_RED,    // Red&lt;br /&gt;GAVL_CCH_GREEN,  // Green&lt;br /&gt;GAVL_CCH_BLUE,   // Blue&lt;br /&gt;GAVL_CCH_Y,      // Luminance (also grayscale)&lt;br /&gt;GAVL_CCH_CB,     // Chrominance blue (aka U)&lt;br /&gt;GAVL_CCH_CR,     // Chrominance red (aka V)&lt;br /&gt;GAVL_CCH_ALPHA,  // Transparency (or, to be more precise, opacity)&lt;br /&gt;} gavl_color_channel_t;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;For getting the exact grayscale format for one color channel you first call:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;int gavl_get_color_channel_format(const gavl_video_format_t * frame_format,&lt;br /&gt;                               gavl_video_format_t * channel_format,&lt;br /&gt;                               gavl_color_channel_t ch);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;It returns 1 on success or 0 if the format doesn't have the requested channel. After you have the channel format, extracting and inserting is done with:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;int gavl_video_frame_extract_channel(const gavl_video_format_t * format,&lt;br /&gt;                                   gavl_color_channel_t ch,&lt;br /&gt;                                   const gavl_video_frame_t * src,&lt;br /&gt;                                   gavl_video_frame_t * dst);&lt;br /&gt;&lt;br /&gt;int gavl_video_frame_insert_channel(const gavl_video_format_t * format,&lt;br /&gt;                                 gavl_color_channel_t ch,&lt;br /&gt;                                 const gavl_video_frame_t * src,&lt;br /&gt;                                 gavl_video_frame_t * dst);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;In the gmerlin tree, there are test programs &lt;code&gt;exctractchannel&lt;/code&gt; and &lt;code&gt;insertchannel&lt;/code&gt; which test the functions for all possible combinations of pixelformats and channels. They are in the gmerlin tree and not in gavl because we load and save the test images with gmerlin plugins.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-477616669853725471?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/477616669853725471/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=477616669853725471' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/477616669853725471'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/477616669853725471'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/10/gavl-color-channels.html' title='gavl color channels'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-4437573446857338873</id><published>2009-10-24T01:54:00.006+02:00</published><updated>2009-10-24T02:01:06.040+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Major gmerlin player upgrade</title><content type='html'>While working on several optimization tasks of the player engine, I found out that the player architecture sucked. So I made a major upgrade (well, a downgrade actually since lots of code was kicked out). Let me elaborate what exactly was changed.&lt;br /&gt;&lt;br /&gt;Below you see a block schematics of the player engine as it was before (subtitle handling is omitted for simplicity):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/SuJCXwNBK2I/AAAAAAAAAGk/V7Zvey94ulM/s1600-h/player_old.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 163px;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/SuJCXwNBK2I/AAAAAAAAAGk/V7Zvey94ulM/s400/player_old.png" alt="" id="BLOGGER_PHOTO_ID_5395948279387204450" border="0" /&gt;&lt;/a&gt;The audio- and video frames were read by the input thread from the file, pulled through the filter chains (see &lt;a href="http://hirntier.blogspot.com/2008/11/gmerlin-pipelines-explained.html"&gt;here&lt;/a&gt;) and pushed into the fifos.&lt;br /&gt;&lt;br /&gt;The output threads for audio and video pulled the frames from the fifos and sent them to the soundcard or the display window.&lt;br /&gt;&lt;br /&gt;The idea behind the separate input thread was that if CPU load is high and decoding a frame takes longer than it should, the output threads can still continue with the frames buffered in the fifos. It turned out that this was the &lt;i&gt;only&lt;/i&gt; advantage of this approach, and it only worked if the &lt;i&gt;average&lt;/i&gt; decoding time was still less than realtime.&lt;br /&gt;&lt;br /&gt;The major disadvantage is, that if you have fifos with frames &lt;i&gt;pushed&lt;/i&gt; at the input and &lt;i&gt;pulled&lt;/i&gt; at the output, the system becomes very prone to deadlocks. If fact, the code for the fifos became bloated and messy over the time.&lt;br /&gt;&lt;br /&gt;While programming a nice new feature (updating the video display while the seek slider is moved), the playback was messed up after seeking and I quickly blamed the fifos for this. The resulted was a big cleanup, the result is shown below:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_KslCmtD4qm8/SuJCwJIHGlI/AAAAAAAAAGs/TafBYunwoH0/s1600-h/player_new.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 384px; height: 198px;" src="http://4.bp.blogspot.com/_KslCmtD4qm8/SuJCwJIHGlI/AAAAAAAAAGs/TafBYunwoH0/s400/player_new.png" alt="" id="BLOGGER_PHOTO_ID_5395948698394368594" border="0" /&gt;&lt;/a&gt;You see, that the input thread and fifos are completely removed. Instead, the input plugin is protected by a simple mutex and the output threads do the decoding and processing themselves. The advantages are obvious:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Much less memory usage (one video frame instead of up to 8 frames in the fifo)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Deadlock conditions are much less likely (if not impossible)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Much simpler design, bugs are easier to fix&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;The only disadvantage is that if a file cannot be decoded in realtime, audio and video run out of sync. In the old design the input thread took care that the decoding of the streams didn't run too much out of sync. For these cases, I need to implement frame skipping. This can be done in several steps:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;If the stream has B-frames, skip them&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If the stream has P-frames, skip them (display only I-frames)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Display only every nth I-frame with increasing n&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Frame skipping is the next major thing to do. But with the new architecture it will be much simpler to implement than with the old one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-4437573446857338873?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/4437573446857338873/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=4437573446857338873' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4437573446857338873'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4437573446857338873'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/10/major-gmerlin-player-upgrade.html' title='Major gmerlin player upgrade'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_KslCmtD4qm8/SuJCXwNBK2I/AAAAAAAAAGk/V7Zvey94ulM/s72-c/player_old.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-6521968584659763003</id><published>2009-09-25T00:04:00.003+02:00</published><updated>2009-09-25T00:09:25.978+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Frame tables</title><content type='html'>When thinking a bit about video editing, I thought it might be nice to have the timestamps of all video frames in advance, i.e. without decoding the whole file. With that you can e.g. seek to the Nth frame or for a given absolute time you can find the closest video frame even for variable framerate files.&lt;br /&gt;&lt;br /&gt;The seeking functions always take a (scaled) time as argument and I saw no reason to change that.&lt;br /&gt;&lt;br /&gt;So what I needed was a table which allows the translation of framecounts to/from PTS values even for variable framerate files. For many fileformats a similar information is already stored internally (as a file index) so the task was only to convert the info into something usable and export it through the public API.&lt;br /&gt;&lt;br /&gt;Since this feature is very generic and might get used in both gmerlin and gmerlin-avdecoder, I decided to put the stuff into gavl.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Implicit table compression&lt;/b&gt;&lt;br /&gt;One very smart feature in the Quicktime format is the stts atom. That's because it stores a table of frame_count/frame_duration pairs. The nice thing is, that for constant framerate files (the vast majority) the table consists of just one entry and translating framecounts to/from timestamps becomes trivial. Only for variable framerate streams, the table has more entries and the translation functions need longer.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The implementation&lt;/b&gt;&lt;br /&gt;The frame table is called &lt;code&gt;gavl_frame_table_t&lt;/code&gt; and is defined in &lt;code&gt;gavl.h&lt;/code&gt;. &lt;i&gt;Note: The structure is public at present but might become private before the next public release.&lt;/i&gt;&lt;br /&gt;Here, there are also the translation functions &lt;code&gt;gavl_frame_table_frame_to_time()&lt;/code&gt;, &lt;code&gt;gavl_frame_table_time_to_frame()&lt;/code&gt; and &lt;code&gt;gavl_frame_table_num_frames()&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;If you use gmerlin-avdecoder for decoding files, you can use &lt;code&gt;bgav_get_frame_table()&lt;/code&gt; to obtain a frame table of a video stream. It can be called after the stream has been fully initialized. Naturally you will want to use sample accurate decoding mode before obtaining the frame table. If you want to reuse the frame table after the file was closed, use &lt;code&gt;gavl_frame_table_save()&lt;/code&gt; and &lt;code&gt;gavl_frame_table_load()&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Future extension&lt;/b&gt;&lt;br /&gt;An also interesting information are the timecodes. First of all, what's the difference between timecodes and timestamps in gmerlin terminology? Timestamps are used to synchronize multiple streams (audio, video, subtitles) of a file with each other for playback. They usually start at zero or another small value and play an important role e.g. after seeking in the files. Timecodes are usually given for each video frame and are used to identify scenes in a bunch of footage. They can resemble e.g. recording time/date.&lt;br /&gt;&lt;br /&gt;Timecodes will also be supported by the frame table, but this isn't implemented yet.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-6521968584659763003?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/6521968584659763003/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=6521968584659763003' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/6521968584659763003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/6521968584659763003'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/09/frame-tables.html' title='Frame tables'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-3764491986829126205</id><published>2009-09-15T01:14:00.003+02:00</published><updated>2009-09-15T01:26:52.098+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='travels'/><title type='text'>Back from Malta</title><content type='html'>After a number of adventure- and/or conference trips, I decided to make a more ordinary vacation. Since the goal was to extend the summer a bit, we decided to travel in the first 2 September weeks (when German weather can already start to suck) and move to the southernmost country, which can be reached without many difficulties. Easy traveling for Europeans means to stay within the EU (and &lt;a href="http://en.wikipedia.org/wiki/Euro"&gt;Euro&lt;/a&gt; zone), so the destination was chosen to be &lt;a href="http://en.wikipedia.org/wiki/Malta"&gt;Malta&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I knew a bit about Malta from TV documentaries, and while reading the Wikipedia article a bit, I became even more curious.&lt;br /&gt;&lt;br /&gt;Here are some facts I figured out. For pictures click on the Wikipedia links, they have higher quality than the ones from my crappy mobile.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It was damn hot and humid. Not all people from the Northern countries can withstand that. I was happy to find out that after surviving 4 weeks in the monsoon season in Southern India, I'm more heat resistant than my Greek companion.&lt;/li&gt;&lt;li&gt;It is indeed very stress-free because everyone speaks English (unlike in other Southern European countries) and the islands are small enough to reach practically every destination by bus. Just make sure you stay in the capital &lt;a href="http://en.wikipedia.org/wiki/Valletta"&gt;Valletta&lt;/a&gt;, almost all bus routes start there. On the smaller island &lt;a href="http://en.wikipedia.org/wiki/Gozo"&gt;Gozo&lt;/a&gt;, all busses start in &lt;a href="http://en.wikipedia.org/wiki/Victoria,_Malta"&gt;Victoria&lt;/a&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It has an extremely rich 7000 years old history. You can visit &lt;a href="http://en.wikipedia.org/wiki/Megalithic_Temples_of_Malta"&gt;prehistoric temples&lt;/a&gt; (probably the oldest of their kind worldwide), churches, fortresses and remains from the &lt;a href="http://en.wikipedia.org/wiki/Ancient_Rome"&gt;Romans&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Phoenicia"&gt;Phoenicians&lt;/a&gt;. I already saw many churches from the inside before, I was not really a fan of Latin language and Roman history in school, and my companion didn't want to bother looking for sight seeing destinations at all. So I decided to concentrate on the prehistoric stuff.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;We saw the temples of &lt;a href="http://en.wikipedia.org/wiki/%C4%A6a%C4%A1ar_Qim"&gt;Ħaġar Qim&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Mnajdra"&gt;Mnajdra&lt;/a&gt; (both covered with giant protective tents now), &lt;a href="http://en.wikipedia.org/wiki/Tarxien_Temples"&gt;Tarxien&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/%C4%A0gantija"&gt;Ġgantija&lt;/a&gt;. The latter ones were the most impressive, especially after we reached them after a long walk in the afternoon heat 20 minutes before the last admission :)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The temples of &lt;a href="http://en.wikipedia.org/wiki/Skorba_Temples"&gt;Skorba&lt;/a&gt; are the oldest ones, but not very spectacular and not accessible for the public.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;No chance to get into the &lt;a href="http://en.wikipedia.org/wiki/Hypogeum_of_%C4%A6al-Saflieni"&gt;Hypogeum of Ħal-Saflieni&lt;/a&gt;. It was booked out 4 weeks in advance. That sucked.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If you like beaches, Malta is definitely not the #1 destination for you. There are just a few, a nice one we visited is &lt;a href="http://en.wikipedia.org/wiki/Ramla_Bay"&gt;Ramla Bay&lt;/a&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If you like diving at cliffs, Malta is a paradise for you.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Funny is the bit of British culture (Malta was British until 1964). People are driving on the left, you get chips with most meals, and the phone booths and mailboxes are red, like in the UK.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Some Maltese people know more German soccer teams than me.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Shortly after coming back home, German weather started to suck.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-3764491986829126205?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/3764491986829126205/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=3764491986829126205' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/3764491986829126205'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/3764491986829126205'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/09/back-from-malta.html' title='Back from Malta'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-5207184383942264085</id><published>2009-08-14T17:56:00.003+02:00</published><updated>2009-08-14T18:06:22.640+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='computer'/><title type='text'>Quick &amp; dirty fix for the latest linux NULL pointer vulnerability</title><content type='html'>&lt;a href="http://lwn.net/Articles/347006/"&gt;This&lt;/a&gt; one is pretty scary. It is the result of several flaws in SELinux, pulseaudio and some obscure network protocols. Proper fixing of this would require work at many places in the code.&lt;br /&gt;&lt;br /&gt;Up to now, Ubuntu doesn't have a patched kernel. In the meantime, place the following into the modprobe configuration:&lt;br /&gt;&lt;pre&gt;install appletalk /bin/true&lt;br /&gt;install ipx /bin/true&lt;br /&gt;install irda /bin/true&lt;br /&gt;install x25 /bin/true&lt;br /&gt;install pppox /bin/true&lt;br /&gt;install bluetooth /bin/true&lt;br /&gt;install sctp /bin/true&lt;br /&gt;install ax25 /bin/true&lt;br /&gt;&lt;/pre&gt;Then either unload these modules by hand (if they are loaded) or reboot the machine. One some systems I had to uninstall bluetooth support, which wasn't needed anyway. Naturally these protocols will stop working, but fortunately the exploit will stop working either :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-5207184383942264085?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/5207184383942264085/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=5207184383942264085' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/5207184383942264085'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/5207184383942264085'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/08/quick-dirty-fix-for-latest-linux-null.html' title='Quick &amp; dirty fix for the latest linux NULL pointer vulnerability'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-4953650007316106499</id><published>2009-08-07T00:53:00.007+02:00</published><updated>2009-08-07T01:28:07.872+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='spam'/><title type='text'>Spambot ladies @ blogspot</title><content type='html'>She left &lt;a href="https://www.blogger.com/comment.g?blogID=2134600781755905611&amp;amp;postID=442538920056180985"&gt;this&lt;/a&gt; heart warming comment on my blog. Out of curiosity, I wanted to see what Martha aka Susan blogs &lt;a href="http://kidsmusic1.blogspot.com/"&gt;herself&lt;/a&gt;. Then I wondered, how a person, who isn't even able to blog one meaningful sentence, can be interested in my articles. But &lt;a href="http://www.google.de/search?hl=en&amp;amp;q=%22I+recently+came+accross+your+blog+and+have+been+reading+along%22+site%3Ablogspot.com"&gt;look&lt;/a&gt;, this lady seems to have an extremely wide range of interests (although she's not very imaginative in commenting):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/SntiKC4nqlI/AAAAAAAAAGc/psNOhDZyO1s/s1600-h/spambot.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 278px;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/SntiKC4nqlI/AAAAAAAAAGc/psNOhDZyO1s/s400/spambot.png" alt="" id="BLOGGER_PHOTO_ID_5366991305654643282" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;She has multiple blogs under different names, and some people even &lt;a href="http://kidsmusic1.blogspot.com/2009/05/play-instruments-are-party-fun-music-to.html#comments"&gt;talk&lt;/a&gt; to her as if she was a human.&lt;br /&gt;&lt;br /&gt;Let's see how this develops in the future. Jack Crossfire, the original author of cinelerra and quicktime4linux (from which we forked libquicktime), also has something to &lt;a href="http://www.rcgroups.com/forums/showthread.php?t=1087015"&gt;say&lt;/a&gt; on this matter.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-4953650007316106499?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/4953650007316106499/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=4953650007316106499' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4953650007316106499'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4953650007316106499'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/08/spambot-ladies-blogspot.html' title='Spambot ladies @ blogspot'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_KslCmtD4qm8/SntiKC4nqlI/AAAAAAAAAGc/psNOhDZyO1s/s72-c/spambot.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-1735091539027784305</id><published>2009-08-01T02:22:00.006+02:00</published><updated>2010-03-04T14:23:32.149+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>AVCHD timecodes revealed</title><content type='html'>When playing with my new Canon HF200 camera, I got curious where the recording time (and date) is hidden in the AVCHD format.&lt;br /&gt;&lt;br /&gt;The first idea was the SEI pic timing message of the H.264 stream. I already parse it for getting information whether pictures are frame- or field coded. So I extended my code to parse the timecode in HH:MM:SS:FF format, only to find out, that this info isn't present at all in my files :(&lt;br /&gt;&lt;br /&gt;Googling for more informations about that, I found that nobody knows how to get the recording time and even professional programs fail to display it. But some very few programs do, so we know that it &lt;i&gt;must&lt;/i&gt; be coded in the transport stream itself (and not in the other files written by the camera).&lt;br /&gt;&lt;br /&gt;Finally I found &lt;a href="http://forum.videohelp.com/images/guides/p1953302/canon_analyze_mts.zip"&gt;this&lt;/a&gt; perl script, which extracts the date and time from canon mts files. It's a pretty simple implementation: It scans the multiplexed transport stream for a particular bit-pattern and then extracts the data. The script works for Canon-files but fails e.g. for Panasonic files.&lt;br /&gt;&lt;br /&gt;Then I found where exactly the information is located: A H.264 stream has SEI (supplemental enhancement information) messages, which can contain additional (e.g. timing) information. For each SEI message the parser can obtain the message type (an integer) and the size of the message in bytes. AVCHD files have SEI messages of type 5, which means "user data unregistered" (== proprietary extension). The H.264 standard says, that these messages start with a 16 byte GUID followed by the payload.&lt;br /&gt;&lt;br /&gt;Now take a look at the hexdump of such an SEI message:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;17 ee 8c 60 f8 4d 11 d9 8c d6 08 00 20 0c 9a 66 ...`.M...... ..f&lt;br /&gt;4d 44 50 4d 09 18 02 20 09 08 19 01 01 25 45 70 MDPM... .....%Ep&lt;br /&gt;c7 f2 ff ff 71 ff ff ff ff 7f 00 00 65 84 e0 10 ....q.......e...&lt;br /&gt;11 30 02 e1 07 ff ff ff ee 19 19 02 00 ef 01 c0 .0..............&lt;br /&gt;00 00                                           ..&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;From this I found the following structure:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The GUID is the first 16 bytes. It's always the same for the info we want, but I found other SEI messages of type 5 with different GUIDs in AVCHD files.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;4 characters "MDPM". They occur in all files I looked at.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;An unknown byte (0x09, other vendors have other values)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The byte 0x18 (probably indicating that year and month follow)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;An unknown byte (0x02, other vendors have other values)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The year and month in 3 BCD coded bytes: 0x20 0x09 0x08&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The byte 0x19 (probably indicating that day and time follow)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;An unknown byte (0x01, other vendors have other values)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The day, hour, minute and second as 4 BCD encoded bytes (0x01 0x01 0x25 0x45)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;In this case, I extract the recording time "2009-08-01 01:25:45" (which is correct).&lt;br /&gt;&lt;br /&gt;The remainder of the SEI is completely unknown, but I'm sure if someone would figure out the complete data structure (including the unknown bytes), one might be able to extract other interesting informations.&lt;br /&gt;&lt;br /&gt;These messages are present for almost all frames, but I plan to read them only from the first frame because the following ones are redundant.&lt;br /&gt;&lt;br /&gt;Next project will be to clean up the parsing code in gmerlin-avdecoder and make the timecode actually appear along with the first decoded frame.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-1735091539027784305?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/1735091539027784305/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=1735091539027784305' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1735091539027784305'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1735091539027784305'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/08/avchd-timecodes-revealed.html' title='AVCHD timecodes revealed'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-9067000747256216116</id><published>2009-07-20T23:45:00.003+02:00</published><updated>2009-07-21T00:13:03.107+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>GPU accelerated H.264 decoding</title><content type='html'>A while ago I bought a new &lt;a href="http://www.canon.de/For_Home/Product_Finder/Camcorders/High_Definition_HD/LEGRIA_HF_200/index.asp"&gt;camera&lt;/a&gt; and I had to learn that my dual core machine isn't able to play footage encoded in highest quality level in realtime (second highest quality works). Fortunately, ffplay can't do that either so it's not gmerlins fault. &lt;br /&gt;&lt;br /&gt;H.264 decoding on the graphics card can be done with &lt;a href="http://en.wikipedia.org/wiki/VDPAU"&gt;vdpau&lt;/a&gt; or &lt;a href="http://en.wikipedia.org/wiki/VAAPI"&gt;vaapi&lt;/a&gt;. The former is Nvidia specific and libavcodec can use it for H.264. The latter is vendor independent (it can use vdpau as backend on nvidia cards) but H.264 decoding with vaapi is not supported by ffmpeg yet.&lt;br /&gt;&lt;br /&gt;In principle I prefer vendor independent solutions, but since I need H.264 support and ATI cards suck anyway on Linux, I tried VDPAU first.&lt;br /&gt;&lt;br /&gt;The implementation in my libavcodec video frontend was straightforward after studying the MPlayer source. The VDPAU codecs are completely separated from the other codecs. They can simply be selected e.g. with &lt;code&gt;avcodec_find_decoder_by_name("h264_vdpau")&lt;/code&gt;. Then, one &lt;i&gt;must&lt;/i&gt; supply callback functions for &lt;code&gt;get_buffer&lt;/code&gt;, &lt;code&gt;release_buffer&lt;/code&gt; and &lt;code&gt;draw_horiz_band&lt;/code&gt;. That's because the rendering targets are no longer frames in memory but rather handles of data-structures on the GPU. See &lt;a href="http://gmerlin.cvs.sourceforge.net/viewvc/gmerlin/gmerlin_avdecoder/lib/vdpau.c?view=markup"&gt;here&lt;/a&gt; and &lt;a href="http://gmerlin.cvs.sourceforge.net/viewvc/gmerlin/gmerlin_avdecoder/lib/video_ffmpeg.c?view=markup"&gt;here&lt;/a&gt; to see the details.&lt;br /&gt;&lt;br /&gt;After the decoding, the image data is copied to memory by calling &lt;code&gt;VdpVideoSurfaceGetBitsYCbCr&lt;/code&gt;. This brings of course a severe slowdown. A much better way would be to keep the frames in graphics memory as long as possible. But this needs to be done in a much more generic way: Images can be VDPAU or VAAPI video surfaces, OpenGL textures or whatever. Implementing generic support for video frames, which are not in regular RAM, will be another project.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-9067000747256216116?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/9067000747256216116/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=9067000747256216116' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/9067000747256216116'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/9067000747256216116'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/07/gpu-accelerated-h264-decoding.html' title='GPU accelerated H.264 decoding'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-1822933221222035469</id><published>2009-07-13T23:53:00.001+02:00</published><updated>2009-07-13T23:54:42.611+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Gmerlin releases</title><content type='html'>Get them &lt;a href="http://gmerlin.sourceforge.net/"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-1822933221222035469?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/1822933221222035469/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=1822933221222035469' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1822933221222035469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1822933221222035469'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/07/gmerlin-releases.html' title='Gmerlin releases'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-7199482340569003058</id><published>2009-07-01T18:27:00.003+02:00</published><updated>2009-07-01T19:18:18.485+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Linux serial port driver madness</title><content type='html'>Serial ports are almost extinct on desktop PCs because everyone uses USB or ethernet. But for many automation tasks, RS-232 connections are still state of the art. That's mostly because they are well standardized since the 60s and UART chips are cheap.&lt;br /&gt;&lt;br /&gt;I wanted to write a program, which uses a serial port for controlling 2 stepper motors. I wanted to write it such that users have the least possible trouble. Especially during the startup phase, a dialog box should show all available serial ports and let the user select the right one.&lt;br /&gt;&lt;br /&gt;And while trying to get all available serial ports, I stumbled across 3 nasty linux bugs:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bug 1: Always 4 serial port devices&lt;/b&gt;&lt;br /&gt;One of the intentions of udev was that only the devices, which are physically present on the system, appear as nodes in the &lt;code&gt;/dev&lt;/code&gt; directory. A big step forward compared to the situation before. Unfortunately, the serial driver always creates &lt;code&gt;/dev/ttyS[0-3]&lt;/code&gt;. The reason (forgot the link) is that the ports of some exotic multi I/O board aren't detected properly. So the &lt;span style="font-style: italic;"&gt;fix&lt;/span&gt; was to get 1000s of users into trouble instead of just making one special module parameter for that board.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bug 2: open() succeeds for nonexistant ports&lt;/b&gt;&lt;br /&gt;The manual page of open(2) says, that if one tries to open a device node with no physical device is behind it, errno is set to &lt;code&gt;ENODEV&lt;/code&gt; or &lt;code&gt;ENXIO&lt;/code&gt;. In the case of a non existing serial port, a valid filedescriptor is returned&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bug 3: tcgetattr returns &lt;code&gt;EIO&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;According to the glibc manual &lt;code&gt;EIO&lt;/code&gt; is usually used for physical read/write errors. For regular files this error means, that you should backup your data because the disk is about to die. A &lt;span style="font-style: italic;"&gt;physical&lt;/span&gt; read/write error can never happen on a device which is &lt;span style="font-style: italic;"&gt;physically nonexistant&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;The second best thing would be to return &lt;code&gt;ENXIO&lt;/code&gt; (no such device or address). The best thing would be to return &lt;code&gt;EBADF&lt;/code&gt; (bad file descriptor) because the &lt;code&gt;open()&lt;/code&gt; call before would have returned -1 already.&lt;br /&gt;&lt;br /&gt;At least &lt;code&gt;tcgetattr()&lt;/code&gt; doesn't succeed like &lt;code&gt;open()&lt;/code&gt; so it can be used for the detection routine below.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The solution&lt;/b&gt;&lt;br /&gt;The good thing is that once there is a workaround, the problem is quickly forgotten. Here's mine (returns 1 if the port is a physically existant serial device, 0 else):&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;#include &amp;lt;termios.h&amp;gt;&lt;br /&gt;#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;&lt;br /&gt;int is_serial_port(const char * device)&lt;br /&gt; {&lt;br /&gt; int fd, ret = 1;&lt;br /&gt; struct termios t;&lt;br /&gt; fd = open(device, O_RDWR);&lt;br /&gt;&lt;br /&gt; if(fd &amp;lt; 0)&lt;br /&gt;   return 0;&lt;br /&gt;&lt;br /&gt; if(tcgetattr(fd, &amp;amp;t))&lt;br /&gt;   ret = 0;&lt;br /&gt; close(fd);&lt;br /&gt; return ret;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Of course this won't find ports, which are currently opened by another application.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-7199482340569003058?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/7199482340569003058/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=7199482340569003058' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7199482340569003058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7199482340569003058'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/07/linux-serial-port-driver-madness.html' title='Linux serial port driver madness'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-2040434538626724607</id><published>2009-06-26T00:32:00.001+02:00</published><updated>2009-06-26T00:33:55.388+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>New gmerlin prereleases</title><content type='html'>Here we go:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gmerlin.sourceforge.net/gmerlin-all-in-one-20090626.tar.bz2"&gt;http://gmerlin.sourceforge.net/gmerlin-all-in-one-20090626.tar.bz2&lt;/a&gt;&lt;br /&gt;&lt;a href="http://gmerlin.sourceforge.net/gmerlin-dependencies-20090625.tar.bz2"&gt;http://gmerlin.sourceforge.net/gmerlin-dependencies-20090625.tar.bz2&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-2040434538626724607?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/2040434538626724607/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=2040434538626724607' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/2040434538626724607'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/2040434538626724607'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/06/new-gmerlin-prereleases.html' title='New gmerlin prereleases'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-1481033136420896911</id><published>2009-06-25T01:03:00.003+02:00</published><updated>2009-06-25T01:36:23.014+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='society'/><category scheme='http://www.blogger.com/atom/ns#' term='Misc'/><category scheme='http://www.blogger.com/atom/ns#' term='computer'/><category scheme='http://www.blogger.com/atom/ns#' term='politics'/><title type='text'>What to expect for the remaining century</title><content type='html'>&lt;a href="http://www.theregister.co.uk/2009/06/17/german_block_list/"&gt;Web censorship by police agencies&lt;/a&gt;, &lt;a href="http://www.wired.com/threatlevel/2009/06/riaa-jury-slaps-2-million-fine-on-jammie-thomas/"&gt;terrorism&lt;/a&gt; of the music industry against ordinary people and other scaring stories indicate that society changes dramatically these days. These changes are in my opinion hopelessly underestimated by most. But what exactly is happening? I'll try to explain it with some examples from history.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Technological developments are triggers for social changes&lt;/b&gt;&lt;br /&gt;It has been always like this: If a new technology appears, it offers both chances and challenges. Chances to solve some problems of mankind come always along with the challenge to use it responsibly. If you look back in history, you'll find many examples, where inventions resulted in revolutionary changes of society.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;6000 BC&lt;/b&gt;&lt;br /&gt;One of the earliest known examples for this phenomenon is the invention of irrigation around 6000 BC in Mesopotamia. It triggered the following chain reaction:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Farmland became more efficient allowing a higher population density&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Higher population density results either in self-extinction, mass exodus or a better organization. The Mesopotamians were smart enough to choose the latter.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Suddenly there was some essential infrastructure (the irrigation channels), which had to be maintained. People who maintain irrigation channels cannot work on the fields at the same time, so the division of labor had to be invented&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If you have division of labor, you need trade&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If you have trade, you need money&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If you have money, you need mathematics&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Money and trade hardly work if you don't invent some alphabet&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;What looks like an amazingly fast transition from an archaic rural society to an urban civilization is, in my opinion, simply a result of some smart farmers no longer wanting to wait for rain.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;20th century&lt;/b&gt;&lt;br /&gt;The beginning of the 20th century was characterized by by lots of revolutionary advances especially in electronic communication (radio and TV), transportation (cars and airplanes) and manufacturing (mass production). Some of the results follow:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Ordinary people could more easily stay informed about political affairs. Without a well informed population modern democracy is impossible.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;What we call a global society today, became possible through electronic communication.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Many cultural genres, e.g. music styles, are no longer local (or national) phenomena but global ones.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In years where the weather sucks (from the farmers points of view) people no longer have to be afraid of famines (today's famines always have political reasons). This feeling of safety has a large impact on human mind and society.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;As you might see, the technological revolution at the beginning of the 20th century was an important trigger for the development of the so called Western Civilization we are so proud of today.&lt;br /&gt;&lt;br /&gt;Irresponsible uses of 20th century technology were European dictatorships (which utilized the then new electronic mass media for propaganda) resulting in 2 World Wars (made possible by the newly invented cars and planes).&lt;br /&gt;&lt;br /&gt;The biggest challenge of the 20th century was &lt;b&gt;not&lt;/b&gt; to start a nuclear war. Before, all weapons ever developed were eventually used. The atomic bomb was never used in a war by any nation except &lt;a href="http://en.wikipedia.org/wiki/Atomic_bombings_of_Hiroshima_and_Nagasaki"&gt;one&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Beginning of the 21st century&lt;/b&gt;&lt;br /&gt;The beginning of the 21st century was characterized by masses of ordinary people beginning to use the &lt;i&gt;internet&lt;/i&gt;. Many argue, that the internet is just another means of communication like the ones we had before. This is completely wrong imo. All mass media we had before were &lt;i&gt;unidirectional&lt;/i&gt; (&lt;i&gt;few&lt;/i&gt; content producers serve &lt;i&gt;many&lt;/i&gt; consumers). The internet is &lt;i&gt;multidirectional&lt;/i&gt; (every content consumer can also be a producer). This is a completely different topology of information flow with possibilities far beyond everything we had before. Just look at some examples of what was achieved by now:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;People write an &lt;a href="http://www.wikipedia.org/"&gt;encyclopedia&lt;/a&gt;, which is larger, more up-to-date but not more incorrect than any paper-encyclopedia you could buy before.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;1000s of computer nerds spending their nights in front of their PCs wrote one of the world's best &lt;a href="http://en.wikipedia.org/wiki/Linux"&gt;OSes&lt;/a&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In times of war or unrest, gagging orders no longer work&lt;br /&gt;&lt;/li&gt;&lt;li&gt;No matter what absurd &lt;a href="http://en.wikipedia.org/wiki/Flying_Spaghetti_Monster"&gt;theories&lt;/a&gt; you believe, you'll always find like-minded people.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Some &lt;a href="http://www.zeitgeistmovie.com/"&gt;internet movies&lt;/a&gt; become more popular than some commercial Hollywood productions.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Future developments&lt;/b&gt;&lt;br /&gt;Some things I see coming:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Internet communities can make big achievements even though they are mostly &lt;i&gt;self organized&lt;/i&gt;. Politicians are afraid of self organizing structures because they fear to become superfluous.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;People, who work voluntarily on self-organized projects, will no longer feel the need to be ruled by politicians.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Big record companies will die out, because they &lt;a href="http://www.wired.com/threatlevel/2009/06/riaa-jury-slaps-2-million-fine-on-jammie-thomas/"&gt;terrorized&lt;/a&gt; their customers and ripped off musicians. Even Paul McCartney let Starbucks &lt;a href="http://news.bbc.co.uk/2/hi/entertainment/6476843.stm"&gt;merchandise&lt;/a&gt; an album already. Also they still follow the obsolete model of &lt;i&gt;few&lt;/i&gt; producers serving &lt;i&gt;many&lt;/i&gt; consumers. Only small manufacturers of vinyl disks will survive (at least as long as there are DJs who know how to use them).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Traditional newspapers will become superfluous because they cost money and the 12 hour delay due to the printing time will be unacceptable for most people.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Traditional radio and TV-stations will die out because people don't want to read programming schedules anymore. They want to see and hear &lt;i&gt;what&lt;/i&gt; they want &lt;i&gt;when&lt;/i&gt; they want.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;With no &lt;i&gt;unidirectional&lt;/i&gt; mass media left, controlling the information flow will become impossible. As a result, Dictators and conspirators will have a hard time.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;We see, that today's elites have enough reasons to try to control the internet. Actually there are 2 possible outcomes:&lt;br /&gt;&lt;br /&gt;1. They learn that their undertaking is doomed to failure and try to arrange with the new conditions before it's too late.&lt;br /&gt;2. They will continue trying and the WWW will move from today's client-server model to strongly encrypted p2p technologies like &lt;a href="http://freenetproject.org/"&gt;freenet&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I personally would prefer option 1, but in either case they'll lose.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Challenges&lt;/b&gt;&lt;br /&gt;As said before, new technologies also bring new challenges:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Computer expertise becomes more and more important for ordinary people. In the last century, learning to read and write was already a good start of the career. Today, knowing to use a computer mouse is at least as important.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The fact that in the internet &lt;i&gt;everyone&lt;/i&gt; can be content provider naturally results in a large percentage of bullshit. The &lt;i&gt;preselection&lt;/i&gt; of information, which was done by editorial offices of the traditional mass media, must now be done by the consumer. The advantage of &lt;i&gt;unfiltered&lt;/i&gt; information comes along with the task to decide yourself.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Even if you don't agree with my theories explained above, you know at least where the title of my blog comes from :)&lt;br /&gt;&lt;br /&gt;If you agree with what I said, you'll like &lt;a href="http://vimeo.com/4489849"&gt;this&lt;/a&gt; movie.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-1481033136420896911?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/1481033136420896911/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=1481033136420896911' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1481033136420896911'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1481033136420896911'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/06/what-to-expect-for-remaining-century.html' title='What to expect for the remaining century'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-4712795878197495663</id><published>2009-06-19T00:29:00.005+02:00</published><updated>2009-06-19T01:13:14.434+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computer'/><category scheme='http://www.blogger.com/atom/ns#' term='politics'/><title type='text'>Being ruled by morons</title><content type='html'>&lt;b&gt;17. June 2009:&lt;/b&gt; German parliament &lt;a href="http://www.bundestag.de/aktuell/archiv/2009/24774354_kw25_17juni/index.html"&gt;commemorates&lt;/a&gt; the &lt;a href="http://en.wikipedia.org/wiki/Uprising_of_1953_in_East_Germany"&gt;uprising&lt;/a&gt; in Eastern Germany of 1953. The uprising was called the &lt;i&gt;start of a fight for freedom, which was successful in the end&lt;/i&gt; (resulting in the &lt;a href="http://en.wikipedia.org/wiki/German_reunification"&gt;German reunification&lt;/a&gt; in 1990). Before 1990, the 17. of June was the national holiday in Western Germany.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;18. June 2009:&lt;/b&gt; German parliament passes a &lt;a href="http://www.theregister.co.uk/2009/06/17/german_block_list/"&gt;law&lt;/a&gt;, which allows the federal German police agency to put together a blacklist of domains, which must be blocked by ISPs. This law violates many fundamental principles of German constitution like &lt;a href="http://en.wikipedia.org/wiki/Separation_of_powers"&gt;separation of powers&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Federalism"&gt;federalism&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Freedom_of_information"&gt;freedom of information&lt;/a&gt; and the prohibition of censorship.&lt;br /&gt;&lt;br /&gt;Below you'll find some preliminary success stories regarding the latest developments:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;As explained &lt;a href="http://hirntier.blogspot.com/2009/05/what-evildoers-do-if-web-gets-censored.html"&gt;before&lt;/a&gt;, the actual target group of this law (producers and consumers of child pornography) can be more relaxed than before, because it comes &lt;a href="http://www.youtube.com/watch?v=1NNG5I6DBm0"&gt;pretty&lt;/a&gt; &lt;a href="http://sourceforge.net/projects/censorshiptools"&gt;handy&lt;/a&gt; for them. You'll see none of them demonstrating against this.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Conspiracy theorists said from the beginning, that the fight against child pornography is just a bogus argument for installing a censorship infrastructure. Fortunately, some politicians were even stupid enough to admit this in public.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Before this madness started, there was no noticeable digital civil rights movement in Germany. This changed dramatically during the last months. Now they are many and perfectly organized. On Saturday, there will be coordinated demonstrations in at least 11 German cities.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;An official &lt;a href="https://epetitionen.bundestag.de/index.php?action=petition;sa=details;petition=3860"&gt;online petition&lt;/a&gt; got signed by 134014 people, an all-time record in the history of online-petitions.&lt;br /&gt;&lt;li&gt;The German &lt;a href="http://en.wikipedia.org/wiki/Social_Democratic_Party_of_Germany"&gt;Social Democratic Party&lt;/a&gt; is one big step further in making itself superfluous. I predict that some of the last bright minds will leave the party before the next parliamentary elections in September.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Rulers of China and Iran no longer have to fear bothering remarks regarding their censorship policies from German politicians. Improved bilateral relations are good for German economy, which is highly dependent on foreign sales markets and energy resources.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Almost unknown a year ago, the German &lt;a href="http://www.piratenpartei.de/"&gt;Pirate Party&lt;/a&gt; got 0.9% in the European parliamentary elections, with constantly rising popularity.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;This is the &lt;i&gt;start of a fight for freedom, which will be successful in the end&lt;/i&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-4712795878197495663?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/4712795878197495663/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=4712795878197495663' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4712795878197495663'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4712795878197495663'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/06/being-ruled-by-morons.html' title='Being ruled by morons'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-5948187865035002004</id><published>2009-06-10T00:32:00.002+02:00</published><updated>2009-06-10T00:40:19.882+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>gmerlin video thumbnailer</title><content type='html'>&lt;b&gt;The project&lt;/b&gt;&lt;br /&gt;The goal of this 2-day project was to write a gmerlin based video thumbnailer, which fails for fewer files than the gstreamer based thumbnailer shipped with Ubuntu. The result is a small (less than 350 lines) program, which takes a video file as argument and produces a small png preview. All required functionality was already available in gmerlin and gavl.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;How to try&lt;/b&gt;&lt;br /&gt;If you want, you can check out and install the latest gmerlin CVS. Make sure that gmerlin is compiled with libpng support. The program is called &lt;code&gt;gmerlin-video-thumbnailer&lt;/code&gt;. To enable gmerlin thumbnails in nautilus, open &lt;code&gt;gconf-editor&lt;/code&gt; and go to &lt;code&gt;/desktop/gnome/thumbnailers&lt;/code&gt;. For each mimetype, you have a directory. In each directory, you have a key called &lt;code&gt;command&lt;/code&gt;. For each mimetype you want to be thumbnailed by gmerlin, simply change the command to&lt;br /&gt;&lt;br /&gt;&lt;code&gt;/usr/local/bin/gmerlin-video-thumbnailer -s %s %i %o&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Of course you must change &lt;code&gt;/usr/local&lt;/code&gt; to the prefix where you installed gmerlin.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bugs fixed&lt;/b&gt;&lt;br /&gt;The thumbnailer quickly exposed some bugs in gmerlin-avdecoder and gavl. Looking at a nautilus window full of thumbnails is faster than playing 100s of movie files. 3 major bugs were fixed, one minor bug will get fixed before the next release.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;References&lt;/b&gt;&lt;br /&gt;The thumbnail standard, which seems to be used by gnome, is &lt;a href="http://jens.triq.net/thumbnail-spec/index.html"&gt;here&lt;/a&gt;. How thumbnailers are installed in gnome, is described &lt;a href="http://library.gnome.org/devel/integration-guide/stable/thumbnailer.html.en"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-5948187865035002004?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/5948187865035002004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=5948187865035002004' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/5948187865035002004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/5948187865035002004'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/06/gmerlin-video-thumbnailer.html' title='gmerlin video thumbnailer'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-449832223780233228</id><published>2009-06-06T01:09:00.003+02:00</published><updated>2009-06-06T01:12:26.771+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Gmerlin prereleases</title><content type='html'>Check these out:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gmerlin.sourceforge.net/gmerlin-dependencies-20090606.tar.bz2"&gt;http://gmerlin.sourceforge.net/gmerlin-dependencies-20090606.tar.bz2&lt;/a&gt;&lt;br /&gt;&lt;a href="http://gmerlin.sourceforge.net/gmerlin-all-in-one-20090606.tar.bz2"&gt;http://gmerlin.sourceforge.net/gmerlin-all-in-one-20090606.tar.bz2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you want to help pushing things forward, try to install and run them and report any problems you encountered.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-449832223780233228?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/449832223780233228/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=449832223780233228' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/449832223780233228'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/449832223780233228'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/06/gmerlin-prereleases.html' title='Gmerlin prereleases'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-442538920056180985</id><published>2009-05-25T23:29:00.003+02:00</published><updated>2009-05-26T00:05:11.546+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computer'/><category scheme='http://www.blogger.com/atom/ns#' term='politics'/><title type='text'>What evildoers do, if the web gets censored</title><content type='html'>Lots of discussion happens in German cyberspace around the planned blocking of child porn. It is foreseen to redirect DNS queries for blacklisted domains to a "Stop" sign, which will tell the user, that the site (s)he wanted to visit is blocked. Contrary to earlier promises the IP addresses of the users will be made available to the law enforcement agencies for what they call &lt;i&gt;real time surveillance&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;The proponents (one of them confused "servers" and "surfers" in a parliament debate) try their best to make every opponent of this plan look like a pedophile.&lt;br /&gt;&lt;br /&gt;I think it's the opposite: Child molesters and other evildoers will have a lot of fun once the blocking mechanism is operational.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Disclaimer&lt;/b&gt;&lt;br /&gt;&lt;b&gt;I neither plan nor endorse any of the activities listed below.&lt;/b&gt; I only describe theoretical possibilities to demonstrate, how easy it is to abuse such a nonsense.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Things to come&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Pedophile newbies, who search for child porn, will find a leaked block-list from another country. The secret German list will leak as well, sooner or later. It was never as easy as today.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Less smart pedophiles will google for ways to circumvent the blockade. The first hit will be some commercial "surf anonymous" proxy provider. In the end, even more people will earn money with abused kids.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Smarter pedophiles will google better and configure another DNS server. If using another DNS server becomes illegal, they'll find a free SSL proxy. If SSL proxies become illegal, they'll use foreign VPN provider. Connections to foreign VPN providers cannot be illegal because they are used by lots of big companies.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Less smart child porn webmasters will simply use numeric IPs for linking to their sites&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Smarter webmasters will regularly send a DNS query for their domain to a censored DNS server. If the returned IP number becomes incorrect, it's time to shut down the site and reopen it somewhere else under a different name. Can easily be scripted into a fully automated warning system. Also works if the block-list is &lt;i&gt;really&lt;/i&gt; secret.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Never give your IP address to a bad guy. Today you can get hacked, if you have a poor firewall. Tomorrow the cops might pay you a visit. There is no firewall against cops wanting to visit you. That's because one can easily flood the DNS server of your German provider with forged requests for some blocked domain and your IP address as source. Should be no more than 50 lines in C, including comments.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The ultimate end of civilization is near if the first stop-sign worm appears. Not only because it'll DDOS the stop-sign servers. Most important effect will be to flood the law enforcement agencies with IPs of innocent people. Less time to concentrate on the bad guys.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Those, who know the facts and still want to install DNS blockades against child porn, are either pedophiles, criminals or conspirators who'd like a censorship mechanism for all kinds of unpleasant content. The latter might be the reason why each country maintains it's own block list.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Alternatives&lt;/b&gt;&lt;br /&gt;Everyone except the pedophiles supports a serious fight against child abuse and child porn, not only in the internet. There are lots of organizations, who are committed to this. Interestingly, some of them are against blockades. Here are 3 German ones:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.carechild.de/"&gt;Carechild&lt;/a&gt;&lt;br /&gt;&lt;a href="http://mogis.wordpress.com/"&gt;Abuse victims against internet blockades&lt;/a&gt;&lt;br /&gt;&lt;a href="http://trotzallem.de/"&gt;Self-help group of abused women&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-442538920056180985?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/442538920056180985/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=442538920056180985' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/442538920056180985'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/442538920056180985'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/05/what-evildoers-do-if-web-gets-censored.html' title='What evildoers do, if the web gets censored'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-7016979672581161960</id><published>2009-05-10T02:42:00.003+02:00</published><updated>2009-05-10T03:03:42.448+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Psychedelic visualization programming howto</title><content type='html'>A 3 day project was to enhance gmerlins &lt;a href="http://gmerlin.cvs.sourceforge.net/viewvc/gmerlin/gmerlin/plugins/visualization/vis_scope.c?view=markup"&gt;default visualization&lt;/a&gt; (the one which comes with the gmerlin tarball) a bit. Here is a description, how this visual work. There are lots of other visuals out there, which are based on the same algorithm.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;General alrorithm&lt;/b&gt;&lt;br /&gt;It's actually pretty simple: Each frame is the last frame filtered a bit and with something new drawn onto it. This repeats over and over.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Drawing something&lt;/b&gt;&lt;br /&gt;There are 2 absolutely trivial things to draw, which are directly generated by the audio signal:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Stereo waveform: Looks pretty much like a 2 beam oscilloscope&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Vectorscope: One channel is the x-coordinate, the other one is the y-coordinate. The whole thing is then rotated by 45 degrees counterclockwise. It only sucks for mono signals, because it will display a pretty boring vertical line then.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;The vectorscope is drawn as lines and as dots. This makes a total of 3 foreground patterns in 7 colors.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Filtering&lt;/b&gt;&lt;br /&gt;There are 3 types of filters, which are always applied to the last frame: An &lt;a href="http://hirntier.blogspot.com/2008/09/image-transformation.html"&gt;image transformation&lt;/a&gt;, a &lt;a href="http://hirntier.blogspot.com/2008/09/fun-with-colormatrix.html"&gt;colormatrix&lt;/a&gt; and a 3-tap blur filter. The blur filter is needed to wipe out artifacts when doing the image transform repeatedly on the same image.&lt;br /&gt;&lt;br /&gt;The image transform uses the (usually bad) linear interpolation, which means that the recursive process also blurs the image. There are 8 transforms: Rotate left, rotate right, zoom in, zoom out, ripple, lens, sine and cosine.&lt;br /&gt;&lt;br /&gt;Then there are 6 colormatrices. Each of these fades 1 or 2 of the RGB components to zero. Together with the foreground colors (which always have all 3 components nonzero) we get a large variation of the colors when they are recursively filtered.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Background colors&lt;/b&gt;&lt;br /&gt;The image transformation sometimes makes areas in the current frame, which are outside of the last frame. These areas are filled with one of 6 background colors. Background colors are dark (all components &amp;lt 0.5) to increase the contrast against the foreground.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Beat sensitivity&lt;/b&gt;&lt;br /&gt;Then I wrote the most simple beat detection: For each audio frame, compute the sound energy (i.e. calculate the sum of the squared samples for each channel). If the energy is twice as large as the time averaged energy, we have a beat. The time averaged energy if calculated by doing a simple RC low-pass filtering of the energies for each frame:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;avg_energy = avg_energy * 0.95 + new_energy * 0.05;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Variations&lt;/b&gt;&lt;br /&gt;The following things can be changed on each beat (and a random decision with defined probability):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Foreground pattern (3)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Background color (5)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Transform (8)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Colormatrix (6)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;This makes 5040 principal variations.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Result&lt;/b&gt;&lt;br /&gt;I hacked a small tool (&lt;code&gt;gmerlin_visualize&lt;/code&gt;), which takes an audio file and puts it into a video file together with the visualization. The result is here:&lt;br /&gt;&lt;br /&gt;&lt;object height="305" width="400"&gt;&lt;param name="allowfullscreen" value="true"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=4562515&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=4562515&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" height="305" width="400"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&lt;a href="http://vimeo.com/4562515"&gt;Simple music visualization&lt;/a&gt; from &lt;a href="http://vimeo.com/gmerlin"&gt;gmerlin&lt;/a&gt; on &lt;a href="http://vimeo.com/"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;As you can see, the video compression on vimeo isn't really suited for that kind of footage. But that's not vimeos fault. It's a general problem that video compression algorithms are optimized  for natural images rather than computer generated images.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Possible improvements&lt;/b&gt;&lt;br /&gt;The variety can easily be extended by adding new image transformations and new foreground patterns. Also, some contrast between foreground and background is sometimes a bit weak. A more careful selection of the colors and colormatrices would bring less variety but a nicer overall appearance.&lt;br /&gt;&lt;br /&gt;Feel free to make a patch :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-7016979672581161960?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/7016979672581161960/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=7016979672581161960' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7016979672581161960'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7016979672581161960'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/05/psychedelic-visualization-programming.html' title='Psychedelic visualization programming howto'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-7120967772822565968</id><published>2009-04-25T19:23:00.004+02:00</published><updated>2009-04-25T19:44:22.169+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='travels'/><category scheme='http://www.blogger.com/atom/ns#' term='computer'/><title type='text'>Lessons from LAC</title><content type='html'>I was at the &lt;a href="http://lac.linuxaudio.org/"&gt;Linux Audio Conference&lt;/a&gt; in Parma/Italy. The weather sucked most of the time, but everything else was great. These are the most important things I learned:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Jack rules&lt;/b&gt;&lt;br /&gt;Of course I already heard about the &lt;a href="http://jackaudio.org/"&gt;Jack&lt;/a&gt; sound system before. But since I never had a linux distribution, which installs jack by default, I didn't pay much attention to it.&lt;br /&gt;&lt;p&gt;If you go to the LAC, you start to think that there is hardly any audio architecture besides jack. After looking a bit into Jack (and writing &lt;a href="http://gmerlin.cvs.sourceforge.net/viewvc/gmerlin/gmerlin/plugins/jack/"&gt;gmerlin plugins&lt;/a&gt; for Jack audio I/O) I learned, that it's actually pretty cool. I like especially the way, you can connect arbitrary jack applications together while they are running. This is a nice feature for the &lt;a href="http://gmerlin.sourceforge.net/visualizer.html"&gt;gmerlin-visualizer&lt;/a&gt; since it relies on grabbing the audio stream from another application.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ambisonics rocks&lt;/b&gt;&lt;br /&gt;I also heard about Ambisonics before but mostly ignored it. If you read the &lt;a href="http://en.wikipedia.org/wiki/Ambisonics"&gt;Wikipedia article&lt;/a&gt;, you get the impression, that ambisonics is a quite exotic recording/playback technique, which is used only by a small group of enthusiasts.&lt;/p&gt;&lt;p&gt;If you go to LAC, you get the impression that Ambisonics is &lt;b&gt;the&lt;/b&gt; standard and things like stereo or 5.1 are quite exotic recording/playback techniques, which are used only by a small group of enthusiasts.&lt;br /&gt;&lt;/p&gt;&lt;b&gt;Audio vs. video development&lt;/b&gt;&lt;br /&gt;I told the audio developers that they don't know how lucky they are. They agreed on an audio capture/playback architecture (jack), they all use the same internal format (floating point, not interleaved) for processing and they manage to make a conference each year.&lt;br /&gt;&lt;br /&gt;In the video area, things are much worse. Capture and playback APIs exist, but they are far too many. Some people do internal processing in RGB, others prefer Y'CbCr. Converting to a common internal format is not feasible because video has much higher data rates than audio and conversion overhead must always be minimized. Also the video developer community is very fragmented, centered around different frameworks (each one being &lt;b&gt;the&lt;/b&gt; standard). Cross project communication is sparse. The only common code is &lt;a href="http://www.ffmpeg.org/"&gt;ffmpeg&lt;/a&gt; for video en-/decoding, but mostly because nobody wants to reimplement such a beast.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Gmerlin development&lt;/b&gt;&lt;br /&gt;I talked a lot with Richard Spindler from the &lt;a href="http://openmovieeditor.org/"&gt;openmovieeditor&lt;/a&gt;, which makes heavy use of gmerlin libraries. We agreed that our collaboration works quite well because the development activities of gmerlin and openmovieeditor are nicely decoupled. Let's proceed like we already did.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-7120967772822565968?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/7120967772822565968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=7120967772822565968' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7120967772822565968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7120967772822565968'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/04/lessons-from-lac.html' title='Lessons from LAC'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-7771399380955729926</id><published>2009-03-15T22:34:00.005+01:00</published><updated>2009-03-16T10:10:01.189+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Dirac video</title><content type='html'>Libquicktime and gmerlin-avdecoder now support &lt;a href="http://diracvideo.org/about_dirac"&gt;Dirac&lt;/a&gt; in quicktime. En- and decoding is done with the &lt;a href="http://diracvideo.org/download/schroedinger/"&gt;libschrödinger&lt;/a&gt; library. Having already implemented support for lots of other video codecs I noticed some things, both positive and negative.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Positive&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Very precise specification of the uncompressed video format. Interlacing (including field order) is stored in the stream as well as singal ranges (video range or full range). This brings direct support for lots of colormodels.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Support for &gt; 8 bit encoding. This is really a rare feature. While ffmpeg always sticks with 8 bit even for codecs with 10 bit or 12 bit modes, the libschrödinger API has higher precision options. Not sure if these modes are really supported internally by now.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It seems to aim for scalability from low-end internet downloads to intra-only modes for video editing applications. A lossless mode is also there. Whether it performs equally well for all usage scenarios is yet to be found out.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It pretends to be patent free. But since the patent jungle is so dense, it is almost impossible to prove this.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It has the &lt;a href="http://www.bbc.co.uk"&gt;BBC&lt;/a&gt; behind it, which hopefully means serious development, funding and a chance for a wide deployment.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Negative&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Sequence end code in an own sample&lt;/i&gt;&lt;br /&gt;In the &lt;a href="http://www.diracvideo.org/download/mapping-specs/dirac-mapping-isom-latest.pdf"&gt;quicktime mapping specification&lt;/a&gt; it is required, that the sequence end code (a 13 byte string telling that the stream ends here) must be in an own sample. This is a mess, since for &lt;i&gt;all&lt;/i&gt; Quicktime codecs I know (even the most disgusting ones) 1 sample always corresponds to 1 frame. Having a sample, which is no frame, screws up the timing because there is a "duration" associated with the sequence-end-sample, which makes the total stream duration seem larger than is actually is. Also, a frame accurate demuxer will expect one frame more than the file actually has. For both libquicktime and gmerlin-avdecoder I wrote workarounds for this (they simply discard the last packet).&lt;br /&gt;&lt;br /&gt;If I had written the mapping spec, I would require the sequence end code to be appended to the last frame (in the same sample). In addition it can be made optional since it's not really needed in quicktime.&lt;br /&gt;&lt;br /&gt;If libquicktime encodes dirac files, everything is done according to the spec. Conformance to the spec is more important than my personal opinion about it :)&lt;br /&gt;&lt;br /&gt;&lt;i&gt;No ctts atom required&lt;/i&gt;&lt;br /&gt;Quicktime timestamps (as given by the stts atom) are &lt;i&gt;decoding&lt;/i&gt; timestamps. For all low-delay streams (i.e. streams without B-frames), these are equal to the presentation timestamps. For H.264 and MPEG4 ASP streams with B-frames, the ctts atom specifies the difference between PTS and DTS for each frame and lets the demuxer calculate correct presentation timestamps without touching the video data. If the ctts atom is missing, such quicktime files become as disgusting as AVIs with B-frames. Unfortunately the ctts atom isn't required by the mapping spec, which means we'll see such files in the wild.&lt;br /&gt;&lt;br /&gt;The good news is, that the ctts atom isn't mentioned at all in the spec. From this I conclude that it is not forbidden either. Therefore, libquicktime always writes a ctts atom if the stream has B-frames.&lt;br /&gt;&lt;br /&gt;On the decoding side (in gmerlin-avdecoder), the quicktime demuxer checks if a dirac stream has a ctts atom. If yes, it is used and everything is fine. If not (i.e. if the file wasn't written by libquicktime), a parser is fired up and an index must be built if one wants sample accuracy. The good news is that the parser is pretty simple and the same thing is needed anyway for decoding dirac in MPEG transport streams.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Other news&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;gmerlin-avdecoder also supports Dirac in &lt;a href="http://diracvideo.org/download/mapping-specs/dirac-mapping-ogg-latest.pdf"&gt;Ogg&lt;/a&gt; and &lt;a href="http://diracvideo.org/download/mapping-specs/dirac-mapping-mpeg2ts-latest.pdf"&gt;MPEG-2 transport streams&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-7771399380955729926?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/7771399380955729926/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=7771399380955729926' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7771399380955729926'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7771399380955729926'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/03/dirac-video.html' title='Dirac video'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-4621902663594584432</id><published>2009-02-28T03:18:00.004+01:00</published><updated>2009-02-28T14:36:27.860+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Parallelizing video routines</title><content type='html'>&lt;b&gt;Basics&lt;/b&gt;&lt;br /&gt;One project shortly after the last gmerlin release was to push the CPU usage beyond 50 % on my dual core machine. There is lots of talk about parallelization of commonly used video routines, but only little actual code.&lt;br /&gt;&lt;br /&gt;In contrast to other areas (like scientific simulations) video routines are extremely simple to parallelize because most of them fulfill the following 2 conditions:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The outermost loop runs over scanlines of the destination image&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The destination image is (except for a few local variables) the only memory area, where data are &lt;i&gt;written&lt;/i&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;So the first step is to change the conversion functions of the type:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;void conversion_func(void * data, int width, int height)&lt;br /&gt;{&lt;br /&gt;int i, j;&lt;br /&gt;for(i = 0; i &amp;lt; height; i++)&lt;br /&gt;  {&lt;br /&gt;  for(j = 0; j &amp;lt; width; j++)&lt;br /&gt;    {&lt;br /&gt;    /* Do something here */&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;to something like:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;void conversion_func(void * data, int width, int start, int end)&lt;br /&gt;{&lt;br /&gt;int i, j;&lt;br /&gt;for(i = start; i &amp;lt; end; i++)&lt;br /&gt;  {&lt;br /&gt;  for(j = 0; j &amp;lt; width; j++)&lt;br /&gt;    {&lt;br /&gt;    /* Do something here */&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;code&gt;data&lt;/code&gt; argument points to a struct containing everything needed for the conversion like destination frame, source frame(s), lookup tables etc. The height argument was replaced by start and end arguments, which means that the function now processes a &lt;i&gt;slice&lt;/i&gt; (i.e. a number of consecutive scanlines) instead of the whole image. The remaining thing now is to call the conversion function from multiple threads. It is important that the &lt;i&gt;outermost&lt;/i&gt; loop is split to keep the overhead as small as possible.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The API perspective&lt;/b&gt;&lt;br /&gt;Everything was implemented&lt;br /&gt;&lt;ul&gt;&lt;li&gt;With a minimal public API&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Backwards compatible (i.e. if you ignore the API extensions things still work)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Completely independent of the actual thread implementation (i.e. no -lpthread is needed for gavl)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;The whole magic can be boiled down to:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;/* Slice process function, which is passed by gavl to the application */&lt;br /&gt;typedef void (*gavl_video_process_func)(void * data, int start, int end);&lt;br /&gt;&lt;br /&gt;/* Routine, which passes the actual work to the worker thread (which is managed by the application) */&lt;br /&gt;typedef void (*gavl_video_run_func)(gavl_video_process_func func,&lt;br /&gt;                               void * gavl_data,&lt;br /&gt;                               int start, int end,&lt;br /&gt;                               void * client_data, int thread);&lt;br /&gt;&lt;br /&gt;/* Synchronization routine, which waits for the worker thread to finish */&lt;br /&gt;typedef void (*gavl_video_stop_func)(void * client_data, int thread);&lt;br /&gt;&lt;br /&gt;/* Set the maximum number of available worker threads (gavl might not need all of them&lt;br /&gt;if you have more CPUs than scanlines) */&lt;br /&gt;void gavl_video_options_set_num_threads(gavl_video_options_t * opt, int n);&lt;br /&gt;&lt;br /&gt;/* Pass the run function to gavl */&lt;br /&gt;gavl_video_options_set_run_func(gavl_video_options_t * opt,&lt;br /&gt;                                gavl_video_run_func func,&lt;br /&gt;                                void * client_data);&lt;br /&gt;&lt;br /&gt;/* Pass the stop function to gavl */&lt;br /&gt;void gavl_video_options_set_stop_func(gavl_video_options_t * opt,&lt;br /&gt;                                 gavl_video_stop_func func,&lt;br /&gt;                                 void * client_data);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Like always, the gavl_video_options_set_* routines have corresponding&lt;br /&gt;gavl_video_options_get_* routines. These can be used for using the same&lt;br /&gt;multithreading mechanism outside gavl (e.g. in a video filter).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The application perspective&lt;/b&gt;&lt;br /&gt;As noted above there is no pthread specific code inside gavl. Everything is passed via callbacks. libgmerlin has a pthread based thread pool, which does exactly what gavl needs.&lt;br /&gt;&lt;br /&gt;The thread pool is just an array of context structures for each thread:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;typedef struct&lt;br /&gt;{&lt;br /&gt;/* Thread handle */&lt;br /&gt;pthread_t t;&lt;br /&gt;&lt;br /&gt;/* gavl -&amp;gt; thread: Do something */&lt;br /&gt;sem_t run_sem;&lt;br /&gt;&lt;br /&gt;/* thread -&amp;gt; gavl: I'm done */&lt;br /&gt;sem_t done_sem;&lt;br /&gt;&lt;br /&gt;/* Mechanism to make the fuction finish */&lt;br /&gt;pthread_mutex_t stop_mutex;&lt;br /&gt;int do_stop;&lt;br /&gt;&lt;br /&gt;/* Passed from gavl */&lt;br /&gt;void (*func)(void*, int, int);&lt;br /&gt;void * data;&lt;br /&gt;int start;&lt;br /&gt;int end;&lt;br /&gt;} thread_t;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;The main loop (passed to pthread_create) for each worker thread looks like:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;static void * thread_func(void * data)&lt;br /&gt;{&lt;br /&gt;thread_t * t = data;&lt;br /&gt;int do_stop;&lt;br /&gt;while(1)&lt;br /&gt;  {&lt;br /&gt;  sem_wait(&amp;amp;t-&amp;gt;run_sem);&lt;br /&gt;&lt;br /&gt;  pthread_mutex_lock(&amp;amp;t-&amp;gt;stop_mutex);&lt;br /&gt;  do_stop = t-&amp;gt;do_stop;&lt;br /&gt;  pthread_mutex_unlock(&amp;amp;t-&amp;gt;stop_mutex);&lt;br /&gt;&lt;br /&gt;  if(do_stop)&lt;br /&gt;    break;&lt;br /&gt;  t-&amp;gt;func(t-&amp;gt;data, t-&amp;gt;start, t-&amp;gt;len);&lt;br /&gt;  sem_post(&amp;amp;t-&amp;gt;done_sem);&lt;br /&gt;  }&lt;br /&gt;return NULL;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;The worker threads are launched at program start and run all the time. As long as nothing is to do, they wait for the &lt;code&gt;run_sem&lt;/code&gt; semaphore (using zero CPU). Launching new threads for every little piece of work would have a much higher overhead.&lt;br /&gt;&lt;br /&gt;Passing work to a worker thread happens with the following gavl_video_run_func:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;void bg_thread_pool_run(void (*func)(void*,int start, int len),&lt;br /&gt;                     void * gavl_data,&lt;br /&gt;                     int start, int end,&lt;br /&gt;                     void * client_data, int thread)&lt;br /&gt;{&lt;br /&gt;bg_thread_pool_t * p     = client_data;&lt;br /&gt;p-&amp;gt;threads[thread].func  = func;&lt;br /&gt;p-&amp;gt;threads[thread].data  = gavl_data;&lt;br /&gt;p-&amp;gt;threads[thread].start = start;&lt;br /&gt;p-&amp;gt;threads[thread].end   = end;&lt;br /&gt;&lt;br /&gt;sem_post(&amp;amp;p-&amp;gt;threads[thread].run_sem);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Synchronizing (waiting for the work to finish) happens with the following gavl_video_stop_func:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;void bg_thread_pool_stop(void * client_data, int thread)&lt;br /&gt;{&lt;br /&gt;bg_thread_pool_t * p     = client_data;&lt;br /&gt;sem_wait(&amp;amp;p-&amp;gt;threads[thread].done_sem);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;The whole thread pool implementation can be found in the gmerlin source tree in &lt;code&gt;lib/threadpool.c&lt;/code&gt; (128 lines including copyright header).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Implementations&lt;/b&gt;&lt;br /&gt;Parallelization was implemented for&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The video scaler&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The &lt;a href="http://hirntier.blogspot.com/2008/09/fun-with-colormatrix.html"&gt;colormatrix&lt;/a&gt; filters&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The &lt;a href="http://hirntier.blogspot.com/2008/09/image-transformation.html"&gt;Image transformation&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The &lt;a href="http://svn.mplayerhq.hu/mplayer/trunk/libmpcodecs/vf_yadif.c?view=markup"&gt;Yadif&lt;/a&gt; port in the deinterlacing filter&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Benchmarks&lt;/b&gt;&lt;br /&gt;For benchmarking one needs a scenario where the parallelized processing routine(s) need the lions share of the total CPU usage. I decided to make a (completely nonsensical) gaussian blur with a radius of 50 over 1000 frames of a 720x576 PAL sequence. All code was compiled with default (i.e. optimized) options. Enabling 2 threads descreased the processing time from 81.31 sec to 43.35 sec.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Credits&lt;/b&gt;&lt;br /&gt;Finally found &lt;a href="http://formatmysourcecode.blogspot.com/"&gt;this&lt;/a&gt; page for making source snippets in blogger suck less.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-4621902663594584432?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/4621902663594584432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=4621902663594584432' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4621902663594584432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4621902663594584432'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/02/parallelizing-video-routines.html' title='Parallelizing video routines'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-5350213370291609642</id><published>2009-02-27T00:54:00.004+01:00</published><updated>2009-02-27T01:10:31.492+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Elementary stream parsing</title><content type='html'>See the image below for an updated block-schematics of gmerlin-avdecoder as addition to my last &lt;a href="http://hirntier.blogspot.com/2008/12/how-gmerlin-avdecoder-works.html"&gt;blog entry&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/SacsHChEXjI/AAAAAAAAAF0/u3IVPkDbUfc/s1600-h/avdec_parser.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 100px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/SacsHChEXjI/AAAAAAAAAF0/u3IVPkDbUfc/s400/avdec_parser.png" alt="" id="BLOGGER_PHOTO_ID_5307259185325694514" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;You see, what's new here: A parser, which converts "bad" packets into "good" packets. Now what does that exactly mean? A good (or well formed) packet has the following properties:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; It always has a correct timestamp (the presentation time at least)&lt;br /&gt;&lt;/li&gt;&lt;li&gt; It has a flag to determine if the packet is a keyframe&lt;br /&gt;&lt;/li&gt;&lt;li&gt; It has a valid duration. This is necessary for frame accurate repositioning of the stream after seeking. At least if you want to support variable framerates.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Good packets (e.g. from quicktime files) can directly be passed to the decoder, and it's possible to build an index from them. Unfortunately some formats (most notably MPEG program- and transport streams), don't have such nice packets. You neither know where a frame starts, nor whether it is a keyframe. Large frames can be split across many packets, some packets can contain several small frames. Also timestamps are not available for each frame. To make things worse, these formats are very widely used. You find them in .mpg files, on DVDs, (S)VCDs, BluRay disks and in DVB broadcasts. Also all newer formats for consumer cameras (HDV and AVCHD) use MPEG-2 transport streams. Since they are important for video editing applications, so sample accurate access is a main goal here.&lt;br /&gt;&lt;br /&gt;The first solutions for this were realized inside the decoders. libmpeg2 is very tolerant with regard to the frame alignment and ffmpeg has an AVParser, which splits a continuous stream into frames. Additional routines were written for building an index.&lt;br /&gt;&lt;br /&gt;It was predictable that this would not be the ultimate solution. The decoders got very complicated and building the index was not possible without firing up an ffmpeg decoder (because the AVParser doesn't tell about keyframes) so index building was very slow.&lt;br /&gt;&lt;br /&gt;So I spent some time to write a parsers for elementary A/V streams, which parse the streams to get all necessary information for creating well formed packets.&lt;br /&gt;&lt;br /&gt;After that worked, I could be sure, that every codec always gets well-formed packets. What followed then, was by far the biggest cleanup in the history of gmerlin-avdecoder. Many things could be simplified, lots of cruft got kicked out, duplicate code got moved to central locations.&lt;br /&gt;&lt;br /&gt;New features are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Decoding of (and sample accurate seeking within) elementary H.264 streams&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Sample accurate access for elementary MPEG-1/2 streams&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Sample accurate access for MPEG program streams with DTS- and LPCM audio&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Faster seeking because non-reference frames are skipped while approaching the new stream position&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Much cleaner and simpler architecture.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;The cleaner architecture doesn't necessarily mean less bugs, but they are easier to fix now :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-5350213370291609642?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/5350213370291609642/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=5350213370291609642' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/5350213370291609642'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/5350213370291609642'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2009/02/elementary-stream-parsing.html' title='Elementary stream parsing'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_KslCmtD4qm8/SacsHChEXjI/AAAAAAAAAF0/u3IVPkDbUfc/s72-c/avdec_parser.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-869583242413443376</id><published>2008-12-11T20:11:00.005+01:00</published><updated>2008-12-11T22:26:47.601+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>How gmerlin-avdecoder works</title><content type='html'>If you study multimedia decoding software like xine, ffmpeg or MPlayer you find that they all work surprisingly similar. Gmerlin-avdecoder is no exception here. The important components are shown in the image below:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/SUFnM8nyj3I/AAAAAAAAAFI/efHAHYtEu8M/s1600-h/avdec.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 98px;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/SUFnM8nyj3I/AAAAAAAAAFI/efHAHYtEu8M/s400/avdec.png" alt="" id="BLOGGER_PHOTO_ID_5278613710384041842" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Input&lt;/b&gt;&lt;br /&gt;The input module obtains the data. Examples for data sources are regular files, DVDs or DVB- or network streams. Usually the data is delivered in raw bytes. For DVDs and VCDs however, read and seek operations are sector based. Since both formats require that each sector must start with a syncpoint (an MPEG pack header) having sector based data in the demultiplexer speeds up several things.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Demultiplexer&lt;/b&gt;&lt;br /&gt;This is, where the compressed frames are extracted from the container. During initialization, the demultiplexer creates the track table (&lt;code&gt;bgav_track_table_t&lt;/code&gt;). This contains the tracks (in most cases just one). Each track contains the streams for audio, video and subtitles. For most containers the demultiplexer already knows the audio and video formats. For others, the codec must detect it. This means you should never trust the formats before you called &lt;code&gt;bgav_start()&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;In some cases (DVD, VCD, DVB) the input already knows the complete track layout and which demultiplexer to use. The initialization of the demultiplexer can then skip the stream detection. In the general case the demultiplexer is selected according to the file content (i.e. the first few bytes). Some formats (MPEG, mp3) can have garbage before the first detection pattern, so we must repeatedly skip bytes before checking for one of these.&lt;br /&gt;&lt;br /&gt;The demultiplexer has a routine, which reads the next packet from the input. Depending on the format, this involves decoding a packet header and extracting the compressed data, which can be handled by the codec later on. Some formats (rm, asf, MPEG-2 transport streams, Ogg) use 2-layer multiplexing. There are top-level packets which contain subpackets.&lt;br /&gt;&lt;br /&gt;If the format is well designed, we also know the timestamps and duration of the packet and if the packet contains a keyframe. Not all formats are well designed though (or encoders are buggy), then we must do a lot of black magic to get as much information as possible.&lt;br /&gt;&lt;br /&gt;Most demultiplexers are native implementations. As a fallback for very obscure formats we also support demultiplexing with libavformat.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Buffers&lt;/b&gt;&lt;br /&gt;Gmerlin-avdecoder is strictly pull-based. If a video codec requests a packet, but the next packet in the stream belongs to an audio stream, it must be stored for later usage. For this, we have buffers, which are just chained lists of packets. They can grow dynamically. This approach makes the decoding process mostly insensitive to badly interleaved streams.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Interleaved vs. noninterleaved&lt;/b&gt;&lt;br /&gt;The demultiplexing method described above reads the file strictly sequentially. This has the advantage that we never seek in the stream so we can do this for non-seekable sources.&lt;br /&gt;&lt;br /&gt;Some files (more than you might think) are however completely non-interleaved, e.g. the audio packets follow all video packets. These always have a global index though. In this case, if a video packet is requested, the demultiplexer seeks to the packet start and reads the packet. This mode, which is also used in sample accurate mode, only works for seekable sources.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Codecs&lt;/b&gt;&lt;br /&gt;These convert packets to A/V frames. In most cases, one packet equals one frame. In some cases (mostly for MPEG streams), the codec must first assemble frames from packets or split packets containing multiple frames. The codec outputs the gavl frames, which are handled by the application.&lt;br /&gt;&lt;br /&gt;Codecs are selected according to fourccs. For formats, which don't have fourccs, we either invent them or we use fourccs from AVI or Quicktime.&lt;br /&gt;&lt;br /&gt;Video codecs must care about timestamps. For MPEG streams the timestamps at multiplex level (with a 90 kHz clock) must be converted to the ones according to the video framerate. Audio codecs must do buffering because the the application can decide how many samples to read at once.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Text subtitles&lt;/b&gt;&lt;br /&gt;There are no codecs for text subtitles. Each packet contains the string (converted to UTF-8 by the demultiplexer), the presentation timestamp and the duration.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Reading a Video frame&lt;/b&gt;&lt;br /&gt;If an application calls &lt;code&gt;bgav_read_video&lt;/code&gt;, the following happens:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The core calls the decode function of the codec&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The codec checks if there is already a decoded frame available. This is the case after initialization because some codecs need to decode the first picture to detect the video format&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If no frame is left, the codec decodes one. For this it will most likely need a new packet&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The codec requests a packet. Either the packet buffer already has one, or the demultiplexer must get one&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In streaming mode, the demultiplexer gets the next packet from the input and puts it into the packet buffer of the stream to which it belongs. If the stream is not used, no packet is produced and it's data is skipped. This is repeated until the end is reached or we found a packet for the video stream. If the end is reached, the demultiplexer signals EOF for the whole track.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In non-streaming mode the demultiplexer knows, which stream requested the packet. It seeks to the position of the next packet and reads it. EOF is signaled per stream.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Index building&lt;/b&gt;&lt;br /&gt;Building file indices for sample accurate access can happen in different ways depending on the container format. In the end, we need byte positions in the file, the associated timestamps (in output timescale) and keyframe flags. The following modes are supported:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;MPEG mode: The codec must build the index. This involves parsing the frames (only needed parts) to extract timing information with sample accuracy. Codecs supporting this mode are libmpeg2, libavcodec, libmad, liba52 and faad2.&lt;/li&gt;&lt;li&gt;Simple mode: The demultiplexer knows about the output timescale, gets precise timestamps and one packet equals one frame. Then no codec is needed for building the index.&lt;/li&gt;&lt;li&gt;Mix of the above. E.g. in flv timestamps are always in millseconds. This is precise for video streams. For audio streams (mostly mp3), we need MPEG mode.&lt;/li&gt;&lt;/ul&gt;B-frames are ommitted in the index. That's because noone will use them for a seekpoint anyway and the PTS get strictly monotone. This lets us do a fast binary search in the index, but the demultiplexer must be prepared for packets not contained in the index.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-869583242413443376?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/869583242413443376/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=869583242413443376' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/869583242413443376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/869583242413443376'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/12/how-gmerlin-avdecoder-works.html' title='How gmerlin-avdecoder works'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_KslCmtD4qm8/SUFnM8nyj3I/AAAAAAAAAFI/efHAHYtEu8M/s72-c/avdec.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-5724274958254516807</id><published>2008-12-05T22:27:00.014+01:00</published><updated>2008-12-06T00:26:22.331+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='computer'/><title type='text'>Downscaling algorithms</title><content type='html'>&lt;b&gt;The theory&lt;/b&gt;&lt;br /&gt;Downscaling images is a commonly needed operation, e.g. if HD videos or photos from megapixel cameras are converterted for burning on DVD or uploading to the web. Mathematically, image scaling is exactly the same as audio samplerate conversion (only in 2D, but it can be decomposed into two subsequent 1D operations). All these have in common that samples of the destination must be calculated from the source samples, which are given on a (1D- or 2D-) grid with a different spacing (temporally or spatially). The reciprocal of the grid spacings are the sample frequencies.&lt;br /&gt;&lt;br /&gt;In the general case (i.e. if the resample ratio is arbitrary) one will end up with interpolating the destination samples from the source samples. The interpolation method (nearest neighbor, linear, cubic...) can be configured in better applications.&lt;br /&gt;&lt;br /&gt;One might think we are done here, but unfortunately we aren't. Interpolation is the &lt;i&gt;second&lt;/i&gt; step of downscaling. Before that, we must make sure that the &lt;a href="http://en.wikipedia.org/wiki/Sampling_theorem"&gt;sampling theorem&lt;/a&gt; is not violated. The sampling theorem requires the original signal to be band-limited with a cutoff frequency of half the sample frequency. This cutoff frequency is also called the &lt;a href="http://en.wikipedia.org/wiki/Nyquist_frequency"&gt;Nyquist frequency&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So if we upscale an image (or resample an audio signal to a &lt;i&gt;higher&lt;/i&gt; sample frequency), we can assume that the original was already band-limited with half the &lt;i&gt;lower&lt;/i&gt; lower sample frequency so we have no problem. If we downsample, we must first apply a digital low-pass filter to the image. Low-pass filtering of images is the same as &lt;i&gt;blurring&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The imagemagick solution&lt;/b&gt;&lt;br /&gt;What pointed me to this subject in the first place was &lt;a href="http://sourceforge.net/forum/forum.php?thread_id=2146210&amp;amp;forum_id=62254"&gt;this&lt;/a&gt; post in the gmerlin-help forum (I knew about sampling theory before, I simply forgot about it when programming the scaler). The suggestion, which is implemented in ImageMagick, was to simply widen the filter kernel by the inverse scaling ratio. For linear downscaling to 1/2 size this would mean to do an interpolation involving 4 source pixels instead of 2. The implementation extremely simple, it's just one additional variable for calculating the number and values of filter coefficients. This blurs the image because it does some kind of averaging involving more source pixels. Also the amount of blurring is proportional to the inverse scale factor, which is correct. The results actually look ok.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The gavl solution&lt;/b&gt;&lt;br /&gt;I thought what's the correct way to do this. As explained above, we must blur the image with a defined cutoff frequency and then interpolate it. For the blur filter, I decided to use a Gaussian low-pass because it seems the best suited for this.&lt;br /&gt;&lt;br /&gt;The naive implementation is to blur the source image into a temporary video frame and then interpolate into the destination frame. It can however be done much faster and without temporary frame, because the 2 operations are both &lt;a href="http://en.wikipedia.org/wiki/Convolution"&gt;convolutions&lt;/a&gt;. And the convolution has the nice property, that it's &lt;a href="http://en.wikipedia.org/wiki/Associativity"&gt;associative&lt;/a&gt;. This means, that we can convolve the blur coefficients with the interpolation coefficients resulting in the filter coefficients for the combined operation. These are then used on the images.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The difference&lt;/b&gt;&lt;br /&gt;The 2 solutions have a lot in common. Both run in 1 pass and blur the image according to the inverse scaling ratio. The difference is, that the imagemagick method simply widens the filter kernel by a factor while gavl widens the filter by colvolving with a low-pass.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Examples&lt;/b&gt;&lt;br /&gt;During my research I found &lt;a href="http://www.xs4all.nl/%7Ebvdwolf/main/foto/down_sample/down_sample.htm"&gt;this&lt;/a&gt; page. I downloaded the sample image (1000x1000) and downscaled it to 200x200 with different methods.&lt;br /&gt;&lt;br /&gt;First the scary ones:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/STmfTAApAII/AAAAAAAAAD4/uOdLVFTtCu0/s1600-h/scale_gl.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 200px; height: 200px;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/STmfTAApAII/AAAAAAAAAD4/uOdLVFTtCu0/s400/scale_gl.png" alt="" id="BLOGGER_PHOTO_ID_5276423587210264706" border="0" /&gt;&lt;/a&gt;OpenGL (Scale mode linear, GeForce 8500 GT, binary drivers)&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_KslCmtD4qm8/STmgFA8glsI/AAAAAAAAAEA/yoAFlQ57SCQ/s1600-h/scale_xv.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 200px; height: 200px;" src="http://4.bp.blogspot.com/_KslCmtD4qm8/STmgFA8glsI/AAAAAAAAAEA/yoAFlQ57SCQ/s400/scale_xv.png" alt="" id="BLOGGER_PHOTO_ID_5276424446454830786" border="0" /&gt;&lt;/a&gt;XVideo (GeForce 8500 GT, binary drivers)&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/STmidblqBUI/AAAAAAAAAEI/zEVwBf80X_0/s1600-h/scale_firefox.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 200px; height: 200px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/STmidblqBUI/AAAAAAAAAEI/zEVwBf80X_0/s400/scale_firefox.png" alt="" id="BLOGGER_PHOTO_ID_5276427064946853186" border="0" /&gt;&lt;/a&gt;Firefox 3.0.4 (that's why I never let the browser scale when writing html)&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/STmmNof9X-I/AAAAAAAAAEY/S9Qs7GcOpkM/s1600-h/scale_gimp_linear_indexed.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 200px; height: 200px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/STmmNof9X-I/AAAAAAAAAEY/S9Qs7GcOpkM/s400/scale_gimp_linear_indexed.png" alt="" id="BLOGGER_PHOTO_ID_5276431191581220834" border="0" /&gt;&lt;/a&gt;Gimp (linear, indexed mode)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/STmpYVYmjYI/AAAAAAAAAEo/AYif8uOtcKo/s1600-h/scale_gavl_linear_none.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 200px; height: 200px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/STmpYVYmjYI/AAAAAAAAAEo/AYif8uOtcKo/s400/scale_gavl_linear_none.png" alt="" id="BLOGGER_PHOTO_ID_5276434673963535746" border="0" /&gt;&lt;/a&gt;gavl (&lt;code&gt;&lt;/code&gt;downscale filter: &lt;code&gt;GAVL_DOWNSCALE_FILTER_NONE&lt;/code&gt;)&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Now it gets better:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/STmnGjUU1JI/AAAAAAAAAEg/LEzgzwz_02I/s1600-h/scale_gimp_linear_gray.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 200px; height: 200px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/STmnGjUU1JI/AAAAAAAAAEg/LEzgzwz_02I/s400/scale_gimp_linear_gray.png" alt="" id="BLOGGER_PHOTO_ID_5276432169442792594" border="0" /&gt;&lt;/a&gt;Gimp (linear, grayscale mode)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_KslCmtD4qm8/STm1zmBiN_I/AAAAAAAAAFA/AeRd0t4jt7g/s1600-h/scale_mplayer.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 200px; height: 200px;" src="http://4.bp.blogspot.com/_KslCmtD4qm8/STm1zmBiN_I/AAAAAAAAAFA/AeRd0t4jt7g/s400/scale_mplayer.png" alt="" id="BLOGGER_PHOTO_ID_5276448336426186738" border="0" /&gt;&lt;/a&gt;&lt;code&gt;mplayer -vf scale=200:200 scale.mov&lt;/code&gt;&lt;br /&gt;The movie was made with qtrechunk from the png file.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_KslCmtD4qm8/STmrPvWSN8I/AAAAAAAAAEw/ck2FUV2P57I/s1600-h/scale_gavl_linear_widen.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 200px; height: 200px;" src="http://4.bp.blogspot.com/_KslCmtD4qm8/STmrPvWSN8I/AAAAAAAAAEw/ck2FUV2P57I/s400/scale_gavl_linear_widen.png" alt="" id="BLOGGER_PHOTO_ID_5276436725337569218" border="0" /&gt;&lt;/a&gt;gavl with imagemagick method (&lt;code&gt;&lt;/code&gt;downscale filter: &lt;code&gt;GAVL_DOWNSCALE_FILTER_WIDE&lt;/code&gt;)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/STmsOp4cUnI/AAAAAAAAAE4/2hERl-qR-7Q/s1600-h/scale_gavl_linear_gauss.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 200px; height: 200px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/STmsOp4cUnI/AAAAAAAAAE4/2hERl-qR-7Q/s400/scale_gavl_linear_gauss.png" alt="" id="BLOGGER_PHOTO_ID_5276437806201983602" border="0" /&gt;&lt;/a&gt;gavl with gaussian preblur (&lt;code&gt;&lt;/code&gt;downscale filter: &lt;code&gt;GAVL_DOWNSCALE_FILTER_GAUSS&lt;/code&gt;)&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_KslCmtD4qm8/STmkQcqLwbI/AAAAAAAAAEQ/VN5eQzBYhuE/s1600-h/rings.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 400px;" src="http://4.bp.blogspot.com/_KslCmtD4qm8/STmkQcqLwbI/AAAAAAAAAEQ/VN5eQzBYhuE/s400/rings.png" alt="" id="BLOGGER_PHOTO_ID_5276429040919232946" border="0" /&gt;&lt;/a&gt;Blogger thumbnail (400x400). Couldn't resist to upload the original size image to blogger and see what happens. Not bad, but not 200x200.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-5724274958254516807?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/5724274958254516807/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=5724274958254516807' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/5724274958254516807'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/5724274958254516807'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/12/downscaling-algorithms.html' title='Downscaling algorithms'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_KslCmtD4qm8/STmfTAApAII/AAAAAAAAAD4/uOdLVFTtCu0/s72-c/scale_gl.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-4731533693801399219</id><published>2008-11-29T20:58:00.024+01:00</published><updated>2008-12-02T00:38:04.573+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='computer'/><title type='text'>Video4vimeo</title><content type='html'>Everyone uploads videos nowadays. Specialists use &lt;a href="http://www.vimeo.com/"&gt;vimeo&lt;/a&gt; because youtube quality sucks. So the project goal was to create a video file for upload to Vimeo with Gmerlin-transcoder, optimize the whole process and fix all bugs.&lt;br /&gt;&lt;h1&gt;The footage&lt;/h1&gt;I had to make sure that I own the copyright of the example clip and that nobodies privacy is violated. So I decided to make a short video featuring a toilet toy I bought in Tokyo in 2003. A friend of mine went to the shop a few years later, but it was already sold out.&lt;br /&gt;&lt;h1&gt;The equipment&lt;/h1&gt;My camera is a simple mini-DV one. It's only SD but since the gmerlin architecture is nicely scalable, the same encoder settings (except the picture size) should apply for HD as well. I connected the camera via firewire and recorded directly to the PC (no tape involved) with &lt;a href="http://www.kinodv.org/"&gt;Kino&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/STGhsJslaCI/AAAAAAAAACQ/GsdM08J4LEY/s1600-h/toilet_making_of.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 300px;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/STGhsJslaCI/AAAAAAAAACQ/GsdM08J4LEY/s400/toilet_making_of.jpg" alt="" id="BLOGGER_PHOTO_ID_5274174418516797474" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h1&gt;Capture format&lt;/h1&gt;The camera sends DV frames (with encapsulated audio) via firewire to the PC. This format is called &lt;i&gt;raw DV&lt;/i&gt; (extension .dv). The Kino user can choose whether to wrap the DV frames into AVI or Quicktime or export them raw. Since the raw DV format is completely self-contained, it was choosen as input format for Gmerlin-transcoder. Wrapping DV into another container makes only sense for toolchains, which cannot handle raw DV.&lt;br /&gt;&lt;h1&gt;Quality considerations&lt;/h1&gt;My theory is that the crappy quality of many web-video services is partly due to financial considerations of the service providers (crappy files need less space on the server and less bandwidth for transmission), but partly also due to people making mistakes when preparing their videos. Here are some things, which should be kept in mind:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1. You never do the final compression&lt;/span&gt;&lt;br /&gt;In forums you often see people asking: &lt;i&gt;How can I convert to flv for upload on youtube?&lt;/i&gt; The answer is: Don't do it. Even if you do it, it's unlikely that the server will take your video as it is. Many video services are known to use &lt;a href="http://www.ffmpeg.org/"&gt;ffmpeg&lt;/a&gt; for importing the uploaded files, which can read much more than just flv. Install ffmpeg to check if it can read your files.&lt;br /&gt;&lt;br /&gt;Compression parameters should be optimized for invisible artifacts in the file you upload. That's because in the final compression (out of your control) will add more artifacts. And 2nd generation artifacts look even more ugly, the results can be seen on many places in the web.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2. Minimize additional conversions on the server&lt;/span&gt;&lt;br /&gt;If you scale your video to the same size it will have on the server, chances are good that the server won't rescale it. The advantage is that scaling will happen for the raw material, resulting in minimal quality loss. Scaled video looks ugly if the original has compression artifacts, which would be the case if you let the server scale.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3. Don't forget to deinterlace&lt;/span&gt;&lt;br /&gt;Interlaced video compressed in progressive mode looks extraordinarily ugly. Even more disappointing is that many people apparently forget to deinterlace. Even the crappiest deinterlacer is better than nothing.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;4. Minimize artifacts by prefiltering&lt;/span&gt;&lt;br /&gt;If, for whatever reason, artifacts are unavoidable you can minimize them by doing a slight blurring of the source material. Usually this shouldn't be necessary.&lt;br /&gt;&lt;h1&gt;Format conversion&lt;/h1&gt;All video format conversions can be done in a single pass by the &lt;i&gt;Crop &amp;amp; Scale&lt;/i&gt; filter. This gives maximum speed, smallest rounding errors and smallest blurring.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;Deinterlacing&lt;/b&gt;&lt;br /&gt;Sophisticated deinterlacing algorithms are only meaningful if the vertical resolution should be preserved. In our case, where the image is scaled down anyway, it's better to let the scaler deinterlace. Doing scaling and deinterlacing in one step also decreases the overall blurring of the image.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/STK943uTdkI/AAAAAAAAACY/4syoVgrgt6I/s1600-h/deinterlace.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 203px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/STK943uTdkI/AAAAAAAAACY/4syoVgrgt6I/s400/deinterlace.png" alt="" id="BLOGGER_PHOTO_ID_5274486898332759618" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;b&gt;Scaling&lt;/b&gt;&lt;br /&gt;Image size for Vimeo in SD seems to be 504x380. It's the size of their flash widget and also the size of the .flv video. Square pixels are assumed.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/STK-v3o__UI/AAAAAAAAACg/kQGUoFAZ6Ds/s1600-h/size.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 203px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/STK-v3o__UI/AAAAAAAAACg/kQGUoFAZ6Ds/s400/size.png" alt="" id="BLOGGER_PHOTO_ID_5274487843203317058" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Cropping&lt;/b&gt;&lt;br /&gt;The aspect ratio of PAL DV is a bit larger than 4:3. Also 504x380 with square pixels is not exactly 4:3. Experiments have shown, cropping by 10 pixels each on the left and right borders removed black border at the top and bottom. If your source material has a different size, these values will be different as well.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/STK_p0UyT7I/AAAAAAAAACo/LmglzAJGwS0/s1600-h/crop.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 203px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/STK_p0UyT7I/AAAAAAAAACo/LmglzAJGwS0/s400/crop.png" alt="" id="BLOGGER_PHOTO_ID_5274488838745640882" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;b&gt;Chroma placement&lt;/b&gt;&lt;br /&gt;Chroma placement for PAL DV is different from H.264 (which has the same chroma placement as MPEG-2). Depending on the gavl quality settings, this fact is either ignored or a another video scaler is used for shifting the chroma locations later on. I thought that could be done smarter.&lt;br /&gt;&lt;br /&gt;Since the gavl video scaler can do many things at the same time (it already does deinterlacing, cropping and scaling) it can also do chroma placement correction. For this, I made the chroma output format of the Crop &amp;amp; scale filter configurable. If you set this to the format of the final output, subsequent scaling operations are avoided.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/STLB_Src14I/AAAAAAAAACw/omU3c0smaGA/s1600-h/chroma.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 203px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/STLB_Src14I/AAAAAAAAACw/omU3c0smaGA/s400/chroma.png" alt="" id="BLOGGER_PHOTO_ID_5274491406694274946" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Since ffmpeg doesn't care about chroma placement it's probably unnecessary that we do. On the other hand, our method has zero overhead and does practically no harm.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Audio&lt;/span&gt;&lt;br /&gt;Vimeo wants audio to be sampled at 44,1 kHz, most cameras record in 48 kHz. The following settings take care for that:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/STLoGp0D9-I/AAAAAAAAAC4/q2z41pl2ppM/s1600-h/audio.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 304px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/STLoGp0D9-I/AAAAAAAAAC4/q2z41pl2ppM/s400/audio.png" alt="" id="BLOGGER_PHOTO_ID_5274533314605348834" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;h1&gt;Encoding&lt;/h1&gt;The codecs are H.264 for video and AAC for audio. Not only because they are recommended by vimeo, they give indeed the best results for a given bitrate.&lt;br /&gt;&lt;br /&gt;For some reason, vimeo doesn't accept the AAC streams in Quicktime files created by libquicktime. Apple Quicktime, mplayer and ffmpeg accept them and I found lots of forum posts describing exactly the same problem. So I believe that this is a vimeo problem.&lt;br /&gt;&lt;br /&gt;The solution I found is simple: Use mp4 instead of mov. People think mp4 and mov are indentical, but that's not true. At least in this case it makes a difference. The compressed streams are, however, the same for both formats.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Format&lt;/b&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_KslCmtD4qm8/STMpNpWI83I/AAAAAAAAADA/UFOou47RbVg/s1600-h/format.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 304px;" src="http://4.bp.blogspot.com/_KslCmtD4qm8/STMpNpWI83I/AAAAAAAAADA/UFOou47RbVg/s400/format.png" alt="" id="BLOGGER_PHOTO_ID_5274604902994735986" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The &lt;i&gt;make streamable&lt;/i&gt; option is probably unnecessary, but I allow people to download the original .mp4 file and maybe they want to watch it while downloading.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Audio codec&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/STQ_xZpfwyI/AAAAAAAAADQ/Qi0wAOG_jPU/s1600-h/audiocodec.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 305px; height: 191px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/STQ_xZpfwyI/AAAAAAAAADQ/Qi0wAOG_jPU/s400/audiocodec.png" alt="" id="BLOGGER_PHOTO_ID_5274911181488767778" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The default quality is 100, I increased that to 200. Hopefully this isn't the reason vimeo rejects the audio when in mov. The &lt;span style="font-style: italic;"&gt;Object type&lt;/span&gt; should be &lt;i&gt;Low&lt;/i&gt; (low complexity). Some decoders cannot decode anything else.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Video codec&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/STRArhf5yWI/AAAAAAAAADY/40v-bFXAvL4/s1600-h/frametypes.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 334px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/STRArhf5yWI/AAAAAAAAADY/40v-bFXAvL4/s400/frametypes.png" alt="" id="BLOGGER_PHOTO_ID_5274912180028426594" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I decreased the maximum GOP size to 30 as recommended by Vimeo. B-frames still screw up some decoders, so I didn't enable them. All other settings are default.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/STRDBRYthSI/AAAAAAAAADo/HUit1qK5zj4/s1600-h/ratecontrol.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 334px;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/STRDBRYthSI/AAAAAAAAADo/HUit1qK5zj4/s400/ratecontrol.png" alt="" id="BLOGGER_PHOTO_ID_5274914752683672866" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I encode with constant quality. In quicktime, there is no difference between CBR and VBR video, so the decoder won't notice. Constant quality also has the advantage that this setting is independent from the image size. The quantizer parameter was decreased from 26 to 16 to increase quality. It could be decreased further.&lt;br /&gt;&lt;h1&gt;Bugs&lt;/h1&gt;The following bugs were fixed during that process:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Reading raw DV files was completely broken. I broke it when I implemented DVCPROHD support last summer.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Chroma placement for H.264 is the same as for MPEG-2. This is now handled correctly by libquicktime and gmerlin-avdecoder.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Blending of text subtitles onto video frames in the transcoder was broken as well. It's needed for the advertisement banner at the end.&lt;/li&gt;&lt;li&gt;Gmerlin-avdecoder always signalled the existance of timecodes for raw DV. This is ok if the footage comes from a tape, but when recording &lt;span style="font-style: italic;"&gt;on the fly &lt;/span&gt;my camera produces no timecodes. This resulted in a Quicktime file with a timecode track, but without timecodes. Gmerlin-avdecoder was modified to tell about timecodes only if the first frame actually contains a timecode.&lt;/li&gt;&lt;li&gt;For making the screenshots, I called&lt;br&gt;&lt;code&gt;LANG="C" gmerlin_transcoder&lt;/code&gt;&lt;br&gt;This switched the GUI to English, except the items belonging to libquicktime. I found, that libquicktime translated the strings way to early (the German strings were saved gmerlin plugin registry). I made a change to libquicktime so that the strings are only translated for the GUI widget. Internally they are always English.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h1&gt;The result&lt;/h1&gt;&lt;br /&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-06356861175097459 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-06356861175097459 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-06356861175097459 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-06356861175097459 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-06356861175097459 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-06356861175097459 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-010862858495783745 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-010862858495783745 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-010862858495783745 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-010862858495783745 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-010862858495783745 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-010862858495783745 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-010862858495783745 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-010862858495783745 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-010862858495783745 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;a style="left: 0px ! important; top: 15px ! important;" title="Klicken Sie hier, um dieses Objekt mit Adblock Plus zu blockieren" class="abp-objtab-010862858495783745 visible ontop" href="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;/a&gt;&lt;object height="380" width="504"&gt;&lt;param name="allowfullscreen" value="true"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1"&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=2387314&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" height="380" width="504"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-4731533693801399219?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/4731533693801399219/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=4731533693801399219' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4731533693801399219'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4731533693801399219'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/11/video4vimeo.html' title='Video4vimeo'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_KslCmtD4qm8/STGhsJslaCI/AAAAAAAAACQ/GsdM08J4LEY/s72-c/toilet_making_of.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-4506486742282907465</id><published>2008-11-19T23:49:00.009+01:00</published><updated>2008-11-20T00:27:15.534+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='computer'/><title type='text'>Make your webcam suck less</title><content type='html'>Every webcam sucks. Not because of the webcam itself, but because of the way it's handled by the software. Some programs support only RGB formats, others work only in YUV. Supporting all pixelformats directly by the hardware would increase the price of these low-end articles. Supporting all pixelformats by the drivers would mean to have something similar to gavl in the Linux kernel. The linux kernel developers don't want this because it belongs into userspace. They are right IMO. And since not all programs have proper pixelformat support, you can always find an application, which doesn't support your cam.&lt;br /&gt;&lt;br /&gt;Other problems are that some webcams flip the image horizontally (for reasons I don't want to research). Furthermore, some programs aren't really smart when detecting webcams. They stop at the first device (which can be TV-card instead of a webcam) they can't handle.&lt;br /&gt;&lt;br /&gt;So the project was to make a webcam device at /dev/video0, which supports as many pixelformats as possible and allows image manipulation (like horizontal flipping).&lt;br /&gt;&lt;br /&gt;The solution involved the following:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Wrote a V4L2 input module for the &lt;span style="font-style: italic;"&gt;real&lt;/span&gt; webcam (not directly necessary for this project though).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Fixed my old webcam tool &lt;span style="font-style: italic;"&gt;camelot&lt;/span&gt;. Incredible how software breaks, if you don't maintain it for some time.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Added support for gmerlin filters in camelot: These can not only correct the image flipping, they provide tons of manipulation options. Serious ones and funny ones.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Added an output module for &lt;a href="http://www.lavrsen.dk/twiki/bin/view/Motion/VideoFourLinuxLoopbackDevice"&gt;vloopback&lt;/a&gt;. It's built into camelot and provides the webcam stream through a video4linux (1, not 2) device. It supports  most video4linux pixelformats because it has the conversion power of gavl behind it. Vloopback is not in the standard kernel. I got it from svn with&lt;br /&gt;&lt;br /&gt;&lt;code&gt;svn co http://www.lavrsen.dk/svn/vloopback/trunk/ vloopback&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;A tiny initialization script (to be called as root) initializes the kernel modules:&lt;br /&gt;&lt;pre&gt;#!/bin/sh&lt;br /&gt;# Remove modules if they were already loaded&lt;br /&gt;rmmod pwc&lt;br /&gt;rmmod vloopback&lt;br /&gt;&lt;br /&gt;# Load the pwc module, real cam will be /dev/video3&lt;br /&gt;modprobe pwc dev_hint=3&lt;br /&gt;&lt;br /&gt;# Load the vloopback module, makes /dev/video1 and /dev/video2&lt;br /&gt;modprobe vloopback dev_offset=1&lt;br /&gt;&lt;br /&gt;# Link /dev/video2 to /dev/video0 so even stupid programs find it&lt;br /&gt;ln -sf /dev/video2 /dev/video0&lt;br /&gt;&lt;/pre&gt;Instead of the pwc module, you must the one appropriate for your webcam. Not sure if all webcam drivers support the &lt;code&gt;dev_hint&lt;/code&gt; option.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/SSSc4Z1K1cI/AAAAAAAAACI/74bYt9dBRaU/s1600-h/camelot.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 388px;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/SSSc4Z1K1cI/AAAAAAAAACI/74bYt9dBRaU/s400/camelot.png" alt="" id="BLOGGER_PHOTO_ID_5270509956750824898" border="0" /&gt;&lt;/a&gt;My new webcam works with the following applications:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.videolan.org/"&gt;vlc&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.ffmpeg.org/"&gt;ffmpeg&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amsn-project.net"&gt;amsn&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Java_Media_Framework"&gt;JMF&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://camorama.fixedgear.org"&gt;Camorama&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://effectv.sourceforge.net/"&gt;EffecTV&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://gmerlin.sourceforge.net/"&gt;Camelot&lt;/a&gt; :)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;These are all I need for now. Not working are &lt;a href="http://kopete.kde.org/"&gt;kopete&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Adobe_Flash"&gt;flash&lt;/a&gt; and &lt;a href="http://linux.bytesex.org/xawtv/"&gt;Xawtv&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-4506486742282907465?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/4506486742282907465/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=4506486742282907465' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4506486742282907465'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4506486742282907465'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/11/make-your-webcam-suck-less.html' title='Make your webcam suck less'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_KslCmtD4qm8/SSSc4Z1K1cI/AAAAAAAAACI/74bYt9dBRaU/s72-c/camelot.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-6149407042271123386</id><published>2008-11-13T20:36:00.005+01:00</published><updated>2008-11-13T21:36:51.526+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Gmerlin pipelines explained</title><content type='html'>Building multimedia software on top of gavl saves a lot of time others spend on writing optimized conversion routines (gavl already has more than 2000 of them) and bullet-proof housekeeping functions.&lt;br /&gt;&lt;br /&gt;On the other hand, gavl is a &lt;span style="font-weight: bold;"&gt;low-level&lt;/span&gt; library, which leaves lots of architectural decisions to the application level. And this means, that gavl will not provide you with fully featured A/V pipelines. Instead, you have to write them yourself (or use libgmerlin and take a look at &lt;code&gt;include/gmerlin/filters.h&lt;/code&gt; and &lt;code&gt;include/gmerlin/converters.h&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;I'm not claiming to have found the perfect solution for the gmerlin player and transcoder, but nevertheless here is how it works:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Building blocks&lt;/b&gt;&lt;br /&gt;The pipelines are composed of&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A source plugin, which gets A/V frames from a media file, URL or a hardware device&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Zero or more filters, which somehow change the A/V frames&lt;br /&gt;&lt;/li&gt;&lt;li&gt;A destination plugin. In the player it displays video or sends audio to the soundcard. For the transcoder, it encodes into media files.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Format converters: These are inserted on demand between any two of the above elements&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Asynchronous pull approach&lt;/span&gt;&lt;br /&gt;The whole pipeline is pull-based. Pull-based means, that each component requests data from the preceeding component. Asynchronous means that (in contrast to plain gavl), we make no assumption on how many frames/samples a component needs at the input for producing one output frame/sample. This makes it possible to do things like framerate conversion or framerate-doubling deinterlacing. As a consequence, filters and converters which remember previous frames need a reset function to forget about them (the player e.g. calls them after seeking).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Unified callbacks&lt;/b&gt;&lt;br /&gt;In modular applications it's always important that modules know as little as possible about each other. For A/V pipelines this means, that each component gets data from the preceeding component using a unified callback, no matter if it's a filter, converter or source. There are prototypes in &lt;code&gt;gmerlin/plugin.h&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;typedef int (*bg_read_audio_func_t)(void * priv, gavl_audio_frame_t* frame, int stream,&lt;br /&gt;                                 int num_samples);&lt;br /&gt;&lt;br /&gt;typedef int (*bg_read_video_func_t)(void * priv, gavl_video_frame_t* frame, int stream);&lt;br /&gt;&lt;/pre&gt;These are provided by input plugins, converters and filters. The &lt;code&gt;stream&lt;/code&gt; argument is only meaningful for media files which have more than one audio or video stream. How the pipeline is exactly constructed (e.g. if intermediate converters are needed) matters only during initialization, not in the time critical processing loop.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Asynchronous vs synchronous&lt;/b&gt;&lt;br /&gt;As noted above, some filter types are only realizable if the architecture is asynchronous. Another advantage is that for a filter, the input- and output frame can be the same (in-place conversion). E.g. the timecode tweak filter of gmerlin looks like:&lt;br /&gt;&lt;pre&gt;typedef struct&lt;br /&gt;{&lt;br /&gt;bg_read_video_func_t read_func;&lt;br /&gt;void * read_data;&lt;br /&gt;int read_stream;&lt;br /&gt;&lt;br /&gt;/* Other stuff */&lt;br /&gt;/* ... */&lt;br /&gt;} tc_priv_t;&lt;br /&gt;&lt;br /&gt;static int read_video_tctweak(void * priv, gavl_video_frame_t * frame,&lt;br /&gt;                      int stream)&lt;br /&gt;{&lt;br /&gt;tc_priv_t * vp;&lt;br /&gt;vp = (tc_priv_t *)priv;&lt;br /&gt;&lt;br /&gt;/* Let the preceeding element fill the frame, return 0 on EOF */&lt;br /&gt;if(!vp-&gt;read_func(vp-&gt;read_data, frame, vp-&gt;read_stream))&lt;br /&gt; return 0;&lt;br /&gt;&lt;br /&gt;/* Change frame-&gt;timecode */&lt;br /&gt;/* ... */&lt;br /&gt;&lt;br /&gt;/* Return success */&lt;br /&gt;return 1;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;A one-in-one-out API would need to memcpy the video data only for changing the timecode.&lt;br /&gt;&lt;br /&gt;Of course in some situations outside the scope of gmerlin, asynchronous pipelines can cause problems. This is especially the case in editing applications, where frames might be processed out of order (e.g. when playing backwards). How to solve &lt;span style="font-weight: bold;"&gt;backwards&lt;/span&gt; playback for filters, which use &lt;span style="font-weight: bold;"&gt;previous&lt;/span&gt; frames, is left to the NLE developers. But it would make sense to mark gmerlin filters, which behave synchronously (most of them actually do), as such so we know we can always use them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-6149407042271123386?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/6149407042271123386/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=6149407042271123386' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/6149407042271123386'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/6149407042271123386'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/11/gmerlin-pipelines-explained.html' title='Gmerlin pipelines explained'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-4807569459730802391</id><published>2008-11-09T15:13:00.004+01:00</published><updated>2008-11-09T15:22:50.253+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Release plans</title><content type='html'>Time to make releases of the packages. The current status is the following:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://hirntier.blogspot.com/2008/11/introducing-gmerlin-mozilla.html"&gt;Gmerlin-mozilla&lt;/a&gt; is practically ready, some well hidden bugs cause it to crash sometimes though. Also the scripting interface could be further extended.&lt;br /&gt;&lt;li&gt;gavl is ready. New features since the last version are timecode support, &lt;a href="http://hirntier.blogspot.com/2008/09/image-transformation.html"&gt;image transformation&lt;/a&gt; and a contributed varispeed capable audio resampler.&lt;br /&gt;&lt;li&gt;gmerlin-avdecoder got lots of fixes, support for newer ffmpegs, a demuxer for redcode files and RTP/RTSP support. The latter was most difficult to implement. It still needs some work for better recovering after packet loss in UDP mode. Since all important features are implemented now, gmerlin-avdecoder will get the version 1.0.0.&lt;br /&gt;&lt;li&gt;The GUI player can now import directories with the option "watch directory". This will cause the album to be syncronized with the directory each time it is opened. The plan is to further extend this such that even an opened album is regularly synchronized via inotify. Except from this, the gmerlin package is ready.&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-4807569459730802391?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/4807569459730802391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=4807569459730802391' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4807569459730802391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4807569459730802391'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/11/release-plans.html' title='Release plans'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-1736494351796861670</id><published>2008-11-06T00:39:00.008+01:00</published><updated>2008-11-06T15:30:54.362+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Introducing gmerlin-mozilla</title><content type='html'>What's the best method to check if your multimedia architecture is &lt;span style="font-weight: bold;"&gt;really&lt;/span&gt; completely generic and reusable? One way is to write a firefox plugin for video playback and beat onto everything until it no longer crashes. The preliminary result is here:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/SRIvFQP1FtI/AAAAAAAAACA/3g93i9woiPQ/s1600-h/gmerlin_mozilla_1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 350px; height: 376px;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/SRIvFQP1FtI/AAAAAAAAACA/3g93i9woiPQ/s400/gmerlin_mozilla_1.png" alt="" id="BLOGGER_PHOTO_ID_5265322681656743634" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And here are some things I think are worth noting:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;How a plugin gets it's data&lt;/b&gt;&lt;br /&gt;There are 2 methods:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;firefox handles the TCP connection and passes data via callbacks (&lt;code&gt;NPP_WriteReady&lt;/code&gt;, &lt;code&gt;NPP_Write&lt;/code&gt;). The gmerlin plugin API got a callback based read interface for this.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;the plugin opens the URL itself&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;The first method has the advantage, that procotols not supported by gmerlin but by firefox (e.g. https) will work. The disadvantage is, that passing the data from firefox to the input thread if the player will almost lockup the GUI because firefox spends most of it's time waiting until the player can accept more data. I found no way to prevent this in an elegant way. Thus, such streams are written to a temporary file and read by the input thread. Local files are recognized as such and opened by the plugin.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Emulating other plugins&lt;/b&gt;&lt;br /&gt;Commercial plugins (like Realplayer or Quicktime) have lots of gimmicks. One of these lets you embed multiple instances of the plugin, where one will show up as the video window, another one as the volume slider etc. Gmerlin-mozilla handles these pragmatically: The video window always has a toolbar (which can hide after the mouse was idle), so additional control widgets are not initialized at all. They will appear as grey boxes.&lt;br /&gt;&lt;br /&gt;Of course not all oddities are handled correctly yet, but the infrastructure for doing this is there.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Scriptability&lt;/b&gt;&lt;br /&gt;Another gimmick is to control the plugin from a JavaScript GUI. While the older scripting API (&lt;a href="http://en.wikipedia.org/wiki/Xpcom"&gt;XPCOM&lt;/a&gt;) was kind of bloated and forced the programmer into C++, the new method (&lt;a href="https://developer.mozilla.org/En/Scripting_plugins"&gt;npruntime&lt;/a&gt;) is practically as versatile, but much easier to support (even in plain C). Basically, the plugin exports an object, (an NPObject) which has (among others) functions for querying the  supported methods and properties. Other functions exist for invoking methods, or setting and getting properties. Of course not all scripting commands are supported yet.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;GUI&lt;/b&gt;&lt;br /&gt;A GUI for a web-plugin must look sexy, that's clear. Gmerlin-mozilla has a GUI similar to the GUI player (which might look completely unsexy for some). But in contrast to other free web-plugins it's skinnable, so there is at least a chance to change this.&lt;br /&gt;&lt;br /&gt;Some GUI widgets had to be updated and fixed before they could be used in the plugin. Most importantly, timeouts (like for the scrolltext) have to be removed from the event-loop before the plugin is destroyed, otherwise a crash happens after.&lt;br /&gt;&lt;br /&gt;The fine thing is, that firefox also uses gtk-2 for it's GUI, so having Gtk-widgets works perfectly. If the browser isn't gtk-2 based, the plugin won't load.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Embedding technique&lt;/b&gt;&lt;br /&gt;Gmerlin-mozilla needs &lt;a href="http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html"&gt;XEmbed&lt;/a&gt;. Some people hate XEmbed, but I think it's pretty well designed as long as you don't expect too much from it. The Gmerlin X11 display plugin already supports XEmbed because it always opens it's own X11 connection. After I fixed some things, it embeds nicely into firefox.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Configuration&lt;/b&gt;&lt;br /&gt;The GUI should not be bloated by exotic buttons, which are rarely used. Therefore most of the configuration options are available via the right-click menu. Here, you can also select fullscreen mode.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-1736494351796861670?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/1736494351796861670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=1736494351796861670' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1736494351796861670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1736494351796861670'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/11/introducing-gmerlin-mozilla.html' title='Introducing gmerlin-mozilla'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_KslCmtD4qm8/SRIvFQP1FtI/AAAAAAAAACA/3g93i9woiPQ/s72-c/gmerlin_mozilla_1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-1334361025228826109</id><published>2008-10-16T00:43:00.012+02:00</published><updated>2008-10-16T01:04:57.340+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>Streaming through the NAT</title><content type='html'>The only missing network streaming protocol for gmerlin-avdecoder was &lt;a href="http://en.wikipedia.org/wiki/Rtsp"&gt;RTSP&lt;/a&gt;/&lt;a href="http://en.wikipedia.org/wiki/Real-time_Transport_Protocol"&gt;RTP&lt;/a&gt;, so I decided to implement it. Some parts (the ones needed for playing the &lt;a href="http://en.wikipedia.org/wiki/RealMedia"&gt;Real&lt;/a&gt;-rtsp variant) were already there, but the whole RTP stuff was missing.&lt;br /&gt;&lt;br /&gt;The advantage of these is that they are well documented in RFCs. With some knowledge about how sockets and their API work, implementation was straightforward. Special about RTSP/RTP is, that there is one RTSP connection (usually TCP port 554) which acts like a &lt;i&gt;remote control&lt;/i&gt;, while the actual A/V data are delivered over RTP, which usually uses UDP. To make things more complicated, each stream is transported over an own UDP socket, with another socket used for &lt;a href="http://en.wikipedia.org/wiki/Qos"&gt;Qos&lt;/a&gt; infos. Playing a normal movie with audio and video needs 4 UDP sockets then.&lt;br /&gt;&lt;br /&gt;The basic functions were implemented, I opened 4 UDP ports on my &lt;a href="http://service.t-online.de/c/12/71/70/16/12717016.html"&gt;DSL-Router&lt;/a&gt; and I could play movies :)&lt;br /&gt;&lt;br /&gt;Then I stumbled across something strange:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;    My code worked completely predictable regarding the router configuration. When I closed the ports on the router (or changed the ports in my code), it stopped working&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Both &lt;a href="http://www.blogger.com/www.ffmpeg.org"&gt;ffmpeg&lt;/a&gt; and &lt;a href="http://www.videolan.org/"&gt;vlc&lt;/a&gt; (which, like &lt;a href="http://www.blogger.com/www.mplayerhq.hu"&gt;MPlayer&lt;/a&gt;, uses &lt;a href="http://www.live555.com/"&gt;live555&lt;/a&gt; for RTSP) always work in UDP mode, no need to manually open the UDP ports. Somehow they make my router forward the incoming RTP packets to my machine.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;So the question was: How?&lt;br /&gt;&lt;br /&gt;After spending some time with &lt;a href="http://www.wireshark.org/"&gt;wireshark&lt;/a&gt; and &lt;a href="http://sourceforge.net/projects/strace/"&gt;strace&lt;/a&gt; I made sure, that I setup my sockets the same way as the other applications, and the RTSP requests are the same. When gmerlin-avdecoder still didn't make it through the NAT (and me almost freaking out) I decided to take a look at some TCP packets, which were marked with the string &lt;code&gt;"TCP segment of a reassembled PDU"&lt;/code&gt;. I noticed that these occur only in the wireshark dump of gmerlin-avdecoder, not in the others.&lt;br /&gt;&lt;br /&gt;After googling a bit, the mystery was solved:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The Router (which was found to be a MIPS-based Linux box) recognizes the RTSP protocol. By parsing the &lt;code&gt;client_port&lt;/code&gt; field of the &lt;code&gt;SETUP&lt;/code&gt; request it knows which UDP ports it must open and forward to the client machine.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The "TCP segment of a reassembled PDU" packets are small pieces belonging to one larger RTSP request.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If the &lt;code&gt;SETUP&lt;/code&gt; line is not in the same TCP packet as the line which defines the transport, the recognition by the router will fail.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Wireshark fooled me by assembling the packets belonging to the same request into a larger one and displaying it together with the pieces (this &lt;i&gt;feature&lt;/i&gt; can be turned off in the wireshark TCP configuration).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The fix was simple: I write the whole request into one string, and send this string at once. Finally the router automagically sends the RTP packets to gmerlin-avdecoder.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;What did I learn through this process? Most notably that TCP is &lt;i&gt;stream-based&lt;/i&gt; only for the client and the server. Any hardware between these 2 only sees packets. Applications relying on intelligent network hardware must indeed take care, which data will end up in which packet.&lt;br /&gt;&lt;br /&gt;You might think that it's actually no problem to open UDP ports on the router, and doing such things manually is better than automatically. But then you'll have many people running their clients with the same UDP ports, which makes attacks easier for the case that gmerlin-avdecoder has a security hole. Much better is to choose the ports randomly. Then, we can also have multiple clients in the same machine. The live555 library uses random ports, ffmpeg doesn't.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-1334361025228826109?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/1334361025228826109/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=1334361025228826109' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1334361025228826109'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1334361025228826109'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/10/streaming-through-nat.html' title='Streaming through the NAT'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-7282642150378165478</id><published>2008-10-08T23:39:00.002+02:00</published><updated>2008-10-08T23:44:11.567+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Globals in libs: If and how</title><content type='html'>As a follow-up to &lt;a href="http://hirntier.blogspot.com/2008/10/remove-globals-from-libs.html"&gt;this&lt;/a&gt; post I want to concentrate on the cases, where global variables are tolerable and how this should be done.&lt;br /&gt;&lt;br /&gt;Tolerable as global variables are data, whose initialization &lt;i&gt;must&lt;/i&gt; be done at runtime and &lt;br /&gt;takes a significant amount of time. One example is the libquicktime codec registry. It's creation involves scanning the plugin directory, comparing the contents with a registry and loading all modules (with a time consuming &lt;code&gt;dlopen&lt;/code&gt;), for which the registry entries are missing or outdated. This is certainly not something, which should be done per instance (i.e. for each opened file). Other libraries have similar things.&lt;br /&gt;&lt;br /&gt;Next question is how can they be implemented? A simple goal is, that the library must linkable with a plugin (i.e. dynamic module) instead of an executable. This means, that repeated loading and unloading (from different threads) must work without any problems. A well designed plugin architecture knows as little as possible about the plugins, so having global reference counters for each library a plugin might link in, is not possible.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Global initialization and cleanup functions&lt;/h2&gt;&lt;br /&gt;Many libraries have functions like &lt;code&gt;libfoo_init()&lt;/code&gt; and &lt;code&gt;libfoo_cleanup()&lt;/code&gt;, which are to be called before the first and after the last use of other functions from libfoo respectively. This causes problems for a plugin, which has no idea if this library was already be loaded/initialized by another plugin (or by another instance of itself). Also before a plugin is unloaded there is no way to find out, if &lt;code&gt;libfoo_cleanup()&lt;/code&gt; can safely be called or if this will crash another plugin. Omitting the &lt;code&gt;libfoo_cleanup()&lt;/code&gt; call opens a memory leak if the &lt;code&gt;libfoo_init()&lt;/code&gt; function allocated memory. From this we find that the global housekeeping functions are ok if either:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Initialization doesn't allocate any resources (i.e the cleanup function is either a noop or missing) and&lt;br /&gt;&lt;li&gt;Initialization is (thread safely) protected against multiple calls&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;or:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Initialization and cleanup functions maintain an internal (thread safe) reference counter, so that only the first init and last cleanup call will actually do something&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Initialization on demand, cleanup automatically&lt;/h2&gt;&lt;br /&gt;This is how the libquicktime codec registry is handled. It meets the above goals but doesn't need any global functions. Initialization on demand means, that the codec registry is initialized before it's accessed the first time. Each function, which accesses the registry starts with a call to &lt;code&gt;lqt_registry_init()&lt;/code&gt;. The subsequent registry access is enclosed by &lt;code&gt;lqt_registry_lock()&lt;/code&gt; and &lt;code&gt;lqt_registry_unlock()&lt;/code&gt;. These 3 functions do the whole magic and they look like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;static int registry_init_done = 0;&lt;br /&gt;pthread_mutex_t codecs_mutex = PTHREAD_MUTEX_INITIALIZER;&lt;br /&gt;&lt;br /&gt;void lqt_registry_lock()&lt;br /&gt;  {&lt;br /&gt;  pthread_mutex_lock(&amp;codecs_mutex);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;void lqt_registry_unlock()&lt;br /&gt;  {&lt;br /&gt;  pthread_mutex_unlock(&amp;codecs_mutex);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;void lqt_registry_init()&lt;br /&gt;  {&lt;br /&gt;  /* Variable declarations omitted */&lt;br /&gt;  /* ... */&lt;br /&gt;&lt;br /&gt;  lqt_registry_lock();&lt;br /&gt;  if(registry_init_done)&lt;br /&gt;    {&lt;br /&gt;    lqt_registry_unlock();&lt;br /&gt;    return;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;  registry_init_done = 1;&lt;br /&gt;&lt;br /&gt;  /* Lots of stuff */&lt;br /&gt;  /* ... */&lt;br /&gt;&lt;br /&gt;  lqt_registry_unlock();&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We see that protection against multiple calls is garantueed. The protection mutex itself initialized from the very beginning (before the main function is called).&lt;br /&gt;&lt;br /&gt;While this initialization should work on all POSIX systems, automatic freeing is a bit more tricky and &lt;b&gt;only possible for gcc&lt;/b&gt; (don't know if other compilers have similar features). The best time for freeing global resources is right before the library is unloaded. Most binary formats let you mark functions, which should be called before unmapping the library (in ELF files, this is done by putting these into the &lt;code&gt;.fini&lt;/code&gt; section). In the sourcecode, this looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#if defined(__GNUC__)&lt;br /&gt;&lt;br /&gt;static void __lqt_cleanup_codecinfo() __attribute__ ((destructor));&lt;br /&gt;&lt;br /&gt;static void __lqt_cleanup_codecinfo()&lt;br /&gt;  {&lt;br /&gt;  lqt_registry_destroy();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Fortunately the &lt;code&gt;dlopen()&lt;/code&gt; and &lt;code&gt;dlclose()&lt;/code&gt; functions maintain reference counts for each module. So the cleanup function is garantueed to be called by the &lt;code&gt;dlclose()&lt;/code&gt; call, which unloads the last instance of the last plugin linked to libquicktime. &lt;br /&gt;&lt;br /&gt;I regularly check my programs for memory leaks with valgrind. Usually (i.e. after I fixed my own code) all remaining leaks come from libraries, which miss some of the goals described above.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-7282642150378165478?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/7282642150378165478/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=7282642150378165478' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7282642150378165478'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7282642150378165478'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/10/globals-in-libs-if-and-how.html' title='Globals in libs: If and how'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-3442814150667396051</id><published>2008-10-08T21:41:00.005+02:00</published><updated>2008-10-08T23:56:56.845+02:00</updated><title type='text'>Remove globals from libs</title><content type='html'>Global variables in libraries are bad, everyone knows that. Maybe I'll make another post explaining when they can be tolerated how this can be done. But for now we assume that they are simply bad.&lt;br /&gt;&lt;br /&gt;One common mistake is to declare static data (like tables) as non-const. This makes them&lt;br /&gt;practically variables. If the code never changes them, they cause no problem in terms of thread safety. But unfortunately the dynamic linker doesn't know that, so they will be mapped into r/w pages when the library is loaded. And those pages will, of course, not be shared between applications so you end up with predictable redundant blocks in your precious RAM.&lt;br /&gt;&lt;br /&gt;Cleaning this up is simple: Add &lt;code&gt;const&lt;/code&gt; to all declarations, where it's missing. But how does one find all these declarations in a larger sourcetree in a reasonable time? The ELF format is well documented and there are numerous tools to examine ELF files.&lt;br /&gt;&lt;br /&gt;Let's take the following C file and pretend it's a library build from 100s of sourcefiles with 100000 of codelines:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;struct s&lt;br /&gt;{&lt;br /&gt;char * str;&lt;br /&gt;char ** str_list;&lt;br /&gt;int i;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;struct s static_data_1 =&lt;br /&gt;{&lt;br /&gt;"String1",&lt;br /&gt;(char*[]){ "Str1", "Str2" },&lt;br /&gt;1,&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;char * static_string_1 = "String2";&lt;br /&gt;&lt;br /&gt;int zeroinit = 0;&lt;br /&gt;&lt;/pre&gt;Now there are 2 sections in an ELF file, which need special attention: The &lt;code&gt;.data&lt;/code&gt; section contains statically initialized variables. The &lt;code&gt;.bss&lt;/code&gt; section contains data which is initialized to zero. After compiling the file with &lt;code&gt;gcc -c&lt;/code&gt; the sizes of the sections can be obtained with:&lt;br /&gt;&lt;pre&gt;# size --format=SysV global.o&lt;br /&gt;global.o  :&lt;br /&gt;section           size   addr&lt;br /&gt;.text                0      0&lt;br /&gt;.data               56      0&lt;br /&gt;.bss                 4      0&lt;br /&gt;.rodata             26      0&lt;br /&gt;.comment            42      0&lt;br /&gt;.note.GNU-stack      0      0&lt;br /&gt;Total              128&lt;br /&gt;&lt;/pre&gt;So we have 56 bytes in &lt;code&gt;.data&lt;/code&gt; and 4 bytes in &lt;code&gt;.bss&lt;/code&gt;&lt;code&gt;&lt;/code&gt;. After successful cleanup all these should ideally end up in the &lt;code&gt;.rodata&lt;/code&gt; section (read-only statically initialized data). Since we have 100000 lines of code, the next step is to find the variable names (linker symbols) contained in the sections:&lt;br /&gt;&lt;pre&gt;# objdump -t global.o&lt;br /&gt;&lt;br /&gt;global.o:     file format elf64-x86-64&lt;br /&gt;&lt;br /&gt;SYMBOL TABLE:&lt;br /&gt;0000000000000000 l    df *ABS* 0000000000000000 global.c&lt;br /&gt;0000000000000000 l    d  .text 0000000000000000 .text&lt;br /&gt;0000000000000000 l    d  .data 0000000000000000 .data&lt;br /&gt;0000000000000000 l    d  .bss 0000000000000000 .bss&lt;br /&gt;0000000000000000 l    d  .rodata 0000000000000000 .rodata&lt;br /&gt;0000000000000020 l     O .data 0000000000000010 __compound_literal.0&lt;br /&gt;0000000000000000 l    d  .note.GNU-stack 0000000000000000 .note.GNU-stack&lt;br /&gt;0000000000000000 l    d  .comment 0000000000000000 .comment&lt;br /&gt;0000000000000000 g     O .data 0000000000000018 static_data_1&lt;br /&gt;0000000000000030 g     O .data 0000000000000008 static_string_1&lt;br /&gt;0000000000000000 g     O .bss 0000000000000004 zeroinit&lt;br /&gt;&lt;/pre&gt;Now we know that the variables &lt;code&gt;static_data_1&lt;/code&gt;, &lt;code&gt;static_string_1&lt;/code&gt;&lt;br /&gt;and &lt;code&gt;zeroinit&lt;/code&gt; are affected.&lt;br /&gt;&lt;br /&gt;The symbol &lt;code&gt;__compound_literal.0&lt;/code&gt; comes from the expression &lt;code&gt;(char*[]){ "Str1", "Str2" }&lt;/code&gt;. The bad news is that compound literals are lvalues according to the C99 standard, so they won't be assumed const by gcc. You can declare them const, but they'll still be in the &lt;code&gt;.data&lt;/code&gt; section, at least for &lt;code&gt;gcc-Version 4.2.3 (Ubuntu 4.2.3-2ubuntu7)&lt;/code&gt;. The cleaned up file looks like:&lt;br /&gt;&lt;pre&gt;struct s&lt;br /&gt;{&lt;br /&gt;const char * str;&lt;br /&gt;char ** const str_list;&lt;br /&gt;int i;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;static const struct s static_data_1 =&lt;br /&gt;{&lt;br /&gt;"String1",&lt;br /&gt;(char*[]){ "Str1", "Str2" },&lt;br /&gt;1,&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;char const * const static_string_1 = "String2";&lt;br /&gt;&lt;br /&gt;const int zeroinit = 0;&lt;br /&gt;&lt;/pre&gt;The resulting symbol table:&lt;br /&gt;&lt;pre&gt;0000000000000000 l    df *ABS* 0000000000000000 global1.c&lt;br /&gt;0000000000000000 l    d  .text 0000000000000000 .text&lt;br /&gt;0000000000000000 l    d  .data 0000000000000000 .data&lt;br /&gt;0000000000000000 l    d  .bss 0000000000000000 .bss&lt;br /&gt;0000000000000000 l    d  .rodata 0000000000000000 .rodata&lt;br /&gt;0000000000000010 l     O .rodata 0000000000000018 static_data_1&lt;br /&gt;0000000000000000 l     O .data 0000000000000010 __compound_literal.0&lt;br /&gt;0000000000000000 l    d  .note.GNU-stack 0000000000000000 .note.GNU-stack&lt;br /&gt;0000000000000000 l    d  .comment 0000000000000000 .comment&lt;br /&gt;0000000000000040 g     O .rodata 0000000000000008 static_string_1&lt;br /&gt;0000000000000048 g     O .rodata 0000000000000004 zeroinit&lt;br /&gt;&lt;/pre&gt;Larger libraries have &lt;i&gt;huge&lt;/i&gt; symbol tables, so you will of course filter it with:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;grep \\.data | grep -v __compound_literal&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;So if you want to contribute to a library which needs some cleanup, and you are of the &lt;i&gt;"I know just a little C but I want to help"&lt;/i&gt;-type, this is a good idea for a patch :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-3442814150667396051?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/3442814150667396051/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=3442814150667396051' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/3442814150667396051'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/3442814150667396051'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/10/remove-globals-from-libs.html' title='Remove globals from libs'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-1873859846881961859</id><published>2008-10-08T00:37:00.002+02:00</published><updated>2008-10-08T00:41:30.718+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='computer'/><title type='text'>2 ssh servers on the same port</title><content type='html'>A tcp port can only be used by one server process for incoming connections. If another process wants to listen on the same port it will get an "address already in use" error from the OS. If you know the background it's pretty clear why it must be so.&lt;br /&gt;&lt;br /&gt;But imagine a case like the following:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;You want to make a linux machine reachable via ssh&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt; From the same subnet passwords are sufficient&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;From outside only public key authentication is allowed&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Your users are already happy if they get their ssh clients working on Windows XP. You don't want to bother them (and indirectly yourself as the admin) with nonstandard port numbers.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Your sshd doesn't support different configurations depending on the source address.&lt;/li&gt;&lt;/ul&gt;At a first glance, this looks unsolvable. But if you have an iptables firewall (and you will have one if the machine is worldwide reachable) there is a little known trick called port redirection.&lt;br /&gt;&lt;br /&gt;You run 2 ssh servers: The external one (with public key authentication) listens at port 22, the internal one (with passwords) listens e.g. at port 2222. Then you configure your iptables such, that incoming packets which come from the subnet to port 22 are redirected to port 2222. The corresponding lines in the firewall script look like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# Our Subnet&lt;br /&gt;SUB_NET="192.168.1.0/24"&lt;br /&gt;&lt;br /&gt;# iptables command&lt;br /&gt;IPTABLES=/usr/sbin/iptables&lt;br /&gt;&lt;br /&gt;# default policies, flush all tables etc....&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;# ssh from our subnet (redirect to port 2222 and let them through)&lt;br /&gt;$IPTABLES -t nat -A PREROUTING -s $SUB_NET -p tcp --dport 22 \&lt;br /&gt; -j REDIRECT --to-ports 2222&lt;br /&gt;$IPTABLES -A INPUT -p tcp -s $SUB_NET --syn --dport 2222 -j ACCEPT&lt;br /&gt;&lt;br /&gt;# ssh from outside&lt;br /&gt;$IPTABLES -A INPUT -p tcp -s ! $SUB_NET --syn --dport 22 -j ACCEPT&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I have this configuration on 2 machines for many months now with zero complaints so far.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-1873859846881961859?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/1873859846881961859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=1873859846881961859' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1873859846881961859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1873859846881961859'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/10/2-ssh-servers-on-same-port.html' title='2 ssh servers on the same port'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-3471888425650811529</id><published>2008-09-24T23:36:00.005+02:00</published><updated>2008-09-25T00:03:42.947+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>The fastest loop in C</title><content type='html'>Sure, practically nobody knows assembler nowadays. But if you use a certain construct very often in time critical code, you might get curious what's the fastest way to do it. One example are loops. I tested this with a loop of the following properties:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Loop count is unknown at compile time, so the loop cannot be unrolled&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The loop index &lt;code&gt;i&lt;/code&gt; is not used within the loop body. In particular it doesn't matter it it's incremented or decremented&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;This kind of loop occurs very often in gavl colorspace conversion routines. The following C-file has 3 functions, which do exactly the same:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;void do_something();&lt;br /&gt;&lt;br /&gt;static int num;&lt;br /&gt;&lt;br /&gt;void loop_1()&lt;br /&gt;{&lt;br /&gt;int i;&lt;br /&gt;for(i = 0; i &lt; num; i++)&lt;br /&gt;  do_something();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void loop_2()&lt;br /&gt;{&lt;br /&gt;int i = num;&lt;br /&gt;while(i--)&lt;br /&gt;  do_something();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void loop_3()&lt;br /&gt;{&lt;br /&gt;int i = num+1;&lt;br /&gt;while(--i)&lt;br /&gt;  do_something();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;This code can be compiled with &lt;code&gt;gcc -O2 -S&lt;/code&gt;. The resulting loop bodies are the following:&lt;code&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;loop 1:&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;pre&gt;.L17:&lt;br /&gt; xorl    %eax, %eax&lt;br /&gt; addl    $1, %ebx&lt;br /&gt; call    do_something&lt;br /&gt; cmpl    %ebp, %ebx&lt;br /&gt; jne     .L17&lt;br /&gt;&lt;/pre&gt;&lt;b&gt;loop 2:&lt;/b&gt;&lt;br /&gt;&lt;pre&gt;.L11:&lt;br /&gt; xorl    %eax, %eax&lt;br /&gt; addl    $1, %ebx&lt;br /&gt; call    do_something&lt;br /&gt; cmpl    %ebp, %ebx&lt;br /&gt; jne     .L11&lt;br /&gt;&lt;/pre&gt;&lt;b&gt;loop 3:&lt;/b&gt;&lt;br /&gt;&lt;pre&gt;.L5:&lt;br /&gt; xorl    %eax, %eax&lt;br /&gt; call    do_something&lt;br /&gt; subl    $1, %ebx&lt;br /&gt; jne     .L5&lt;br /&gt;&lt;/pre&gt;As you see, the winner is loop 3. Here, the whole logic (without initialization) needs 2 machine instructions:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Decrement &lt;code&gt;ebx&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Jump is result is nonzero&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;I doubt it can be done faster. You also see, that loop 2 looks simpler than loop 1 in C, but the compiler produces exactly the same code. I changed the innermost gavl pixelformat conversion loops to loop 3 and could even measure a tiny speedup for very simple conversions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-3471888425650811529?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/3471888425650811529/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=3471888425650811529' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/3471888425650811529'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/3471888425650811529'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/09/fastest-loop-in-c.html' title='The fastest loop in C'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-3347261149752655655</id><published>2008-09-22T20:52:00.005+02:00</published><updated>2008-09-22T21:33:23.548+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Fun with the colormatrix</title><content type='html'>Some years ago, when I wrote &lt;a href="http://gmerlin.sourceforge.net/lemuria.html"&gt;lemuria&lt;/a&gt;, I was always complaining that OpenGL doesn't have a colormatrix. Later I found, that some extensions provide this, but not the core API. Today I think the main problem was that I used a graphics system (OpenGL) which is optimized to look as &lt;i&gt;realistic&lt;/i&gt; as possible for a visualization which should look a &lt;i&gt;surrealistic&lt;/i&gt; as possible :)&lt;br /&gt;&lt;br /&gt;Later when I concentrated on more serious work like developing high quality video filters, I rediscovered the colormatrix formalism. Now what is that all about and what's so exciting about that? If you assume each pixel to be a vector of color channels, multiplying the vector by a matrix is simply a linear transformation of each pixel. In homogenous coordinates and in RGBA or YUVA (i.e. 4 channel) colorspace, it can be completely described by a 4x5 matrix.  Now there are lots of commonly used video filters, which can be described by a colormatrix multiplication:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Brightness/Contrast (Y'CbCrA)&lt;/b&gt;&lt;br /&gt;&lt;table style="width: 162px; height: 97px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;c&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;b - (1+c)/2&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;b and c are between 0.0 and 2.0.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Saturation/Hue rotation (Y'CbCrA)&lt;/b&gt;&lt;br /&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;s*cos(h)&lt;/td&gt;&lt;td&gt;-s*sin(h)&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;s*sin(h)&lt;/td&gt;&lt;td&gt;s*cos(h)&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;h is between -pi and pi (0 is neutral). s is between 0.0 (grayscale) and 2.0 (oversaturated).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Invert single RGB channels (RGBA)&lt;/b&gt;&lt;br /&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;-1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;This inverts only the first (red) channel. Inverting other channels is done by changing any of the other lines accordingly.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Swap RGB channels (RGBA)&lt;/b&gt;&lt;br /&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;This swaps the first (red) and 3rd (blue) channel. It can be used to rescue things if some buggy routine confused RGB and BGR. Other swapping schemes are trivial to do.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;RGB gain (RGBA)&lt;/b&gt;&lt;br /&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;g&lt;sub&gt;r&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;g&lt;sub&gt;g&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;g&lt;sub&gt;b&lt;/sub&gt;&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;[&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;]&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;g&lt;sub&gt;r&lt;/sub&gt;, g&lt;sub&gt;g&lt;/sub&gt; and g&lt;sub&gt;b&lt;/sub&gt; are in the range 0.0..2.0.&lt;br /&gt;&lt;br /&gt;There are of course countless other filters possible, like generating an alpha channel from (inverted) luminance values etc. Now, what do you do if you want to make e.g. a brightness/contrast filter working on RGBA images? The naive method is to transform the RGBA values to Y'CbCrA, do the filtering and transform back to RGBA. And this is what can be optimized in a very elegant way by using some simple matrix arithmetics. Instead of performing 2 colorspace conversions in addition to the actual filter for each pixel, you can simply transform the colormatrix (M) from Y'CbCrA to RGBA:&lt;br /&gt;&lt;br /&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Untransformed pixel in RGBA:&lt;/td&gt;&lt;td&gt;(p)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Untransformed pixel in Y'CbCrA:&lt;/td&gt;&lt;td&gt;(RGBA-&gt;Y'CbCrA)*(p)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Transformed pixel in Y'CbCrA:&lt;/td&gt;&lt;td&gt;(M)*(RGBA-&gt;Y'CbCrA)*(p)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;Transformed pixel in RGBA:&lt;/td&gt;&lt;td&gt;(Y'CbCrA-&gt;RGBA)*(M)*(RGBA-&gt;Y'CbCrA)*(p)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;The matrices (RGBA-&gt;Y'CbCrA) and (Y'CbCrA-&gt;RGBA) are the ones which convert between RGBA and Y'CbCrA. Since matrix multiplication is &lt;a href="http://en.wikipedia.org/wiki/Associative"&gt;associative&lt;/a&gt; you can combine the 3 matrices to a single one during initialization:&lt;br /&gt;&lt;br /&gt;(M)&lt;sub&gt;RGBA&lt;/sub&gt; = (Y'CbCrA-&gt;RGBA)*(M)&lt;sub&gt;Y'CbCrA&lt;/sub&gt;*(RGBA-&gt;Y'CbCrA)&lt;br /&gt;&lt;br /&gt;If you have generic number-crunching routines, which to the vector-matrix multiplication for all supported  pixelformats, you can reduce conversion overhead in your processing pipeline significantly.&lt;br /&gt;&lt;br /&gt;Another trick is to combine multiple transformations in one matrix. The gmerlin video equalizer filter does this for brightness/contrast/saturation/hue rotation.&lt;br /&gt;&lt;br /&gt;A number of gmerlin filters use the colormatrix method. Of course, for &lt;i&gt;native&lt;/i&gt; pixelformats (image colorspace = matrix colorspace), the transformation is done directly, since it usually needs much less operations than a generic matrix-vector multiplication. But for &lt;i&gt;foreign&lt;/i&gt; colorspaces, the colormatrix is transformed to the image colorspace like described above.&lt;br /&gt;&lt;br /&gt;Some final remarks:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The colormatrix multiplication needs all channels for each pixel. Therefore it doesn't work with subsampled chroma planes. An exception is the brightness/contrast/saturation/hue filter, because luminance and chroma operations are completely separated here.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;For integer pixelformats the floating point matrix is converted to an integer matrix, where the actual ranges/offsets for Y'CbCr are taken into account.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In practically all cases, the color values can over- or underflow. The processing routines must do proper clipping.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-3347261149752655655?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/3347261149752655655/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=3347261149752655655' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/3347261149752655655'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/3347261149752655655'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/09/fun-with-colormatrix.html' title='Fun with the colormatrix'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-4549424625101840127</id><published>2008-09-21T19:44:00.003+02:00</published><updated>2008-09-21T19:52:11.183+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='travels'/><title type='text'>Back from California</title><content type='html'>My job sometimes requires me to attend international conferences and workshops. It's pretty exciting, but also very hard work. You have to prepare presentations, posters and articles for the conference digest. You must concentrate on others presentations (ignoring your jet lag) and the brain gets much more input per day than usual. Some people, who wish me a &lt;i&gt;nice holiday&lt;/i&gt; before I leave, don't really know what this is about :)&lt;br /&gt;&lt;br /&gt;This year, I had the honor to make a 2 weeks trip to Southern California, first to San Diego and then to Pasadena. It was my first visit to the US so here are some differences I noticed with respect to &lt;a href="http://en.wikipedia.org/wiki/Old_Europe"&gt;Old Europe&lt;/a&gt;. Of course these are highly subjective and based on what I saw in two weeks in just a tiny corner of a huge country.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Shops, Restaurants&lt;/b&gt;&lt;br /&gt;If you are used to the unfriendlyness of German shop employees and waiters, you'll be positively surprised in the US. They always have some friendly words for you. They might not be serious with that, but they do it well enough so it works. This is definitely something, where Germans can learn from the US.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Public transport&lt;/b&gt;&lt;br /&gt;As a resident of a big German city, I can live perfectly without owning a car. Of course people here always complain about the local trains being too expensive, finishing operation too early in the night etc. But this is still paradise compared the US. At the bus stops in San Diego I saw mostly people who didn't really look prosperous. It seems that everyone who can afford a car, buys one. For a reason.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;International news&lt;/b&gt;&lt;br /&gt;If you want to learn about a foreign society it's a good idea to watch their TV programs. I learned that American TV news (aside from being &lt;b&gt;extremely&lt;/b&gt; hysteric) mostly deal with domestic issues. I talked to an American colleague about that. I was quite surprised that he told me exactly, what I already thought: &lt;i&gt;Americans are self-centered&lt;/i&gt;. Maybe a bit of information about foreign countries and societies (especially the ones you plan to bomb) would make some things go more smoothly.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Freedom&lt;/b&gt;&lt;br /&gt;Both Western European societies and the US appreciate personal freedom. But the definitions of freedom seem to be somewhat different. In Europe you can usually drink alcohol in public and on many beaches you can decide yourself how much you wear. Americans want to be able to buy firearms, drive big cars and put their dishes into the trashcan after eating. In Europe, I don't miss any of the American freedoms. Not sure about the vice versa.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Shows&lt;/b&gt;&lt;br /&gt;Shows of any kind in the US have definitely another dimension. Germans seem to be way too modest to do something like the killer-whale show in the San Diego &lt;a href="http://en.wikipedia.org/wiki/Seaworld"&gt;Seaworld&lt;/a&gt; or the shows in the &lt;a href="http://en.wikipedia.org/wiki/Universal_Studios_Hollywood"&gt;Universal studios&lt;/a&gt; in Hollywood.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-4549424625101840127?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/4549424625101840127/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=4549424625101840127' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4549424625101840127'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4549424625101840127'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/09/back-from-california.html' title='Back from California'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-8339150799387186500</id><published>2008-09-07T19:31:00.015+02:00</published><updated>2008-09-07T20:26:12.436+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Image transformation</title><content type='html'>&lt;div style="text-align: left;"&gt;Lots of graphics and video effects can be described by a coordinate transformation: There is a rule for calculating destination coordinates from the source coordinates. The rule completely describes the type of transformation.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The most prominent family of transformations are the affine transforms, where the transform rule can be described by a vector matrix multiplication:&lt;br /&gt;&lt;br /&gt;(x&lt;sub&gt;dst&lt;/sub&gt;, y&lt;sub&gt;dst&lt;/sub&gt;) = (A) (x&lt;sub&gt;src&lt;/sub&gt;, y&lt;sub&gt;src&lt;/sub&gt;)&lt;br /&gt;&lt;br /&gt;With this one can implement e.g. scaling and rotation. If the vectors and matrix are given in &lt;a href="http://en.wikipedia.org/wiki/Homogeneous_coordinates"&gt;homogeneous coordinates&lt;/a&gt;, one can also shift the image by a vector. Other transforms can be Lens distortion, perspective distortion, wave effects and much more.&lt;br /&gt;&lt;br /&gt;Now the question: how can such a transform be implemented for digital images? The algorithm is straightforward:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Find the inverse transform. For affine transforms, it will simply be the inverse matrix. For other transforms it might not be that easy. The inverse transform will give you the source coordinates as a function of the destination coordinates&lt;/li&gt;&lt;li&gt;For each destination pixel, find the coordinates in the source image&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If the source coordinates are fractional (they usually will be), interpolate the destination pixel from the surrounding source pixels&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Implementation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Since the interpolation is always the same I decided to implement this in a generic way in gavl (&lt;code&gt;gavl_image_transform_t&lt;/code&gt;). With this we can implement lots of different transforms by just defining the inverse coordinate transform as a C-function and passing it to the interpolation engine. While the algorithm is simple in theory the implementation has to take care for some nasty details:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1. Y'CbCr formats with subsampled chroma planes&lt;/span&gt;&lt;br /&gt;In the video area, these are the rule rather than the exception. Many filters don't support them and use an RGB format instead (causing some conversion overhead). They can, however, be handled easily if you set up separate interpolation engines for each plane. For the subsampled planes, you do the following:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Transform the coordinates of the chroma location to image coordinates (i.e. multiply by the subsampling factors and shift according to the chroma placement)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Call the function to get the source coordinates the usual way&lt;/li&gt;&lt;li&gt;Transform the source coordinates back to the coordinates of the chroma plane&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;2. Destination pixels which are outside the source image&lt;/span&gt;&lt;br /&gt;This is e.g. the case where an image is downscaled. Gavl handles these by not touching the destination pixel at all. Then you can fill the destination frame with a color before the transformation and this color will be the background color later on.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3. Destination pixel is inside the source image, but surrounding pixels (needed for interpolation) are not&lt;/span&gt;&lt;br /&gt;Here, one can discuss a lot what should be done. For gavl, I decided to assume, that the "missing pixels" have the same color as the closest border pixel. The reason is, that instead of handling all possible cases inside the conversion loop for each pixel (which will slow things down due to the additional branches), one can simply shift the source indices and modify the interpolation coefficients once during initialization. The following figure illustrates, how this is done:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/SMQSn_FZrHI/AAAAAAAAABE/itk5Rkfpv1M/s1600-h/transform_border.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/SMQSn_FZrHI/AAAAAAAAABE/itk5Rkfpv1M/s400/transform_border.png" alt="" id="BLOGGER_PHOTO_ID_5243336344324385906" border="0" /&gt;&lt;/a&gt;The start index &lt;i&gt;n&lt;/i&gt; and the interpolation coefficients are saved in the interpolation table. After shifting the table, the interpolation routine works without branches (and without crashes). Due to the way the interpolation coefficients are modified we assume that the missing pixels at -2 and -1 are the same color as the border pixel at 0. Of course this is done for x and y directions and also for the case that indices are &lt;b&gt;larger&lt;/b&gt; than the maximum one.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Usage&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;The image transformation is very easy to use, just get the gavl from CVS and read the API documentation. There is also a gmerlin filter in CVS (&lt;code&gt;fv_transform&lt;/code&gt;) which can be used as reference. Some questions might however arise when using this:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1. How exactly are coordinates defined?&lt;/span&gt;&lt;br /&gt;Gavl scaling and transformation routines work with subpixel presision internally. This is necessary, if one wants to handle chroma placement correctly. To make everything correct one should think a bit how coordinates are exactly defined. This is an example for a 3x3 image:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_KslCmtD4qm8/SMQT_n8itTI/AAAAAAAAABM/0J3vbhdN4-Q/s1600-h/transform_coords.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_KslCmtD4qm8/SMQT_n8itTI/AAAAAAAAABM/0J3vbhdN4-Q/s400/transform_coords.png" alt="" id="BLOGGER_PHOTO_ID_5243337849941701938" border="0" /&gt;&lt;/a&gt;The sample values for each pixel are taken from the pixel center. This means, the top-left pixel has a color value corresponding to the location (0.5, 0.5). For chroma planes, the exact sample locations are considered as described &lt;a href="http://www.mir.com/DMG/chroma.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2. Handling of nonsquare pixels&lt;/span&gt;&lt;br /&gt;These must be handled by the coordinate transform routine provided by you. Basically, you have a "sample aspect ratio" (&lt;code&gt;sar = pixel_width / pixel_height&lt;/code&gt;). In your transformation function, you do something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;x_dst *= sar; /* Distorted -&gt; undistorted */&lt;br /&gt;&lt;br /&gt;/* Calculate source coordinate assuming undistorted image */&lt;br /&gt;&lt;br /&gt;x_src /= sar; /* Undistorted -&gt; distorted */&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3. Image scaling&lt;/span&gt;&lt;br /&gt;One is tempted to think, that this all-in-one solution can be used for scaling as well. It is, of course true, but it's a stupid thing to do. Scaling can be highly optimized in many ways. The &lt;code&gt;gavl_video_scaler_t&lt;/code&gt; does this. It's thus many times faster than the generic transform.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;4. Downsampling issues&lt;/span&gt;&lt;br /&gt;The image transform makes no assumptions about the type of the transform. Especially not if the transform corresponds to &lt;i&gt;downsampling&lt;/i&gt; or not. And this is where some issues arise. While for upsampling it's sufficient to just interpolate the destination pixels from the source pixels, for downsampling the image must be low-pass filtered (i.e. blurred) first. This is because otherwise the &lt;a href="http://en.wikipedia.org/wiki/Sampling_theorem"&gt;sampling theorem&lt;/a&gt; is violated and &lt;a href="http://en.wikipedia.org/wiki/Aliasing"&gt;aliasing&lt;/a&gt; occurs. A very scary example for this is discussed &lt;a href="http://www.xs4all.nl/%7Ebvdwolf/main/foto/down_sample/down_sample.htm"&gt;here&lt;/a&gt;. One more reason to use the &lt;code&gt;gavl_video_scaler_t&lt;/code&gt; wherever possible because it supports antialiasing filters for downscaling. The good news is that usual video material is already a bit blurry and aliasing artifacts are hardly visible.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Examples&lt;/span&gt;&lt;br /&gt;After this much theory, finally some examples. These images were made with the gmerlin transform filter (the original photo was taken in the South Indian ruin city of &lt;a href="http://en.wikipedia.org/wiki/Hampi"&gt;Hampi&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/SMQV-bphipI/AAAAAAAAABU/Fmf3PLtVvXQ/s1600-h/transform_perspective.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/SMQV-bphipI/AAAAAAAAABU/Fmf3PLtVvXQ/s400/transform_perspective.png" alt="" id="BLOGGER_PHOTO_ID_5243340028484094610" border="0" /&gt;&lt;/a&gt;Perspective effect. Coordinate transform ported from the &lt;a href="http://www.gimp.org/"&gt;Gimp&lt;/a&gt;.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/SMQW6KorX3I/AAAAAAAAABc/xmN5CZYPPEE/s1600-h/transform_rotate.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/SMQW6KorX3I/AAAAAAAAABc/xmN5CZYPPEE/s400/transform_rotate.png" alt="" id="BLOGGER_PHOTO_ID_5243341054709292914" border="0" /&gt;&lt;/a&gt;Rotation&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/SMQXWA3yfeI/AAAAAAAAABk/7rCfDEDZLGk/s1600-h/transform_whirl.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/SMQXWA3yfeI/AAAAAAAAABk/7rCfDEDZLGk/s400/transform_whirl.png" alt="" id="BLOGGER_PHOTO_ID_5243341533124656610" border="0" /&gt;&lt;/a&gt;Whirl/pinch (Coordinate transform ported from &lt;a href="http://heroinewarrior.com/"&gt;cinelerra&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/SMQYPulsV0I/AAAAAAAAABs/0fuVPqbbNMQ/s1600-h/transform_lens.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/SMQYPulsV0I/AAAAAAAAABs/0fuVPqbbNMQ/s400/transform_lens.png" alt="" id="BLOGGER_PHOTO_ID_5243342524649330498" border="0" /&gt;&lt;/a&gt;Lens distortion (Coordinate transform ported from &lt;a href="http://effectv.sourceforge.net/lens.html"&gt;EffecTV&lt;/a&gt;)&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_KslCmtD4qm8/SMQY4hUdWDI/AAAAAAAAAB0/QODXotg_Lwo/s1600-h/transform_affine.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_KslCmtD4qm8/SMQY4hUdWDI/AAAAAAAAAB0/QODXotg_Lwo/s400/transform_affine.png" alt="" id="BLOGGER_PHOTO_ID_5243343225462020146" border="0" /&gt;&lt;/a&gt;Generic affine (enter single matrix coefficients)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-8339150799387186500?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/8339150799387186500/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=8339150799387186500' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/8339150799387186500'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/8339150799387186500'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/09/image-transformation.html' title='Image transformation'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_KslCmtD4qm8/SMQSn_FZrHI/AAAAAAAAABE/itk5Rkfpv1M/s72-c/transform_border.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-4165530850375638399</id><published>2008-09-07T00:03:00.004+02:00</published><updated>2008-09-07T00:27:45.963+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Misc'/><category scheme='http://www.blogger.com/atom/ns#' term='computer'/><title type='text'>Music from everywhere - everywhere</title><content type='html'>Project goal was simple. Available audio sources are&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Vinyl records&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Analog tapes&lt;/li&gt;&lt;li&gt;CDs&lt;/li&gt;&lt;li&gt;Radio (analog and internet)&lt;/li&gt;&lt;li&gt;Music files in all gmerlin supported formats on 2 PCs&lt;/li&gt;&lt;/ul&gt;All these should be audible in stereo in all parts of the apartment including balcony and bathroom. Not everywhere at the same time though and not necessarily in high-end quality.&lt;br /&gt;&lt;br /&gt;This had been on my wish list for many years, but I was too lazy to lay cables through the whole apartment (especially through doors, which should be lockable). And since there are lots of signal paths, the result would have been a bit messy. Dedicated wireless audio solutions didn't really convince me, since they are mostly proprietary technology. I never want to become a victim of &lt;a href="http://en.wikipedia.org/wiki/Vendor_lock-in"&gt;Vendor lock-in&lt;/a&gt; (especially not at home).&lt;br /&gt;&lt;br /&gt;When I first read about the WLAN-radios I immediately got the idea, that those are the key to the solution. After researching a lot I found one &lt;a href="http://www.terratec.net/en/products/NOXON_iRadio_2212.html"&gt;radio&lt;/a&gt; which has all features I wanted:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Stereo (if you buy a second speaker)&lt;/li&gt;&lt;li&gt;Custom URLs can be added through a web interface (many WLAN radios don't allow this!)&lt;/li&gt;&lt;li&gt;Ogg Vorbis support (so I can use &lt;a href="http://www.icecast.org/"&gt;Icecast2/ices2&lt;/a&gt; out of the box)&lt;/li&gt;&lt;/ul&gt;The block diagram of the involved components is here:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/SML_c3f9jYI/AAAAAAAAAA8/fNQCMYs6Ym8/s1600-h/wlanradio.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/SML_c3f9jYI/AAAAAAAAAA8/fNQCMYs6Ym8/s400/wlanradio.png" alt="" id="BLOGGER_PHOTO_ID_5243033787612106114" border="0" /&gt;&lt;/a&gt;Now I had to set up the streaming servers. The icecast server itself installs flawlessly on Ubuntu 8.04. It's started automatically during booting. For encoding and sending the stream to icecast, I use ices2 from the commandline. 2 tiny problem had to be solved:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Ubuntu 8.04 uses &lt;a href="http://www.pulseaudio.org/"&gt;PulseAudio&lt;/a&gt; as the sound server, while ices2 only supports Alsa. Recording from an Alsa hardware device while PulseAudio is running doesn't work.&lt;/li&gt;&lt;li&gt;For grabbing the audio from a running gmerlin player the soundcard and driver need to support loopback (i.e. record what's played back). This is the case for the Audigy soundcard in the Media PC, but not for the onboard soundcard in the desktop machine.&lt;/li&gt;&lt;/ul&gt;Both problems can be solved by defining pulse devices in the &lt;code&gt;~/.asoundrc&lt;/code&gt; file:&lt;br /&gt;&lt;pre&gt;pcm.pulse&lt;br /&gt;{&lt;br /&gt;type pulse&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;pcm.pulsemon&lt;br /&gt;{&lt;br /&gt;type pulse&lt;br /&gt;device alsa_output.pci_8086_293e_sound_card_0_alsa_playback_0.monitor&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;ctl.pulse&lt;br /&gt;{&lt;br /&gt;type pulse&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;The Alsa devices to be written into the ices2 configuration files are called &lt;code&gt;pulse&lt;/code&gt; and &lt;code&gt;pulsemon&lt;/code&gt;. The &lt;code&gt;device&lt;/code&gt; line is hardware dependent. Use the &lt;i&gt;PulseAudio Manager&lt;/i&gt; (section Devices) to find out the corresponding name for your card. If your card and driver support loopback, the pulsemon device isn't necessary.&lt;br /&gt;&lt;br /&gt;Some fine-tuning can be done regarding encoding bitrate, buffer sizes and timeouts. When I optimized everything for low latency, icecast considered the WLAN radio too slow and disconnected it. More conservative settings work better. Encoding quality is always 10 (maximum), the corresponding bitrate is around 500 kbit/s.&lt;br /&gt;&lt;br /&gt;Mission accomplished&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-4165530850375638399?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/4165530850375638399/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=4165530850375638399' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4165530850375638399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/4165530850375638399'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/09/music-from-everywhere-everywhere.html' title='Music from everywhere - everywhere'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_KslCmtD4qm8/SML_c3f9jYI/AAAAAAAAAA8/fNQCMYs6Ym8/s72-c/wlanradio.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-25714556678442827</id><published>2008-09-06T01:36:00.004+02:00</published><updated>2008-09-06T02:20:52.382+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>gcc visibility</title><content type='html'>Newer versions of gcc (e.g. gcc-4.2.3 coming with Ubuntu-8.04) support a feature called &lt;i&gt;visibility&lt;/i&gt;. It is explained in detail &lt;a href="http://gcc.gnu.org/wiki/Visibility"&gt;here&lt;/a&gt;. In essence, it lets you declare functions only within the scope of a shared library. The advantages are:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Since these functions are not callable from outside, the dynamic symbol table becomes smaller. This speeds up loading (especially in the debugger I assume) and makes a smaller library.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;If gcc knows at compile-time, that a called function will be in the same shared lib, it can optimize the function call.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;My test candidate for this was libquicktime, because it contains lots of private functions (e.g those in lqt_funcprotos.h). Supporting visibility is easy, this is what I did:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Let the configure script append -fvisibility=hidden to the CFLAGS if that's supported by gcc.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Put the function declarations in public headers between&lt;br /&gt;&lt;br /&gt;&lt;code&gt;#pragma GCC visibility push(default)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;and&lt;br /&gt;&lt;br /&gt;&lt;code&gt;#pragma GCC visibility pop&lt;/code&gt;.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;If there are functions called from outside (e.g. from libquicktime codecs) declared in private headers, add&lt;br /&gt;&lt;br /&gt;&lt;code&gt;__attribute__ ((visibility("default")))&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;to their declarations.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;In the libquicktime case, this change also revealed some dirty corners (which I cleaned up after compiling with &lt;code&gt;-Wmissing-declarations&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;Let's see, what actually changed:&lt;br /&gt;&lt;br /&gt;1. Entries in the dynamic symbol table:&lt;br /&gt;&lt;code&gt;# nm -D /opt/gmerlin/lib/libquicktime.so.0.0.0 | grep " T " | wc -l&lt;/code&gt;&lt;br /&gt;Before: 866&lt;br /&gt;After: 278&lt;br /&gt;&lt;br /&gt;2. Size of the shared library (compiled with standard settings):&lt;br /&gt;Before: 622K&lt;br /&gt;After: 564K&lt;br /&gt;&lt;br /&gt;I did some rough speed tests with &lt;code&gt;time qtinfo some_file.mov&lt;/code&gt;, but the differences were too little and the measurement method too inaccurate to be significant.&lt;br /&gt;&lt;br /&gt;Let's see how this settles down. Doing the same for the gmerlin libraries will be much easier, because they are much cleaner already :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-25714556678442827?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/25714556678442827/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=25714556678442827' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/25714556678442827'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/25714556678442827'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/09/gcc-visibility.html' title='gcc visibility'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-1009362106968117255</id><published>2008-09-05T01:14:00.006+02:00</published><updated>2008-09-05T01:31:28.040+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='electronics'/><title type='text'>Make an analog 5.1 switch using scart components</title><content type='html'>The project had 3 clear goals:&lt;br /&gt;&lt;br /&gt;1. Remove the disastrous cable mess around the Hifi-system and TV&lt;br /&gt;&lt;br /&gt;2. Avoid repeatedly creeping into dark corners to reconnect the cables (always increasing the mess mentioned in 1)&lt;br /&gt;&lt;br /&gt;3. Do 1 and 2 without decreasing functionality of the whole system&lt;br /&gt;&lt;br /&gt;After considering all necessary signal-paths, it turned out, that one component was missing: An analog 5.1 switch. My 5.1 receiver (from Yamaha) is almost 10 years old now, but it still works perfectly. Unfortunately, it has only one 5.1 input (and lots of stereo inputs). Now, I have 2 sources of 5.1 audio: The DVD player and the Media PC.&lt;br /&gt;&lt;br /&gt;And to make things worse: Many stereo movies on the PC (e.g. rips from VHS tapes) are encoded with the old &lt;a href="http://en.wikipedia.org/wiki/Dolby_Surround"&gt;Dolby Surround&lt;/a&gt; method. The Yamaha receiver handles them perfectly, but only when the signal comes into a stereo input. This means that the stereo channels form the 5.1 signal must also be routed to a stereo input of the receiver.&lt;br /&gt;&lt;br /&gt;So I was searching for something, which does the same as the green box in the following figure (the AUX channel is reserved for future extensions):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/SMBtWlKgJBI/AAAAAAAAAAM/Bll11h_TiXU/s1600-h/51_schematic.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/SMBtWlKgJBI/AAAAAAAAAAM/Bll11h_TiXU/s400/51_schematic.png" alt="" id="BLOGGER_PHOTO_ID_5242310200959968274" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Googling around revealed only one &lt;a href="http://www.dodocus.de/UBox51C.htm"&gt;solution&lt;/a&gt;, which is unacceptable because it only switches between 2 sources, needs a power supply (violating goal 1) and costs € 258. All other search results were people saying that this sucks.&lt;br /&gt;&lt;br /&gt;The question was, if some other device can be tricked into doing exactly what I want, and the answer was yes. The idea is to (ab)use the video wires of &lt;a href="http://en.wikipedia.org/wiki/Scart"&gt;scart&lt;/a&gt;-cables for the additional audio channels. So I got 3 adapters with one scart connector and 6 cinch connectors each:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_KslCmtD4qm8/SMBt_9F6GGI/AAAAAAAAAAU/c9xUb2Xl4VU/s1600-h/51_adaptor.JPG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_KslCmtD4qm8/SMBt_9F6GGI/AAAAAAAAAAU/c9xUb2Xl4VU/s400/51_adaptor.JPG" alt="" id="BLOGGER_PHOTO_ID_5242310911757785186" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;And a scart switch-box:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_KslCmtD4qm8/SMBucCbGriI/AAAAAAAAAAc/gcqVHa_176Y/s1600-h/51_box.JPG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_KslCmtD4qm8/SMBucCbGriI/AAAAAAAAAAc/gcqVHa_176Y/s400/51_box.JPG" alt="" id="BLOGGER_PHOTO_ID_5242311394225204770" border="0" /&gt;&lt;/a&gt;The funny thing about the switch box is, that the stereo channels are available at additional cinch connectors (right side), so I can route front L/R to a stereo input of my receiver, like I wanted.&lt;br /&gt;&lt;br /&gt;Mission accomplished.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-1009362106968117255?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/1009362106968117255/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=1009362106968117255' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1009362106968117255'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/1009362106968117255'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/09/make-analog-51-switch-using-scart.html' title='Make an analog 5.1 switch using scart components'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_KslCmtD4qm8/SMBtWlKgJBI/AAAAAAAAAAM/Bll11h_TiXU/s72-c/51_schematic.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2134600781755905611.post-7526634994445251525</id><published>2008-09-04T21:44:00.003+02:00</published><updated>2008-09-06T16:52:57.507+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Misc'/><title type='text'>The future from the last century</title><content type='html'>When I was younger (in the 70s), I spent a lot of time reading books about the exciting things to come in the year 2000 and beyond. We were supposed to have at least one permanent moon base, public transport with &lt;a href="http://en.wikipedia.org/wiki/Cabinentaxi"&gt;Cabin taxis&lt;/a&gt; and underwater cities. And of course peace, happiness and prosperity all over the planet.&lt;br /&gt;&lt;br /&gt;Today, it's a bit disappointing to see that none of these things actually became true. Humans seem to be most innovative when it comes to better &lt;a href="http://www.newscientist.com/article.ns?id=mg18725095.600"&gt;weapons&lt;/a&gt; or more effective &lt;a href="http://en.wikipedia.org/wiki/Vendor_lock-in"&gt;Vendor lock-in&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Technical advances must go hand in hand with advances of the human mind; everything else is doomed to failure. Maybe the internet (which was &lt;b&gt;not&lt;/b&gt; mentioned in my children's books) can help here.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2134600781755905611-7526634994445251525?l=hirntier.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hirntier.blogspot.com/feeds/7526634994445251525/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2134600781755905611&amp;postID=7526634994445251525' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7526634994445251525'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2134600781755905611/posts/default/7526634994445251525'/><link rel='alternate' type='text/html' href='http://hirntier.blogspot.com/2008/09/future-from-last-century.html' title='The future from the last century'/><author><name>burkhard</name><uri>http://www.blogger.com/profile/17023643103552829447</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_KslCmtD4qm8/SWaNnovrnRI/AAAAAAAAAFU/cQc8MWznsGA/S220/gmerlin.jpg'/></author><thr:total>0</thr:total></entry></feed>
