weinimo.github.iohttps://weinimo.github.io/2013-04-13T20:26:00+02:00How to Write udev Rules for USB Devices2013-04-13T20:26:00+02:002013-04-13T20:26:00+02:00Thomas Weiningertag:weinimo.github.io,2013-04-13:/how-to-write-udev-rules-for-usb-devices.html<p><img alt="Udev-tux" src="https://upload.wikimedia.org/wikipedia/de/d/da/Udev-tux.png" />If you use a custom USB device for which there isn't a
suitable udev rule installed on your system yet you might notice that
only the root user has read and write access for it. In order to make it
usable for normal users you need to write a new …</p><p><img alt="Udev-tux" src="https://upload.wikimedia.org/wikipedia/de/d/da/Udev-tux.png" />If you use a custom USB device for which there isn't a
suitable udev rule installed on your system yet you might notice that
only the root user has read and write access for it. In order to make it
usable for normal users you need to write a new udev rule. Here is an
easy way how to do this.</p>
<p>First we have to collect some information about that device. As we have
an USB device in this example, we get some basic information from
<tt class="docutils literal">lsusb</tt>. Its output must look something like this:</p>
<p><tt class="docutils literal">Bus 001 Device 003: ID 1111:2222 FooDev</tt></p>
<p>Now we know where in the Linux filesystem that device has been mounted:</p>
<p><tt class="docutils literal">/dev/bus/usb/001/003</tt></p>
<p>We can use this path now to ask udev about how it sees the device:</p>
<p><tt class="docutils literal">udevadm info <span class="pre">-a</span> <span class="pre">-p</span> $(udevadm info <span class="pre">-q</span> path <span class="pre">-n</span> /dev/bus/usb/001/003)</tt></p>
<p>Maybe you have to use <tt class="docutils literal">udevinfo</tt> instead of <tt class="docutils literal">udevadm info</tt>. The
output should look like this:</p>
<pre class="code text literal-block">
looking at device '/devices/pci0000:00/0000:00:06.0/usb1/1-2':
KERNEL=="1-2"
SUBSYSTEM=="usb"
DRIVER=="usb"
ATTR{bDeviceSubClass}=="00"
ATTR{bDeviceProtocol}=="00"
ATTR{devpath}=="2"
ATTR{idVendor}=="1111"
ATTR{speed}=="12"
ATTR{bNumInterfaces}==" 1"
ATTR{bConfigurationValue}=="1"
ATTR{bMaxPacketSize0}=="64"
ATTR{busnum}=="1"
ATTR{devnum}=="3"
ATTR{configuration}==""
ATTR{bMaxPower}=="500mA"
ATTR{authorized}=="1"
ATTR{bmAttributes}=="80"
ATTR{bNumConfigurations}=="1"
ATTR{maxchild}=="0"
ATTR{bcdDevice}=="0106"
ATTR{avoid_reset_quirk}=="0"
ATTR{quirks}=="0x0"
ATTR{serial}==" 10T7371 9028039"
ATTR{version}==" 1.10"
ATTR{urbnum}=="10"
ATTR{ltm_capable}=="no"
ATTR{manufacturer}=="Foo Inc"
ATTR{removable}=="unknown"
ATTR{idProduct}=="2222"
ATTR{bDeviceClass}=="00"
ATTR{product}=="Some FooDev"
</pre>
<p>With this information we can finally start writing the new udev rule. We do this
by creating a new file:</p>
<p><tt class="docutils literal">vim <span class="pre">/etc/udev/rules.d/10-local.rules</span></tt></p>
<p>For instance if we would like to give everyone in the group "users" read
and write access to the device, we put following line in that file:</p>
<p><tt class="docutils literal"><span class="pre">SUBSYSTEMS=="usb",</span> <span class="pre">ATTRS{product}=="Some</span> FooDev", <span class="pre">GROUP="users"</span></tt></p>
<p>Please note that it's ATTRS in the rules file, not ATTR.</p>
<p>Now we can test the new rule. For this we need the /devices/... path
from the <tt class="docutils literal">udevadm info</tt> output from before.</p>
<p><tt class="docutils literal">udevadm test <span class="pre">/devices/pci0000:00/0000:00:06.0/usb1/1-2</span></tt></p>
<p>Like before, you might have to use <tt class="docutils literal">udevtest</tt> instead of
<tt class="docutils literal">udevadm test</tt>. The output should also contain information about the
permissions for this device:</p>
<p><tt class="docutils literal"><span class="pre">[...]</span> handling device node '/dev/bus/usb/001/003', devnum=c189:2, mode=0664, uid=0, gid=100 set permissions /dev/bus/usb/001/003, 020664, uid=0, gid=100 <span class="pre">[...]</span></tt></p>
<p>gid=100 is the group id of the "users" group. So this is the proof that
it works.</p>
<p>If your rule was not applied although it seems to be correct, chances
are good that it has been overridden by another rule. In that case use
the ":=" operator instead of "=". This tells udev not to override this
property.</p>
NGINX Configuration for Gitweb and git-http-backend2012-12-15T13:55:00+01:002012-12-15T13:55:00+01:00Thomas Weiningertag:weinimo.github.io,2012-12-15:/configuration-of-nginx-for-gitweb-and-git-http-backend.html<p>Gitweb is a nice web interface for Git repositories. For instance go
to <a class="reference external" href="http://git.kernel.org/">http://git.kernel.org/</a> to see how it looks like. I find it useful
especially when I'm using the machine of somebody else and need to check out
some files from one of my own repositories. git-http-backend …</p><p>Gitweb is a nice web interface for Git repositories. For instance go
to <a class="reference external" href="http://git.kernel.org/">http://git.kernel.org/</a> to see how it looks like. I find it useful
especially when I'm using the machine of somebody else and need to check out
some files from one of my own repositories. git-http-backend allows me
to clone git repositories over HTTPS so I don't have to use SSH.</p>
<p>First you have to prepare your repositories on your server for accessing
them via HTTPS. I would recommend to create an extra user "git" for it.
In this example I put my repositories to /home/git/repositories.
Basically you can put them where you want.</p>
<pre class="code sh literal-block">
useradd -m git
su git
git clone --bare /old/repo.git /home/git/repositories/repo.git
<span class="nb">cd</span> /home/git/repositories/repo.git
sudo chmod -R g+ws . <span class="c1"># Setting necessary rights for pushing to the repository.
</span>sudo chgrp -R git .
</pre>
<p>Now configure your repositories:</p>
<pre class="code sh literal-block">
git --bare update-server-info
cp hooks/post-update.sample hooks/post-update
chmod a+x hooks/post-update
</pre>
<p>This generates all the information that is necessary to share the
repository using a webserver like NGINX.</p>
<p>I found a nice tutorial about how to set up NGINX for gitweb and
git-http-backend <a class="reference external" href="http://eatabrick.org/20120126_gitweb_nginx.html">here</a>. It almost worked out-of-the-box for me. I just
had to add following lines to fix some errors I got when I tried to work
with git-http-backend.</p>
<pre class="code text literal-block">
fastcgi_param GIT_HTTP_EXPORT_ALL "";
fastcgi_param REMOTE_USER $remote_user;
</pre>
<p>Furthermore, I have added the auth_basic lines to restrict the access to
my repositories. The configuration shown below has been tested with
Ubuntu 12.04.</p>
<p>But first make sure you have all required packages installed:</p>
<pre class="code text literal-block">
(sudo) apt-get install git gitweb fcgiwrap
</pre>
<p>Here is my NGINX configuration file for Gitweb and git-http-backend. It
allows access only using HTTPS and asks for authentication both for the
web interface and for cloning the repositories. It works basically like
the .htaccess authentication mechanism from Apache.</p>
<pre class="code text literal-block">
server {
listen 80;
server_name git.weinimo.de;
access_log /var/log/nginx/git.weinimo.de.access.log;
rewrite ^ https://$server_name$request_uri? permanent;
}
# HTTPS server
#
server {
listen 443;
server_name git.weinimo.de;
root /usr/share/gitweb;
access_log /var/log/nginx/git.weinimo.de.access.log;
ssl on;
ssl_certificate /etc/ssl/certs/certforyoursite.crt;
ssl_certificate_key /etc/ssl/private/sitekey.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!ADH:!MD5;
ssl_prefer_server_ciphers on;
auth_basic "RESTRICTED ACCESS";
auth_basic_user_file /etc/nginx/access_list;
# static repo files for cloning over https
location ~ ^.*\.git/objects/([0-9a-f]+/[0-9a-f]+|pack/pack-[0-9a-f]+.(pack|idx))$ {
root /home/git/repositories/;
}
# requests that need to go to git-http-backend
location ~ ^.*\.git/(HEAD|info/refs|objects/info/.*|git-(upload|receive)-pack)$ {
root /home/git/repositories;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
fastcgi_param PATH_INFO $uri;
fastcgi_param GIT_PROJECT_ROOT /home/git/repositories;
fastcgi_param GIT_HTTP_EXPORT_ALL "";
fastcgi_param REMOTE_USER $remote_user;
include fastcgi_params;
}
# send anything else to gitweb if it's not a real file
try_files $uri @gitweb;
location @gitweb {
fastcgi_pass unix:/var/run/fcgiwrap.socket;
fastcgi_param SCRIPT_FILENAME /usr/share/gitweb/gitweb.cgi;
fastcgi_param PATH_INFO $uri;
fastcgi_param GITWEB_CONFIG /etc/gitweb.conf;
include fastcgi_params;
}
}
</pre>
<div class="line-block">
<div class="line"><strong>Update1:</strong> I had to add the line fastcgi_param REMOTE_USER</div>
<div class="line">$remote_user; to the NGINX configuration to fix the 403 errors I got</div>
<div class="line">when trying to push changes to the server. This is necessary because I</div>
<div class="line">use HTTP authentification. I also added some commands for preparing the</div>
<div class="line">repositories for git-http-backend.</div>
</div>
<div class="line-block">
<div class="line"><strong>Update2:</strong> Added a section for setting up the repository file modes</div>
<div class="line">to prevent the "remote: error: insufficient permission for adding an</div>
<div class="line">object to repository database ./objects" error when trying to push to</div>
<div class="line">the repository.</div>
</div>
<div class="line-block">
<div class="line"><strong>Update3:</strong> Disabled SSL and enabled TLS1.1 and TLS1.2 support.</div>
<div class="line">Thanks for your comment itefixnet.</div>
</div>
Streaming video files to Samsung TVs (Part 2)2012-06-15T20:49:00+02:002012-06-15T20:49:00+02:00Thomas Weiningertag:weinimo.github.io,2012-06-15:/streaming-video-files-to-samsung-tvs-part-2.html<p>The second (and final) part of my article series about streaming video
files to Samsung TVs describes which video and audio codecs as well as
which container formats to choose for playing back video files on
Samsung devices (tested with UE40D6200). Furthermore I'll show you a
Ruby program I wrote …</p><p>The second (and final) part of my article series about streaming video
files to Samsung TVs describes which video and audio codecs as well as
which container formats to choose for playing back video files on
Samsung devices (tested with UE40D6200). Furthermore I'll show you a
Ruby program I wrote for transcoding files automatically to the right
format.</p>
<p>I have many files like Flash video files from YouTube, recorded MPEG2
transport streams from my PVR and other media files my TV doesn't
understand or doesn't support very well. Unfortunately, many of them
must be transcoded to another format which is more suitable for the TVs
DLNA function. To find the best formats for the conversation I've
started a series of tests. I took a video file and transcoded it using
many different video and audio codecs and container formats and checked
their compatibility with Samsungs SmartHub. To keep this article short,
I show you only the summary of my tests instead of the full results. In
short that is:</p>
<p>Video codecs that don't need to be converted:</p>
<ul class="simple">
<li>H264</li>
<li>MPEG4</li>
<li>WMV3</li>
</ul>
<p>Audio codecs that don't need to be converted:</p>
<ul class="simple">
<li>MP3</li>
<li>AAC</li>
<li>AC3</li>
</ul>
<p>My investigation showed that both the Matroska (.mkv) and the MPEG4
(.mp4) container formats are most suiteable for my scenario.</p>
<p>Above stated rules are simplified as there are also other constraints to
consider when trying to make ones video collection ready for streaming.
I wrote a sweet little Ruby program that is much more sophisticated and
takes care of most codecs and handles them the right way automatically.
It scans given paths or single video files and converts them if
necessary. One goal while designing the tool was to keep the original
quality of the files whenever possible. To achieve this it handles the
audio and video stream of the file independently from each other and
transcodes only those parts that need to be converted. So for instance
if the video stream needs to be converted, but the audio stream does
not, it only changes the video stream and takes the audio track from the
original file.</p>
<p>My program depends on FFMPEG and the Ruby package <a class="reference external" href="https://github.com/streamio/streamio-ffmpeg">streamio-ffmpeg</a>.
You can install it using gem via:</p>
<pre class="code text literal-block">
(sudo) gem install streamio-ffmpeg
</pre>
<p>It's also important that your FFMPEG installation supports libfaac
encoding. To check this simply type "ffmpeg" in your terminal. If there
is a line "libfaac" with an "E" in one of the columns before the codec
name, then everything is fine.</p>
<p>I think the code is pretty selfexplaining. It's far from perfect, but in
practice it works very well. Usage is simple:</p>
<pre class="code text literal-block">
ffautoconv.rb <file(s)/path(s) to scan>
</pre>
<p>It writes a logfile to /tmp/ffautoconv.log while running so it's
possible to reconstruct what the program was doing.</p>
<p>Please leave a comment if you have a suggestion or simply found my
program useful. Here's the download link: <a class="reference external" href="http://weininger.net/wp-content/uploads/2012/06/ffautoconv.gz">ffautoconv</a>.</p>
<pre class="code ruby literal-block">
<span class="ch">#!/usr/bin/env ruby</span>
<span class="c1"># ffautoconv.rb: Program for transcoding videos for streaming them to Samsung TVs.</span>
<span class="c1"># Author: Thomas Weininger (http://blog.weinimo.de)</span>
<span class="c1"># This program is free software: you can redistribute it and/or modify it under the terms of the BSD license.</span>
<span class="nb">require</span> <span class="s1">'find'</span>
<span class="nb">require</span> <span class="s1">'rubygems'</span>
<span class="nb">require</span> <span class="s1">'streamio-ffmpeg'</span>
<span class="k">def</span> <span class="nf">containerChange</span> <span class="p">(</span><span class="n">movie</span><span class="p">,</span> <span class="n">filename</span><span class="p">,</span> <span class="n">container</span><span class="p">)</span>
<span class="k">if</span> <span class="no">File</span><span class="o">.</span><span class="n">extname</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="o">!=</span> <span class="s2">"."</span> <span class="o">+</span> <span class="n">container</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"New container of </span><span class="si">#{</span><span class="n">filename</span><span class="si">}</span><span class="s2"> will be: </span><span class="si">#{</span><span class="n">container</span><span class="si">}</span><span class="s2">.
</span><span class="se">\n</span><span class="s2"> Original file: </span><span class="si">#{</span><span class="n">movie</span><span class="o">.</span><span class="n">video_codec</span><span class="si">}</span><span class="s2">, acodec: </span><span class="si">#{</span><span class="n">movie</span><span class="o">.</span><span class="n">audio_codec</span><span class="si">}</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="n">newfilename</span> <span class="o">=</span> <span class="n">filename</span><span class="o">.</span><span class="n">chomp</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">extname</span><span class="p">(</span><span class="n">filename</span><span class="p">))</span> <span class="o">+</span> <span class="s2">".</span><span class="si">#{</span><span class="n">container</span><span class="si">}</span><span class="s2">"</span>
<span class="n">movie</span><span class="o">.</span><span class="n">transcode</span><span class="p">(</span><span class="n">newfilename</span><span class="p">,</span> <span class="s2">"-vcodec copy -acodec copy -copyts"</span><span class="p">)</span>
<span class="c1"># Check whether container change was successful.</span>
<span class="n">newmovie</span> <span class="o">=</span> <span class="no">FFMPEG</span><span class="o">::</span><span class="no">Movie</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">newfilename</span><span class="p">)</span>
<span class="k">if</span> <span class="n">newmovie</span><span class="o">.</span><span class="n">valid?</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">" Container format changed successfully. Deleting "</span> <span class="o">+</span> <span class="n">filename</span> <span class="o">+</span> <span class="s2">".</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">)</span>
<span class="no">File</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
<span class="k">else</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">" Error occured while trying to change the container format. Deleting "</span> <span class="o">+</span> <span class="n">newfilename</span> <span class="o">+</span> <span class="s2">".</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">)</span>
<span class="no">File</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">newfilename</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">else</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"Container of "</span> <span class="o">+</span> <span class="n">filename</span> <span class="o">+</span> <span class="s2">" doesn't have to be changed"</span> <span class="o">+</span>
<span class="s2">".</span><span class="se">\n</span><span class="s2"> Original file: </span><span class="si">#{</span><span class="n">movie</span><span class="o">.</span><span class="n">video_codec</span><span class="si">}</span><span class="s2">, acodec: </span><span class="si">#{</span><span class="n">movie</span><span class="o">.</span><span class="n">audio_codec</span><span class="si">}</span><span class="s2">.</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">reencode</span> <span class="p">(</span><span class="n">movie</span><span class="p">,</span> <span class="n">filename</span><span class="p">,</span> <span class="n">acodec</span><span class="p">,</span> <span class="n">vcodec</span><span class="p">,</span> <span class="n">container</span><span class="p">)</span>
<span class="c1"># Decide whether we need to transcode or only a container change.</span>
<span class="k">if</span> <span class="n">movie</span><span class="o">.</span><span class="n">audio_codec</span><span class="o">.</span><span class="n">nil?</span> <span class="c1"># Prevent NoMethodError if no audio track is available.</span>
<span class="n">orig_acodec</span> <span class="o">=</span> <span class="s2">""</span>
<span class="k">else</span>
<span class="n">orig_acodec</span> <span class="o">=</span> <span class="n">movie</span><span class="o">.</span><span class="n">audio_codec</span>
<span class="k">end</span>
<span class="k">if</span> <span class="p">(</span> <span class="p">(</span> <span class="n">orig_acodec</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sr">/^</span><span class="si">#{</span><span class="n">acodec</span><span class="si">}</span><span class="sr">(.*)/</span><span class="p">)</span> <span class="o">&&</span> <span class="n">movie</span><span class="o">.</span><span class="n">video_codec</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sr">/^</span><span class="si">#{</span><span class="n">vcodec</span><span class="si">}</span><span class="sr">(.*)/</span><span class="p">)</span> <span class="p">)</span> <span class="o">||</span>
<span class="p">(</span> <span class="n">acodec</span> <span class="o">==</span> <span class="s2">"copy"</span> <span class="o">&&</span> <span class="n">vcodec</span> <span class="o">==</span> <span class="s2">"copy"</span> <span class="p">)</span> <span class="p">)</span>
<span class="n">containerChange</span><span class="p">(</span><span class="n">movie</span><span class="p">,</span> <span class="n">filename</span><span class="p">,</span> <span class="n">container</span><span class="p">)</span>
<span class="k">else</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"New Codecs of </span><span class="si">#{</span><span class="n">filename</span><span class="si">}</span><span class="s2"> will be: </span><span class="si">#{</span><span class="n">vcodec</span><span class="si">}</span><span class="s2">, </span><span class="si">#{</span><span class="n">acodec</span><span class="si">}</span><span class="s2">, </span><span class="si">#{</span><span class="n">container</span><span class="si">}</span><span class="s2">.</span><span class="se">\n</span><span class="s2">"</span> <span class="o">+</span>
<span class="s2">" Original file: </span><span class="si">#{</span><span class="n">movie</span><span class="o">.</span><span class="n">video_codec</span><span class="si">}</span><span class="s2">, acodec: </span><span class="si">#{</span><span class="n">movie</span><span class="o">.</span><span class="n">audio_codec</span><span class="si">}</span><span class="s2">.</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">flush</span>
<span class="n">newfilename</span> <span class="o">=</span> <span class="n">filename</span><span class="o">.</span><span class="n">chomp</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">extname</span><span class="p">(</span><span class="n">filename</span><span class="p">))</span> <span class="o">+</span> <span class="s2">".</span><span class="si">#{</span><span class="n">container</span><span class="si">}</span><span class="s2">"</span>
<span class="n">options</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">:threads</span> <span class="o">=></span> <span class="mi">0</span> <span class="p">}</span>
<span class="n">customopts</span> <span class="o">=</span> <span class="s2">""</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">movie</span><span class="o">.</span><span class="n">video_codec</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sr">/^</span><span class="si">#{</span><span class="n">vcodec</span><span class="si">}</span><span class="sr">(.*)/</span><span class="p">)</span> <span class="o">||</span> <span class="n">vcodec</span> <span class="o">==</span> <span class="s2">"copy"</span> <span class="p">)</span> <span class="c1"># Can we use the video track from the original file?</span>
<span class="n">customopts</span> <span class="o">=</span> <span class="s2">"</span><span class="si">#{</span><span class="n">customopts</span><span class="si">}</span><span class="s2"> -copyts"</span>
<span class="n">options</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">merge</span><span class="p">({</span> <span class="ss">:video_codec</span> <span class="o">=></span> <span class="s2">"copy"</span><span class="p">,</span> <span class="ss">:custom</span> <span class="o">=></span> <span class="n">customopts</span> <span class="p">})</span>
<span class="k">else</span>
<span class="k">case</span> <span class="n">vcodec</span>
<span class="k">when</span> <span class="s2">"h264"</span>
<span class="n">customopts</span> <span class="o">=</span> <span class="s2">"</span><span class="si">#{</span><span class="n">customopts</span><span class="si">}</span><span class="s2"> -qscale 0"</span>
<span class="n">options</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">merge</span><span class="p">({</span> <span class="ss">:video_codec</span> <span class="o">=></span> <span class="s2">"libx264"</span><span class="p">,</span> <span class="ss">:custom</span> <span class="o">=></span> <span class="n">customopts</span> <span class="p">})</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">orig_acodec</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="sr">/^</span><span class="si">#{</span><span class="n">acodec</span><span class="si">}</span><span class="sr">(.*)/</span><span class="p">)</span> <span class="o">||</span> <span class="n">acodec</span> <span class="o">==</span> <span class="s2">"copy"</span> <span class="p">)</span> <span class="c1"># Can we use the audio track from the original file?</span>
<span class="n">options</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">merge</span><span class="p">({</span> <span class="ss">:audio_codec</span> <span class="o">=></span> <span class="s2">"copy"</span> <span class="p">})</span>
<span class="k">else</span>
<span class="k">case</span> <span class="n">acodec</span>
<span class="k">when</span> <span class="s2">"aac"</span>
<span class="n">customopts</span> <span class="o">=</span> <span class="s2">"</span><span class="si">#{</span><span class="n">customopts</span><span class="si">}</span><span class="s2"> -aq 120"</span>
<span class="n">options</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">merge</span><span class="p">({</span> <span class="ss">:audio_codec</span> <span class="o">=></span> <span class="s2">"libfaac"</span><span class="p">,</span> <span class="ss">:custom</span> <span class="o">=></span> <span class="n">customopts</span> <span class="p">})</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">begin</span>
<span class="nb">puts</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">Transcoding: </span><span class="si">#{</span><span class="n">filename</span><span class="si">}</span><span class="s2">.</span><span class="se">\n</span><span class="s2">"</span>
<span class="n">beginning_time</span> <span class="o">=</span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span>
<span class="n">movie</span><span class="o">.</span><span class="n">transcode</span><span class="p">(</span><span class="n">newfilename</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">progress</span><span class="o">|</span> <span class="nb">print</span> <span class="s2">"</span><span class="se">\r</span><span class="si">#{</span><span class="nb">Integer</span><span class="p">(</span><span class="n">progress</span><span class="o">*</span><span class="mi">100</span><span class="p">)</span><span class="si">}</span><span class="s2">%"</span> <span class="p">}</span>
<span class="n">total_secs</span> <span class="o">=</span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span> <span class="o">-</span> <span class="n">beginning_time</span>
<span class="n">combined_mins</span> <span class="o">=</span> <span class="p">(</span><span class="n">total_secs</span> <span class="o">/</span> <span class="mi">60</span><span class="p">)</span><span class="o">.</span><span class="n">floor</span>
<span class="n">combined_secs</span> <span class="o">=</span> <span class="p">(</span><span class="n">total_secs</span> <span class="o">%</span> <span class="mi">60</span><span class="p">)</span><span class="o">.</span><span class="n">floor</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">" Time elapsed: </span><span class="si">#{</span><span class="n">combined_mins</span><span class="si">}</span><span class="s2"> minutes, </span><span class="si">#{</span><span class="n">combined_secs</span><span class="si">}</span><span class="s2"> seconds (</span><span class="si">#{</span><span class="n">total_secs</span><span class="si">}</span><span class="s2"> seconds).</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="k">rescue</span> <span class="no">RuntimeError</span> <span class="o">=></span> <span class="n">e</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">" RUNTIME ERROR occured!!!</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">)</span>
<span class="k">return</span>
<span class="k">rescue</span> <span class="no">Interrupt</span> <span class="o">=></span> <span class="n">e</span>
<span class="nb">puts</span> <span class="s2">"SIGINT caught. Deleting </span><span class="si">#{</span><span class="n">newfilename</span><span class="si">}</span><span class="s2"> and exiting after that."</span>
<span class="no">File</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">newfilename</span><span class="p">)</span>
<span class="nb">exit</span>
<span class="k">end</span>
<span class="c1"># Check whether transcoding was successful.</span>
<span class="n">newmovie</span> <span class="o">=</span> <span class="no">FFMPEG</span><span class="o">::</span><span class="no">Movie</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">newfilename</span><span class="p">)</span>
<span class="k">if</span> <span class="n">newmovie</span><span class="o">.</span><span class="n">valid?</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">" Transcoding completed successfully. Deleting "</span> <span class="o">+</span> <span class="n">filename</span> <span class="o">+</span> <span class="s2">".</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">)</span>
<span class="no">File</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
<span class="k">else</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">" Error occured while transcoding. Deleting "</span> <span class="o">+</span> <span class="n">newfilename</span> <span class="o">+</span> <span class="s2">".</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">)</span>
<span class="no">File</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="n">newfilename</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="vg">$logfile</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s2">"/tmp/ffautoconv.log"</span><span class="p">,</span> <span class="no">File</span><span class="o">::</span><span class="no">WRONLY</span><span class="o">|</span><span class="no">File</span><span class="o">::</span><span class="no">TRUNC</span><span class="o">|</span><span class="no">File</span><span class="o">::</span><span class="no">CREAT</span><span class="o">|</span><span class="no">File</span><span class="o">::</span><span class="no">APPEND</span><span class="p">)</span>
<span class="no">FFMPEG</span><span class="o">::</span><span class="n">logger</span><span class="o">.</span><span class="n">level</span> <span class="o">=</span> <span class="no">Logger</span><span class="o">::</span><span class="no">WARN</span>
<span class="no">ARGV</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">a</span><span class="o">|</span>
<span class="no">Find</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span>
<span class="n">movie</span> <span class="o">=</span> <span class="no">FFMPEG</span><span class="o">::</span><span class="no">Movie</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="k">if</span> <span class="n">movie</span><span class="o">.</span><span class="n">valid?</span>
<span class="n">dstACodec</span> <span class="o">=</span> <span class="s2">"copy"</span> <span class="c1"># Always use the old audio track.</span>
<span class="c1"># Decide new video codec depending on the video codec of the original file.</span>
<span class="k">case</span> <span class="n">movie</span><span class="o">.</span><span class="n">video_codec</span>
<span class="k">when</span> <span class="sr">/^h264(.*)/</span><span class="p">,</span> <span class="sr">/^mpeg4(.*)/</span><span class="p">,</span> <span class="sr">/^wmv3(.*)/</span> <span class="c1"># Video codecs that don't need to be transcoded.</span>
<span class="n">dstVCodec</span> <span class="o">=</span> <span class="s2">"copy"</span>
<span class="k">when</span> <span class="sr">/^flv(.*)/</span><span class="p">,</span> <span class="sr">/^vp6f(.*)/</span><span class="p">,</span> <span class="sr">/^msmpeg4(.*)/</span><span class="p">,</span> <span class="sr">/^theora(.*)/</span><span class="p">,</span>
<span class="sr">/^wmv1(.*)/</span><span class="p">,</span> <span class="sr">/^wmv2(.*)/</span><span class="p">,</span> <span class="sr">/^vc1(.*)/</span><span class="p">,</span> <span class="sr">/^rv40(.*)/</span> <span class="c1"># Video codecs that need to be transcoded.</span>
<span class="n">dstVCodec</span> <span class="o">=</span> <span class="s2">"h264"</span>
<span class="k">when</span> <span class="sr">/^mpeg2video(.*)/</span><span class="p">,</span> <span class="sr">/^mpeg1video(.*)/</span> <span class="c1"># MPEG videos need special treatment.</span>
<span class="k">when</span> <span class="sr">/^mjpeg(.*)/</span>
<span class="k">next</span>
<span class="k">else</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">f</span><span class="si">}</span><span class="s2">: vcodec not handled: </span><span class="si">#{</span><span class="n">movie</span><span class="o">.</span><span class="n">video_codec</span><span class="si">}</span><span class="s2">, acodec: </span><span class="si">#{</span><span class="n">movie</span><span class="o">.</span><span class="n">audio_codec</span><span class="si">}</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">)</span>
<span class="k">next</span>
<span class="k">end</span>
<span class="c1"># Decide which container format we should take depending on the audio codec of the original file.</span>
<span class="k">case</span> <span class="n">movie</span><span class="o">.</span><span class="n">audio_codec</span>
<span class="k">when</span> <span class="kp">nil</span><span class="sc">?,</span> <span class="sr">/aac(.*)/</span><span class="p">,</span> <span class="sr">/mp3(.*)/</span><span class="p">,</span> <span class="sr">/ac3(.*)/</span> <span class="c1"># ffmpeg mp4 does NOT support WMA, but wmv does. See http://en.wikipedia.org/wiki/Comparison_of_container_formats</span>
<span class="n">dstACodec</span> <span class="o">=</span> <span class="s2">"aac"</span> <span class="k">if</span> <span class="n">movie</span><span class="o">.</span><span class="n">video_codec</span> <span class="o">=~</span> <span class="sr">/^vp6f(.*)/</span>
<span class="c1"># Change container for audio formats that are supported by the mp4 container.</span>
<span class="n">dstContainer</span> <span class="o">||=</span> <span class="s2">"mp4"</span>
<span class="k">when</span> <span class="sr">/cook(.*)/</span>
<span class="n">dstACodec</span> <span class="o">=</span> <span class="s2">"aac"</span>
<span class="n">dstContainer</span> <span class="o">||=</span> <span class="s2">"mp4"</span>
<span class="k">else</span> <span class="c1"># wmav2, sipr</span>
<span class="c1"># Matroska offers the best format support for all possible audio codecs.</span>
<span class="n">dstContainer</span> <span class="o">||=</span> <span class="s2">"mkv"</span>
<span class="k">end</span>
<span class="n">reencode</span><span class="p">(</span><span class="n">movie</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">dstACodec</span><span class="p">,</span> <span class="n">dstVCodec</span><span class="p">,</span> <span class="n">dstContainer</span><span class="p">)</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">flush</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="vg">$logfile</span><span class="o">.</span><span class="n">close</span>
</pre>
How to keep apt-get and aptitude from switching repositories when upgrading a certain package2012-05-17T18:30:00+02:002012-05-17T18:30:00+02:00Thomas Weiningertag:weinimo.github.io,2012-05-17:/how-to-keep-apt-get-and-aptitude-from-switching-repositories-when-upgrading-a-certain-package.html<p>Today, after the usual</p>
<pre class="code text literal-block">
apt-get update; apt-get upgrade
</pre>
<p>procedure on my Debian box, I noticed that FFMPEG wasn't working as
expected anymore. It happend that the upgrade installed a new version of
FFMPEG from the official Debian repositories, while I had used the
deb-multimedia.org repository for this package before …</p><p>Today, after the usual</p>
<pre class="code text literal-block">
apt-get update; apt-get upgrade
</pre>
<p>procedure on my Debian box, I noticed that FFMPEG wasn't working as
expected anymore. It happend that the upgrade installed a new version of
FFMPEG from the official Debian repositories, while I had used the
deb-multimedia.org repository for this package before. The official
Debian package lacks support for libfaac. However, I had to switch back
to the old package. Here is how I've done this.</p>
<p>APT allows to set priorities for certain repositories and packages.
Debian calls this feature "APT pinning". All I had to do was to set the
priority for the FFMPEG related packages from deb-multimedia.org to
values >1000 to make apt always install FFMPEG from there. To do this,
just create a new file in /etc/apt/preferences.d. I called that file
deb-multimedia. Here are the contents of the new file:</p>
<pre class="code text literal-block">
Package: ffmpeg
Pin: origin www.debian-multimedia.org
Pin-Priority: 1001
</pre>
<p>Basically it says: "Use the www.debian-multimedia.org repository for the
ffmpeg package no matter what.". Debian plans to replace ffmpeg with
libav in future releases, so I additionally made APT never ever install
libav by creating another file in /etc/apt/preferences.d:</p>
<pre class="code text literal-block">
Package: libav-tools
Pin: origin ""
Pin-Priority: -1
</pre>
<p>Pin-Priority: -1 tells APT to never install that package. After these
few steps it is possible to switch to the old repository by simple
reinstalling the packages. Great work Debian, thank you for this!</p>
Streaming video files to Samsung TVs (Part 1)2012-05-12T18:31:00+02:002012-05-12T18:31:00+02:00Thomas Weiningertag:weinimo.github.io,2012-05-12:/streaming-video-files-to-samsung-tvs-part-1.html<p><a class="reference external" href="http://blog.weinimo.de/wp-content/uploads/2012/05/logo1.png"><img alt="image0" src="http://blog.weinimo.de/wp-content/uploads/2012/05/logo1-300x79.png" /></a></p>
<p>I have a huge video archive on my NAS and I wanted to be able to stream
the files to my Samsung TV UE40D6300 using its DLNA capability. Because
TVs in general and the DLNA standard especially do not support many
codecs it was clear to me that this goal …</p><p><a class="reference external" href="http://blog.weinimo.de/wp-content/uploads/2012/05/logo1.png"><img alt="image0" src="http://blog.weinimo.de/wp-content/uploads/2012/05/logo1-300x79.png" /></a></p>
<p>I have a huge video archive on my NAS and I wanted to be able to stream
the files to my Samsung TV UE40D6300 using its DLNA capability. Because
TVs in general and the DLNA standard especially do not support many
codecs it was clear to me that this goal may not be that easy to
achieve.</p>
<p>I thought transcoding would solve my codec problems, so I installed
<a class="reference external" href="http://mediatomb.cc/">Mediatomb</a> on my NAS. Unfortunately it hasn't. Instead It brought me
some new problems. Suddenly I wasn't able to use the fast-forward
function anymore. Furthermore I didn't like Mediatomb much, as it seems
the developers stopped working on this project. So I searched for
alternatives. <a class="reference external" href="http://sourceforge.net/projects/minidlna/">MiniDLNA</a> was a promising candidate, but it didn't
support on-the-fly transcoding like Mediatomb did and I even noticed
that some files that would play fine on my TV without any transcoding
don't work with miniDLNA. It was clear that miniDLNA couldn't offer the
functionality I needed.</p>
<p>For a long time I had no real solution for these issues and so my search
for a better mediaserver continued. Finally I found a new project called
<a class="reference external" href="http://www.serviio.org/">Serviio</a>. It hasn't been released under Open Source license yet, but
it seemed to have (nearly) all the features I was searching for. It
supports transcoding while it is still possible to jump forwards and
backwards in many movies. Using profiles Serviio is able to adjust its
behaviour depending on what client hardware you use. And Serviio even
detects your hardware and sets the matching profile automatically.
That's how software should work wherever possible - without any
intervention of its user. Of course Serviio isn't perfect yet, eg. it
seems to have problems with huge collections, but it's better than
anything I've tried before. I strongly encourage everyone having similar
problems to try Serviio out. It's a great piece of software.</p>
<p>Basically I was happy with my setup. I was able to watch all my movies
from my NAS on my TV. But on-the-fly transcoding has a downside that
still hindered the perfect streaming experience. It wasn't possible to
use fast-forward wherever transcoding was necessary. I had to find out
which codecs and which container formats my Samsung TV is able to play
over DLNA, so I prepared a series of testvideos with different video and
audio codecs and different container formats on my NAS to test it out.</p>
<p>The results of my investigations and my final approach for solving my
streaming issues will be subject of my next posts.</p>