Building VLC and its Python bindings on Mac OS/X

This page contains information on how to get the VLC python bindings to compile on Mac OS/X, and how to get video output in a wxPython control.

External hints:


The VLC team claims some software is required (

  • MacPorts (or Fink) for:
    • autoconf
    • automake
    • libgcrypt
    • pkgconfig
  • Make sure above programs are in your PATH before the versions shipped with OS/X. Use for instance which autoconf to check this.
  • If Fink is present, disable it (To disable Fink comment the line: #source /sw/bin/init.csh in your .cshrc file or . /sw/bin/init.csh in your .bashrc file in your home-directory.)

Building and installing VLC

Now, we will pull the latest VLC sources from Subversion and compile them. The following was tested on VLC revision 20151. Several patches will be needed to obtain the desired behaviour, and changes in subversion may require changes in the patches as well. See also source:/abc/branches/jelle/guiVWX_3.6/mac/Makefile.

Anyway, we will checkout the source into a directory called vlc-trunk, and build the libraries into a directory called vlc-trunk/install-root. First, we do a lot of compiling, and will apply the patch attached to this wiki page and at source:/abc/branches/jelle/guiVWX_3.6/mac/vlc-macosx.patch.

svn checkout svn:// vlc-trunk

cd vlc-trunk

export VLCDIR=`pwd`

# build supporting packages

cd extras/contrib


make src

cd ../..

# build VLC


./configure --enable-debug --disable-x11 --disable-xvideo --disable-glx --enable-sdl --enable-mad --enable-libdvbpsi --enable-a52 --disable-dvdplay --enable-dvdnav --enable-dvdread --enable-ffmpeg --enable-faad --enable-flac --enable-vorbis --enable-speex --enable-theora --enable-ogg --enable-shout --enable-cddb --disable-cddax --enable-vcdx --disable-skins --disable-skins2 --disable-wxwidgets --enable-freetype --enable-fribidi --disable-caca --enable-live555 --enable-dca --enable-goom --enable-modplug --enable-gnutls --enable-daap --disable-ncurses --enable-libtwolame --enable-x264 --enable-png --enable-realrtsp --disable-libtool --prefix=$VLCDIR/install-root

patch -p0 < vlc-macosx.patch


# install everything we need

make install

for i in `$VLCDIR/vlc-config --target builtin`


  cp $VLCDIR/$i $VLCDIR/install-root/lib/vlc/`basename $i`


cp $VLCDIR/extras/contrib/vlc-lib/vlc_lib*.dylib $VLCDIR/install-root/lib

# build python bindings

cd $VLCDIR/bindings/python

python install --prefix=$VLCDIR/install-root

Now, we can test the bindings by downloading the attached and providing a movie.mpg:

export PYTHONPATH=$VLCDIR/install-root/lib/python*/site-packages

export DYLD_LIBRARY_PATH=$VLCDIR/install-root/lib/


The following script could come in handy if you want to configure the directory of the libraries:

# fix dependencies

(for i in `find $VLCDIR/install-root/lib -name "*.dylib" -or -name "*.so"`


  otool -L $i | perl -ne 'print "install_name_tool -change $1 lib/$2 '$i'

" if m#(@executable_path/lib/(S+))#;'

done) | bash -

What was patched

When the VLC sources change, the patch as provided might break. This section explains the how and why behind the patch, so that it can be adopted when needed.

Building without errors

First of all, po/Makefile, Makefile are patched to allow an error-free make install. Next, vlc-config is patched to remove a superfluous dependency on libvlc-config, and to remove specific Intel tuning parameters. The latter prevents the python bindings from building with a Universal version of python, since it will try to compile for both PPC and Intel and thus (unfortunately) cannot use processor-specific optimisations. Building the PPC part will fail anyway due to lack of PPC VLC libs, but this was the easiest way to get it to build.

Using the right handle

Then, the Mac GUI code was patched. The first thing necessary is to have wxPython and VLC agree on where to draw the video. wxPython supplies a control handle (of type ControlRef), but VLC expects an AGLDrawable surface. To convert a ControlRef to an AGLDrawable, use the following conversion:

  windowhandle = GetControlOwner(controlhandle)

  drawable = GetWindowPort(windowhandle)

See also and for more information. I decided to do this conversion inside VLC, as this allows VLC to know the control to draw in and thus the boundaries of the region, which saves communication.

Redraw and resize properly

Unpatched, the video will not redraw automatically nor resize when the control boundaries change. To fix the redrawing, Carbon needs to be told that the control contents are invalidated and thus need to be redrawn. To fix the resizing, I use a hack: every frame, I recheck the boundaries and make adjustments when the boundaries changed. All of this is done in modules/gui/macosx/voutgl.m:

static void aglSwap( vout_thread_t * p_vout )


    if( !p_vout->p_sys->b_clipped_out || !p_vout->p_sys->b_viewport_set )


        p_vout->p_sys->b_got_frame = VLC_TRUE;


        if( !p_vout->p_sys->b_viewport_set ) {

            WindowRef win;

            Rect rect,clipBounds,viewBounds;

            /* invalidate window data to force redraw */

            win = GetWindowFromPort( p_vout->p_sys->agl_drawable );

            //GetWindowPortBounds( win, &rect );

            GetControlBounds( p_vout->p_sys->theControl, &rect );

            InvalWindowRect( win, &rect );

            /* check for resizing -- ugly hack to do this in aglSwap, i.e. every frame */

            clipBounds = viewBounds = rect;

            /* ignore consecutive viewport update with identical parameters */

            if( memcmp(&clipBounds, &(p_vout->p_sys->clipBounds), sizeof(clipBounds) )

             && memcmp(&viewBounds, &(p_vout->p_sys->viewBounds), sizeof(viewBounds)) )


                /* set first to avoid infinite recursion */

                p_vout->p_sys->clipBounds = clipBounds;

                p_vout->p_sys->viewBounds = viewBounds;

                aglSetViewport(p_vout, viewBounds, clipBounds);

                aglReshape( p_vout );






        /* drop frame */




Ideally, the resizing should be triggered from wxPython. But this was easier :)