Archive for the ‘Code’ Category

Remove Size bbcode from phpbb

Thursday, March 4th, 2010

One of the most annoying things about phpBB is the ability for people to randomly use [size="200"]Huge[/size] BB code, with no easy way to remove it and every time you update between versions it comes back.

To disable it:
in /include/bbcode.php remove the whole

case 5:
...
break;

section.

To remove it from the posting page:
in /styles/prosilver/template/posting_buttons.html remove the whole

<select>
...
</select>

section.

After that, delete your “cache” directory so that it uses the new copy.

Remember that you have to repeat this process after every update to phpBB3.

Moving comments in WordPress

Sunday, February 7th, 2010

In MySQL, first move the comment:

UPDATE wp_comments SET comment_post_ID = P_TO_ID WHERE comment_ID = C_ID;

P_TO_ID is the ID of the post you are moving it to. C_ID is the ID of the comment you are moving. You can find both of these IDs easily from the dashboard.

Then correct the comment counts for the posts:

UPDATE wp_posts SET comment_count = (SELECT count(*) FROM wp_comments WHERE comment_post_id = id AND comment_approved = 1)
WHERE comment_count != (SELECT count(*) FROM wp_comments WHERE comment_post_id = id AND comment_approved = 1);

The second line in this query is not strictly necessary but it lets you know how many posts were affected.

Convert any audio/video files to mp3 for free

Tuesday, November 24th, 2009

It seems that there is some confusion about audio conversion tools and some people even pay for them.

The truth is that most encoders, even ones you pay for, use ffmpeg internally to do all of the actual work and ffmpeg is free and open source.

You can even get it pre-compiled for Windows, though the version isn’t great.

The only problem with using ffmpeg directly is that it has a command line interface and most novices find it hard to use.

Here’s a batch file for use on Windows which will convert anything you drop onto the file into a 320kbps mp3:

ffmpeg -i %1 -ab 320k -y %1.mp3
@if errorlevel 1 @pause

To create this file, open notepad. Copy and paste the above 2 lines in. Save it as “convert.bat” or something similar (the .bat is important) and select “All files” from the drop down list.

Put the bat file in the same directory as ffmpeg.exe and simply drag anything you want converted on top of the bat file.

You can use almost anything. wav, m4a, m4v, mov, mpg, avi, etc. If you use a file with both audio and video, such as a movie, it will just extract the audio and save that as an mp3 file.

ffmpeg target file sizes (again)

Friday, July 17th, 2009

As a bit of a revision to a previous entry, I have converted the combination bash/python scripts I gave earlier into a single large python script.

This can now be used without editing any files to set sizes. An example of use is:
$ ./enc.py -s 700 -a 192 -o '-deinterlace' da_*.VOB
All of these flags are optional except the file list, where at least 1 file must be specified.

enc.tar.gz:

#!/usr/bin/python
 
from getopt import getopt, GetoptError
from sys import argv, exit
from subprocess import Popen, PIPE
from re import search
 
def secs(h, m, s):
  return (((h*60)+m)*60)+s
 
def calc_video_bitrate(target_bytes, ab, time):
  audio_bytes = (ab * 1000 / 8) * time
  return (target_bytes - audio_bytes) / time * 8
 
def get_duration(file):
  ffmpeg = Popen(("ffmpeg", "-i", file), stderr=PIPE)
  match = search("(\\d+):(\\d+):(\\d+)\\.\\d+", ffmpeg.communicate()[1])
  ffmpeg.stderr.close()
 
  if match == None: return 0
  dur = match.groups()
  return secs(int(dur[0]), int(dur[1]), int(dur[2]))
 
def encode(source, dest, opts, vbr, abr=128):
  command1 = "ffmpeg -i %s -pass 1 -an -vcodec libx264 -vpre fastfirstpass -b %d -bt %d -threads 0 %s -y pass1.mp4" % (source, vbr, vbr, opts)
  command2 = "ffmpeg -i %s -pass 2 -acodec libfaac -ac 2 -ab %dk -vcodec libx264 -vpre hq -b %d -bt %d -threads 0 %s -y %s" % (source, abr, vbr, vbr, opts, dest)
 
  pass1 = Popen(command1.split())
  pass1.wait()
 
  pass2 = Popen(command2.split())
  pass2.wait()
 
 
target_size = 350 #MiB
ab = 128 #kbps
extra_options = ""
 
# Parse args
try:
  opts, args = getopt(argv[1:], "s:a:o:", ["size=", "ab=", "opts="])
except GetoptError, err:
  print str(err)
  exit(2)
 
for o, a in opts:
  if o in ("-s", "--size"):
    target_size = int(a) * 1024 * 1024
  elif o in ("-a", "--ab"):
    ab = int(a)
  elif o in ("-o", "--opts"):
    extra_options = a
 
if len(args) < 1:
  print "Usage: " + argv[0] + \
    " [-s <size (MiB)>] [-a <audio bit rate (kbps)>] [-o <additional options>] <file list>"
  exit(2)
 
# Loop through files
for file in args:
  duration = get_duration(file)
  if duration <= 0:
    print "Unable to get length of", file
    continue
 
  bitrate = calc_video_bitrate(target_size, ab, duration)
 
  encode(file, file + ".mp4", extra_options, bitrate, ab)

Network speed test

Tuesday, July 14th, 2009

I currently have the need to test the connection speed between two remote machines, so that I know when it is safe to transfer a large file from one to the other without significantly affecting service by causing long periods of downtime. I couldn’t find any software that did this easily, ran as a single executable (no installation) and was free, so I made one.

This currently only tests speed in one direction (upload from the client to the server) because I have symmetric connections on both machines, so either way should be exactly the same, and so that I can just write a client program and use netcat as a server to accept the packets and dump them to /dev/null. It is simple enough to convert this client into a server so that netcat becomes a client or so that they can be used together. Using netcat as a client probably requires quite a large pre-generated random file and may introduce a bottleneck of the hard drive read speed.

This could also be used in the home for something like testing the effect of wifi strength.

The client takes the command line arguments, connects to the server, generates some random data and sends that data repeatedly until the timer expires. It then prints out the results.

The command to use netcat to listen and ignore any data it receives is nc -lp 9000 > /dev/null though the -p isn’t always needed (it was needed on my Debian box but not my ESX 4 box).

An example of use of this program as a client is:

C:\>SpeedTestClient.exe 192.168.1.157 9000
Uploaded 1181614080 bytes in 10.015 seconds.
943875.45 kbps (943.88 mbps)
115219.17 kB/s (112.52 MB/s)

The results are given in kilobits per second and megabits per second (standard measurements for network speed), kilobytes per second and megabytes per second (more useful for knowing how long a file will take to transfer). In this example, a gigabit ethernet connection is getting roughly 944 mbps.

A few seconds later, VLC 1.0 is opened and paused, so nothing is playing and the test is run again. The results are quite interesting:

Uploaded 225083392 bytes in 10.015 seconds.
179797.02 kbps (179.80 mbps)
 21947.88 kB/s ( 21.43 MB/s)

It says it is using next to no system resources when paused but it slows data transfer dramatically and this is with 8GiB of RAM, an otherwise idle CPU and no hard drives being used. When hard drives enter the equation this drops down to about 8MB/s (while VLC is still paused – I have particularly fast hard drives so I still get about 110MB/s when hard drives are used and VLC is turned off).

The code listing is as follows:

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
 
namespace SpeedTest
{
    class SpeedTestClient
    {
        static int Main(string[] args)
        {
            if (args.Length < 2)
            {
                Console.WriteLine("Usage: SpeedTestClient <ip> <port> [<seconds>]");
                return 1;
            }
 
            try
            {
                ulong bytes = 0;
                int startTime = Environment.TickCount;
                bool started = false;
                int timeout = 10;
 
                // Get the optional number of seconds argument
                if (args.Length > 2) timeout = int.Parse(args[2]);
 
                // Connect to the server via TCP
                TcpClient client = new TcpClient();
                client.Connect(IPAddress.Parse(args[0]), int.Parse(args[1]));
                NetworkStream stream = client.GetStream();
 
                // Generate some random data. Buffers too small will bottleneck.
                RandomNumberGenerator rng = RandomNumberGenerator.Create();
                byte[] buffer = new byte[32768];
                rng.GetBytes(buffer);
 
                while (true)
                {
                    // Write the data
                    stream.Write(buffer, 0, buffer.Length);
 
                    // Start the timer only after some data has been sent
                    if (!started)
                    {
                        startTime = Environment.TickCount;
                        started = true;
                    }
 
                    // Add to the bytes counter
                    bytes += (ulong)buffer.LongLength;
 
                    if (Environment.TickCount - startTime >= timeout * 1000) break;
                }
 
                PrintFinalStats(startTime, Environment.TickCount, bytes);
 
                try { client.Close(); }
                catch (IOException) { /* Do nothing */ }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine(ex);
                return 1;
            }
 
            return 0;
        }
 
        static void PrintFinalStats(int start, int end, ulong bytes)
        {
            double seconds = (end - start) / 1000.0;
            ulong bits = bytes * 8;
 
            double bitsPerSecond = bits / seconds;
            double bytesPerSecond = bytes / seconds;
 
            string kbps = string.Format("{0:0.00}", bitsPerSecond / 1000);
            string mbps = string.Format("{0:0.00}", bitsPerSecond / 1000 / 1000);
            string kibs = string.Format("{0:0.00}", bytesPerSecond / 1024);
            string mibs = string.Format("{0:0.00}", bytesPerSecond / 1024 / 1024);
 
            int kPad = Math.Max(kbps.Length, kibs.Length);
            int mPad = Math.Max(mbps.Length, mibs.Length);
 
            Console.WriteLine("Uploaded {0} bytes in {1} seconds.", bytes, seconds);
            Console.WriteLine("{0} kbps ({1} mbps)", kbps.PadLeft(kPad), mbps.PadLeft(mPad));
            Console.WriteLine("{0} kB/s ({1} MB/s)", kibs.PadLeft(kPad), mibs.PadLeft(mPad));
        }
    }
}

A compiled .net executable is available here.

P.S. always remember to turn off VLC before transferring files over the network.

How to set target file sizes in ffmpeg

Tuesday, June 16th, 2009

After much searching, I found almost nothing regarding how to target a specific file size with ffmpeg, such as 700MiB to fit on a CD. Almost everything I found suggested to use a negative bitrate for mencoder, such as -700000. However, this did not work for me at all (even by copying and pasting verbatim). Because of this, I set about doing it manually with some mathematics.

What I am going to do here assumes a single audio stream, a single video stream and nothing else in the output file at all (i.e. no subtitles, no alternate languages, no director’s commentaries, etc).

When encoding media, audio uses a constant bit rate. This means that you can calculate how much space it will take if you know which bit rate you are using and how long the stream is in seconds. This also means that to know how big the video stream should be, we can calculate how big the audio stream will be and subtract it from the target file size. For example, if the target file size is 350MiB and the audio will take up 30MiB, we know the video should aim to be 320MiB (ignoring overheads in the multiplexing, container headers, etc). We can use the same calculations in reverse to work out an average or constant bit rate from a file size and a length in seconds.

In this example, I will be using Dad’s Army Series 6 Episode 1 – The Deadly Attachment. I have already ripped this from the DVD with SlySoft AnyDVD and PgcDemux on Windows. Automating PgcDemux rips is worth a whole entry in itself. Note that I have bought this legally and I do not intend to sell or share it. I just want a copy that I can store on my network and play from any computer. Storing 14 DVD images on hard drives takes quite a lot of space, which is where encoding comes in handy. In fact, some of my box sets contain over 60 DVDs (roughly 270GiB).

———-
To install ffmpeg on Debian, it is best to get it from the Debian Multimedia repository. Add

deb http://www.debian-multimedia.org lenny main
deb-src http://www.debian-multimedia.org lenny main

to your /etc/apt/sources.list and then run

# apt-get update
# apt-get install debian-multimedia-keyring
# apt-get update
# apt-get install ffmpeg

———-

The first thing we need to do to put this into practice is to get the length of the stream. If you supply the file to ffmpeg without telling it to do anything, it will give you an error. However, this error already contains all of the information we need:

$ ffmpeg -i da_6_01.VOB
FFmpeg version SVN-r13582, Copyright (c) 2000-2008 Fabrice Bellard, et al.
  configuration: --prefix=/usr ...
  built on May  3 2009 12:07:18, gcc: 4.3.2
Input #0, mpeg, from 'da_6_01.VOB':
  Duration: 00:29:23.90, start: 0.287267, bitrate: 5987 kb/s
    Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 16:15 DAR 4:3], 9800 kb/s, 25.00 tb(r)
    Stream #0.1[0x80]: Audio: ac3, 48000 Hz, stereo, 192 kb/s
Must supply at least one output file

We can see that the duration is 00:29:23.90.

As this is an error, we need to redirect stderr to stdout. This can be done with 2>&1. We then need the line that contains the “Duration”, which we can get with grep. We can cut this line up to take the time out.

$ ffmpeg -i da_6_01.VOB 2>&1 | grep Duration | cut -d ' ' -f 4 | cut -d '.' -f 1
00:29:23

This can be converted into seconds with the formula s' = (h * 60 + m) * 60 + s.

I used a python script to do this and to perform the necessary calculations mentioned earlier. Note that most of this python script is just for the convenience of getopts and that, if needed, it can be cut down significantly at the cost of flexibility. If you always want the same output size and audio bitrate, this script can be simplified to just a couple of lines.

calc_bitrate.py:

#!/usr/bin/python
 
from getopt import getopt, GetoptError
from sys import argv, exit
 
def secs(h, m, s):
  return (h * 60 + m) * 60 + s
 
def calc_video_bitrate(target_size, abr, time):
  target_bytes = target_size * 1024 * 1024
  audio_bytes = ((abr * 1000) / 8) * time
  return (target_bytes - audio_bytes) / time * 8
 
try:
  opts, args = getopt(argv[1:], "s:a:t:", ["size=", "abr=", "time="])
except GetoptError, err:
  print str(err)
  exit(2)
 
target_size = 350 #MiB
abr = 128 #kbps
time = 0
 
for o, a in opts:
  if o in ("-s", "--size"):
    target_size = int(a)
  elif o in ("-a", "--abr"):
    abr = int(a)
 
if len(args) < 1:
  print "Usage: " + argv[0] + " [-s <size (M)>] [-a <audio bit rate (k)>] [[hh:]mm:]ss"
  exit(2)
 
time_part = args[0].split(":")
if len(time_part) < 2: time_part = [0] + time_part
if len(time_part) < 3: time_part = [0] + time_part
time = secs(int(time_part[0]), int(time_part[1]), int(time_part[2]))
 
print "%d" % calc_video_bitrate(target_size, abr, time)

A typical usage for this would be ./calc_python.py -s 350 -a 128 29:23.

We can then use a combination of these inside a shell script easily. I’m using bash syntax just to nest the commands into a single line but for compatibility with other shells, backticks (`) can be used and this is what I normally prefer. This script loops through all of the .VOB files in the directory and gives their target video bitrate for a 350MiB output and 128kbps audio.

#!/bin/bash
 
for vob in *.VOB
do
  BR=$(./calc_bitrate.py -s 350 -a 128 $(ffmpeg -i $vob 2>&1 | grep Duration | cut -d ' ' -f 4 | cut -d '.' -f 1))
  echo $vob $BR
done

I use this in 2-pass VBR mode with the libx264 video encoder and libfaac audio encoder in an mp4 container just by replacing the “echo” line of the above script with the following 2 lines:

ffmpeg -i $vob -an -pass 1 -vcodec libx264 -vpre fastfirstpass -b $BR -bt $BR -deinterlace -threads 0 -y pass1.mp4
ffmpeg -i $vob -acodec libfaac -ab 128k -ac 2 -pass 2 -vcodec libx264 -vpre hq -b $BR -bt $BR -deinterlace -threads 0 -y `echo $vob | sed 's/VOB/mp4/'`

I have 2 versions of this script, one with “-deinterlace” and one without. PAL video (576i – used in Europe), such as Dad’s Army needs deinterlacing. NTSC video (480 – used in North America) usually does not.

For ripping DVDs to H.264 and maintaining the quality, I wouldn’t use a bitrate lower than 1000k or an audio bitrate lower than 128k stereo. This means that for getting media onto a CD, you probably want no smaller than 350MiB for 30 to 40 minutes or 700MiB for 1 hour or more, depending on your resolution. Also note that I only intend to play these in computers and fitting them onto CDs is just a convenience – standalone DVD players will not be able to play H.264 or AAC (Blu-ray and HDDVD players may be able to – mp4 containers and reading CDs would be more of an issue).

Update:

I have successfully tested this in a LG BD370 Blu-ray player.

I have not tried actually burning these onto CDs yet so if 2 won’t fit on a 700MiB CD, even with overburn (because of overheads that were ignored, as stated earlier) then the simplest solution is to just target a smaller file size with something like “-s 348″.

The result

Dad’s Army episodes are not all of the same length. Some are several minutes longer than others. The following shows how no matter the size of the input file, they all produce the same size output file:

$ ll da_5_*.VOB
-r--r--r-- 1 mythtv mythtv 1364660224 2009-06-04 00:01 da_5_01.VOB
-r--r--r-- 1 mythtv mythtv 1360699392 2009-06-04 00:01 da_5_02.VOB
-r--r--r-- 1 mythtv mythtv  956084224 2009-06-04 00:02 da_5_03.VOB
-r--r--r-- 1 mythtv mythtv 1355888640 2009-06-04 00:02 da_5_04.VOB
-r--r--r-- 1 mythtv mythtv 1114351616 2009-06-03 23:59 da_5_05.VOB
-r--r--r-- 1 mythtv mythtv 1034289152 2009-06-04 00:00 da_5_06.VOB
-r--r--r-- 1 mythtv mythtv 1045202944 2009-06-04 00:00 da_5_07.VOB
-r--r--r-- 1 mythtv mythtv 1345318912 2009-06-04 00:00 da_5_08.VOB
-r--r--r-- 1 mythtv mythtv 1391529984 2009-06-04 00:02 da_5_09.VOB
-r--r--r-- 1 mythtv mythtv 1342748672 2009-06-04 00:03 da_5_10.VOB
-r--r--r-- 1 mythtv mythtv 1158017024 2009-06-04 00:03 da_5_11.VOB
-r--r--r-- 1 mythtv mythtv 1157097472 2009-06-04 00:03 da_5_12.VOB
-r--r--r-- 1 mythtv mythtv 1180098560 2009-06-04 00:04 da_5_13.VOB
$ ll -h da_5_*.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 06:18 da_5_01.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 06:55 da_5_02.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 07:30 da_5_03.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 08:08 da_5_04.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 08:44 da_5_05.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 09:21 da_5_06.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 09:58 da_5_07.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 10:36 da_5_08.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 11:15 da_5_09.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 11:54 da_5_10.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 12:29 da_5_11.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 13:05 da_5_12.mp4
-rw-r--r-- 1 mythtv mythtv 352M 2009-06-17 13:41 da_5_13.mp4

Update:

A pure python replacement script is available here that simplifies use.

Pixel theme

Saturday, June 13th, 2009

I’ve just switched to the Pixel WordPress theme. It’s quite nice but it does have some flaws. Images no longer auto-size so some are too large. I have had to manually resize the widest offenders.

Another problem is that it forces you to have a crappy redundant “Welcome” message. I have removed this in the style.css and, while I was at it, I made the post content justified. If anyone else wants to do the same thing (or I want to repeat this modification after a WordPress update), the following CSS is simply appended to the theme’s style.css:

#welcome { display: none; }
.topContent { text-align: justify; }

There is no logical equivalent to conditional statements!

Monday, May 18th, 2009

For a long time, I have seen chunks of code in languages such as Lua and Python that claim that they can reproduce the C conditional operator just by using two logical operators, “and” and “or”.

The conditional statement in C uses the following format:

v = c ? t : f;

This is equivalent to saying “if the condition c (a boolean expression) is met assign t to v, otherwise assign f to v”. It’s a shorthand way of writing

if (c)
  v = t;
else
  v = f;

Some example outputs:

v = 1 ? "foo" : "bar"; /* v = "foo" */
v = 0 ? "foo" : "bar"; /* v = "bar" */
v = 1 ? 0 : 2;         /* v = 0 */

In the first example, the condition is true (C has no boolean type, anything that isn’t zero equates to true) so “t” is assigned. In the second example, the condition is false (zero) so “f” is assigned. The important thing to note here is that in the third example, the condition is true so “t” is still assigned.

As proof of this final value, the following was taken from a cgwin shell using the gcc compiler

~$ gcc --version
gcc (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
~$ cat cond.c
#include <stdio.h>
 
int main(void)
{
  printf("The result is %d!\n", 1 ? 0 : 2);
 
  return 0;
}
~$ gcc -Wall -o cond cond.c
~$ ./cond
The result is 0!
~$

The following is an implementation in Python that always gives the same behaviour as the conditional operator in C.

def cond_if(c, t, f):
  if c: return t
  else: return f

We can show the same examples again:

>>> cond_if(True, "foo", "bar")
'foo'
>>> cond_if(False, "foo", "bar")
'bar'
>>> cond_if(True, 0, 2)
0

However, many people claim that the same code can be written more concisely and, more importantly, inline by using logical operators to mimic the behaviour. The following is such a function:

def cond_logic(c, t, f):
  return c and t or f

When we try to actually run this, with the same examples again, we see where it all falls down.

>>> cond_logic(True, "foo", "bar")
'foo'
>>> cond_logic(False, "foo", "bar")
'bar'
>>> cond_logic(True, 0, 2)
2

While it works for the first two examples as it should, any pair of values which can be equated to a boolean expression will corrupt the logic used. In this example, simply passing zero as the “t” makes the result of “c and t” false (because false and _ = false), which reduces it to “false or f”, which returns “f” (because false or _ = _). No combination of parenthesis or variables will correct this problem.

Here’s the final example again but this time in Lua:

> function cond_logic(c, t, f)
>>   return c and t or f;
>> end
 
> = cond_logic(1, false, 2);
2

So, the next time someone tells you that your code can be improved in this way or that logical operators can be used to mimic the conditional operator, tell them that they are wrong and give them an example to prove it, then stick to using if statements if the language does not provide a conditional operator.

libvlc media player in C# (part 2)

Friday, May 8th, 2009

I gave some simplified VLC media player code in part 1 to show how easy it was to do and how most wrapper libraries make a mountain out of a mole hill. In that entry, I briefly touched on using some classes to make it easier and safer to implement actual programs with this.

The first thing to do is write a wrapper for the exceptions, so that they are handled nicely in C#. For a program using the library, exceptions should be completely transparent and should be handled in the normal try/catch blocks without having to do anything like initialise them or check them.

Another thing to do is to move all of the initialisation functions into constructors and all of the release functions into destuctors or use the System.IDisposable interface.

Here is the code listing for the 4 classes used (VlcInstance, VlcMedia, VlcMediaPlayer and VlcException). Note that the first 3 of these are very similar and that the main difference is that the media player class has some extra functions for doing things like playing and pausing the content.

class VlcInstance : IDisposable
{
    internal IntPtr Handle;
 
    public VlcInstance(string[] args)
    {
        VlcException ex = new VlcException();
        Handle = LibVlc.libvlc_new(args.Length, args, ref ex.Ex);
        if (ex.IsRaised) throw ex;
    }
 
    public void Dispose()
    {
        LibVlc.libvlc_release(Handle);
    }
}
 
class VlcMedia : IDisposable
{
    internal IntPtr Handle;
 
    public VlcMedia(VlcInstance instance, string url)
    {
        VlcException ex = new VlcException();
        Handle = LibVlc.libvlc_media_new(instance.Handle, url, ref ex.Ex);
        if (ex.IsRaised) throw ex;
    }
 
    public void Dispose()
    {
        LibVlc.libvlc_media_release(Handle);
    }
}
 
class VlcMediaPlayer : IDisposable
{
    internal IntPtr Handle;
    private IntPtr drawable;
    private bool playing, paused;
 
    public VlcMediaPlayer(VlcMedia media)
    {
        VlcException ex = new VlcException();
        Handle = LibVlc.libvlc_media_player_new_from_media(media.Handle, ref ex.Ex);
        if (ex.IsRaised) throw ex;
    }
 
    public void Dispose()
    {
        LibVlc.libvlc_media_player_release(Handle);
    }
 
    public IntPtr Drawable
    {
        get
        {
            return drawable;
        }
        set
        {
            VlcException ex = new VlcException();
            LibVlc.libvlc_media_player_set_drawable(Handle, value, ref ex.Ex);
            if (ex.IsRaised) throw ex;
            drawable = value;
        }
    }
 
    public bool IsPlaying { get { return playing && !paused; } }
 
    public bool IsPaused { get { return playing && paused; } }
 
    public bool IsStopped { get { return !playing; } }
 
    public void Play()
    {
        VlcException ex = new VlcException();
        LibVlc.libvlc_media_player_play(Handle, ref ex.Ex);
        if (ex.IsRaised) throw ex;
 
        playing = true;
        paused = false;
    }
 
    public void Pause()
    {
        VlcException ex = new VlcException();
        LibVlc.libvlc_media_player_pause(Handle, ref ex.Ex);
        if (ex.IsRaised) throw ex;
 
        if (playing)
            paused ^= true;
    }
 
    public void Stop()
    {
        VlcException ex = new VlcException();
        LibVlc.libvlc_media_player_stop(Handle, ref ex.Ex);
        if (ex.IsRaised) throw ex;
 
        playing = false;
        paused = false;
    }
}
 
class VlcException : Exception
{
    internal libvlc_exception_t Ex;
 
    public VlcException() : base()
    {
        Ex = new libvlc_exception_t();
        LibVlc.libvlc_exception_init(ref Ex);
    }
 
    public bool IsRaised { get { return LibVlc.libvlc_exception_raised(ref Ex) != 0; } }
 
    public override string Message { get { return LibVlc.libvlc_exception_get_message(ref Ex); } }
}

Using these classes is even easier than before, can use proper exception handling (removed for brevity) and cleans up better at the end. In this example, I have added an OpenFileDialog, which is where the file is loaded.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
 
namespace MyLibVLC
{
    public partial class Form1 : Form
    {
        VlcInstance instance;
        VlcMediaPlayer player;
 
        public Form1()
        {
            InitializeComponent();
 
            openFileDialog1.FileName = "";
            openFileDialog1.Filter = "MPEG|*.mpg|AVI|*.avi|All|*.*";
 
            string[] args = new string[] {
                "-I", "dummy", "--ignore-config",
                @"--plugin-path=C:\Program Files (x86)\VideoLAN\VLC\plugins",
                "--vout-filter=deinterlace", "--deinterlace-mode=blend"
            };
 
            instance = new VlcInstance(args);
            player = null;
        }
 
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            if(player != null) player.Dispose();
            instance.Dispose();
        }
 
        private void Open_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() != DialogResult.OK)
                return;
 
            using (VlcMedia media = new VlcMedia(instance, openFileDialog1.FileName))
            {
                if (player != null) player.Dispose();
                player = new VlcMediaPlayer(media);
            }
 
            player.Drawable = panel1.Handle;
        }
 
        private void Play_Click(object sender, EventArgs e)
        {
            player.Play();
        }
 
        private void Pause_Click(object sender, EventArgs e)
        {
            player.Pause();
        }
 
        private void Stop_Click(object sender, EventArgs e)
        {
            player.Stop();
        }
    }
}

Update:

I have just corrected a minor bug (the wrong release function being called on the player handle) and uploaded the full Visual Studio 2005 project. You can get it here. It comes with the libvlc.dll and libvlccore.dll for VLC 1.0.1 in the bin\x86\Debug directory so if you have a version other than this, just overwrite those files.

libvlc media player in C# (part 1)

Wednesday, May 6th, 2009

There seems to be a massive misconception about using VLC inside an application and many, many large wrapper libraries have been written. These are often harder to use than libvlc itself, buggy or just downright don’t work (at least not in what will be “the latest” version of VLC at the time you want to write anything).

Using the libvlc documentation directly and the libvlc example I wrote a simple wrapper class that performs the basics needed to play, pause and stop media. Because it is libvlc, things like resizing the video, toggling full screen by double clicking the video output or streaming media from a source device or network are handled automatically.

This code was all written and tested with VLC 0.98a but because it is taken from the documentation and example, it should work for all versions 0.9x and later with only minor changes. Because it is so simple, these changes should be easy to make. Most of the time, these changes will just be slight function name changes and no new re-structuring is needed.

The first thing to note is that there is no version of libvlc for Windows x64. All developers should set their CPU type to x86, even if they have a 32bit machine. If you set it to “Any CPU” then 64bit users will not be able to load libvlc.dll and will crash out. If you are compiling from the command line, this should look something like csc /platform:x86 foobar.cs

The second thing to note, which trips up a lot of users, is that you must specify VLC’s plugin directory. This may make distribution a nightmare, as the plugin directory is a large directory full of DLLs. It may be possible to narrow down these DLLs to just the ones your application actually needs but I don’t know if videolan have any advice about or licensing with redistribution of these.

libvlc is made up of several modules. For the sake of simplicity in this example, I will use 1 static class to contain every exported C function and split them up visually by module with #region.

The nicest thing about VLC, as far as interop with C# goes, is that all memory management is handled internally by libvlc and functions are provided for doing anything that you would need to do to their members. This means that using an IntPtr is suitable for almost everything. You just need to make sure that you pass the correct IntPtr into each function but another layer of C# encapsulating this would easily be able to make sure of that, as discussed in part 2. The only structure that you need to define is an exception, which is very simple. You then simply always pass in references to these structs with ref ex.

The code listing for the wrapper class is as follows:

using System;
using System.Runtime.InteropServices;
 
namespace MyLibVLC
{
  // http://www.videolan.org/developers/vlc/doc/doxygen/html/group__libvlc.html
 
  [StructLayout(LayoutKind.Sequential, Pack = 1)]
  struct libvlc_exception_t
  {
    public int b_raised;
    public int i_code;
    [MarshalAs(UnmanagedType.LPStr)]
    public string psz_message;
  }
 
  static class LibVlc
  {
    #region core
    [DllImport("libvlc")]
    public static extern IntPtr libvlc_new(int argc, [MarshalAs(UnmanagedType.LPArray,
      ArraySubType = UnmanagedType.LPStr)] string[] argv, ref libvlc_exception_t ex);
 
    [DllImport("libvlc")]
    public static extern void libvlc_release(IntPtr instance);
    #endregion
 
    #region media
    [DllImport("libvlc")]
    public static extern IntPtr libvlc_media_new(IntPtr p_instance,
      [MarshalAs(UnmanagedType.LPStr)] string psz_mrl, ref libvlc_exception_t p_e);
 
    [DllImport("libvlc")]
    public static extern void libvlc_media_release(IntPtr p_meta_desc);
    #endregion
 
    #region media player
    [DllImport("libvlc")]
    public static extern IntPtr libvlc_media_player_new_from_media(IntPtr media,
      ref libvlc_exception_t ex);
 
    [DllImport("libvlc")]
    public static extern void libvlc_media_player_release(IntPtr player);
 
    [DllImport("libvlc")]
    public static extern void libvlc_media_player_set_drawable(IntPtr player, IntPtr drawable,
      ref libvlc_exception_t p_e);
 
    [DllImport("libvlc")]
    public static extern void libvlc_media_player_play(IntPtr player, ref libvlc_exception_t ex);
 
    [DllImport("libvlc")]
    public static extern void libvlc_media_player_pause(IntPtr player, ref libvlc_exception_t ex);
 
    [DllImport("libvlc")]
    public static extern void libvlc_media_player_stop(IntPtr player, ref libvlc_exception_t ex);
    #endregion
 
    #region exception
    [DllImport("libvlc")]
    public static extern void libvlc_exception_init(ref libvlc_exception_t p_exception);
 
    [DllImport("libvlc")]
    public static extern int libvlc_exception_raised(ref libvlc_exception_t p_exception);
 
    [DllImport("libvlc")]
    public static extern string libvlc_exception_get_message(ref libvlc_exception_t p_exception);
    #endregion
  }
}

For a sample application to use this simple wrapper, I just created a new Windows form and added a play button, stop button and a panel for viewing the video. In this example, the stop button also cleans everything up so you should make sure to press it before closing the form.

At one point during this code, libvlc can optionally be given a HWND to draw to. If you don’t give it one, it pops up a new player. However, people seem to be confused over how simple this is to do in C# and have been making large amounts of interop calls to the Win32 API to get handles. This is not necessary, as System.Windows.Forms.Control.Handle allows you go get the window handle (HWND) to any component that inherits from the Control class. This includes the Form class and the Panel class (and even the Button class) so all you actually need to pass it is this.Handle (for the handle to the form itself) or panel.Handle (for a Panel called panel). If you want it to start fullscreen, add the command line argument “-f” rather than using the Win32 function GetDesktopWindow().

Because I will be using this to display PAL video, which is interlaced at 576i, I have added some deinterlacing options to the command line. These are --vout-filter=deinterlace and --deinterlace-mode=blend.

Without further ado, here is the code listing for the partial windows form class:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
 
using System.Runtime.InteropServices;
 
namespace MyLibVLC
{
  public partial class Form1 : Form
  {
    IntPtr instance, player;
 
    public Form1()
    {
      InitializeComponent();
    }
 
    private void Play_Click(object sender, EventArgs e)
    {
      libvlc_exception_t ex = new libvlc_exception_t();
      LibVlc.libvlc_exception_init(ref ex);
 
      string[] args = new string[] {
        "-I", "dummy", "--ignore-config",
        @"--plugin-path=C:\Program Files (x86)\VideoLAN\VLC\plugins",
        "--vout-filter=deinterlace", "--deinterlace-mode=blend"
      };
 
      instance = LibVlc.libvlc_new(args.Length, args, ref ex);
      Raise(ref ex);
 
      IntPtr media = LibVlc.libvlc_media_new(instance, @"C:\foobar.mpg", ref ex);
      Raise(ref ex);
 
      player = LibVlc.libvlc_media_player_new_from_media(media, ref ex);
      Raise(ref ex);
 
      LibVlc.libvlc_media_release(media);
 
      // panel1 may be any component including a System.Windows.Forms.Form but
      // this example uses a System.Windows.Forms.Panel
      LibVlc.libvlc_media_player_set_drawable(player, panel1.Handle, ref ex);
      Raise(ref ex);
 
      LibVlc.libvlc_media_player_play(player, ref ex);
      Raise(ref ex);
    }
 
    private void Stop_Click(object sender, EventArgs e)
    {
      libvlc_exception_t ex = new libvlc_exception_t();
      LibVlc.libvlc_exception_init(ref ex);
 
      LibVlc.libvlc_media_player_stop(player, ref ex);
      Raise(ref ex);
 
      LibVlc.libvlc_media_player_release(player);
      LibVlc.libvlc_release(instance);
    }
 
    static void Raise(ref libvlc_exception_t ex)
    {
      if (LibVlc.libvlc_exception_raised(ref ex) != 0)
        MessageBox.Show(LibVlc.libvlc_exception_get_message(ref ex));
    }
  }
}

Adding a pause button is similar to the stop button but without the cleanup.

Here is an example slightly further on down the line but using the same code:
Example of LibVLC

See part 2 for more.