apocryph.org Notes to my future self

15Sep/075

UPnP AV Support (Or Lack Thereof) in VLC Player

Lately I’ve been trying to find a better solution to my video watching system. Currently I have a 19″ LCD monitor in my bedroom, hooked up to a laptop on my WiFi LAN and running Windows XP. When I want to watch a video file I manually start it playing, then recline on my bed. This sucks for a few reasons:

  • Requires manual playing of each video
  • Doesn’t remember where in a series I left off
  • Downloading additional video content is a multi-step process involving my dedicated torrent machine, my NAS box, and my video player box
  • It’s not the slightest bit cool

My ReadyNAS NV+ NAS box supports UPnP AV, so I already have a MediaServer machine exposing my videos. I had heard somewhere that VLC Player supported UPnP streaming, so I looked into it.

First off, the pre-built binaries have no_ UPnP support. They have Zeroconf and some other technology I’ve never heard of. Second, you can supposedly enable UPnP support with the --enable-cyberlink config option when building from source, assuming you download the latest CyberLink for C++ package. Unfortunately, I could barely get VLC compiled without CyberLink (I had the same issue as there guys, and that French prick in the forums is no help at all), and I had absolutely _no luck building Cyberlink for C++ myself on Cygwin. From this thread I conclude the UPnP support is either totally broken or buggy.

Dammit. Are there any free/open source UPnP MediaRenderers out there?

27Nov/050

Looking for Non-Lame Open-Source UPnP Media Server

Ever since I got my Linksys wireless music system, I’ve wanted an open source UPnP music server to stream my music to it. As one might expect, it ships w/ Musicmatch Jukebox, which has a UPnP server feature, but MMJB isn’t open source and only runs on Windows. My music is stored on aenea, a FreeBSD 6.0 box, so why can’t I run a media server there, where it makes the most sense?

I’ve found three potentially non-shitty media servers thus far:

As is the case with most trendy open source projects today, they’re all written for Linux. Since I use FreeBSD, there’s inevitably some degree of contortion required to make them work.

First is the installation of the FreeBSD port of libupnp, which is in /usr/ports/devel/upnp. That built and installed without incident.

MediaTomb seems the most mature and feature rich, so I’ll start there. I downloaded the source tarball, and am having quite a bit of trouble getting it to build.

configure is failing somewhat stupidly. First, it couldn’t find the iconv.h header file located in /usr/local/include, even when I point it there explicitly. I had to set the CPPFLAGS env var to -I/usr/local/include to get past that point.

Next, configure can’t find upnp/ixml.h. This one doesn’t appear to be configure‘s fault; there really is no ixml.h in the port. Perhaps part of the problem is the age of the FreeBSD libupnp port; it reports itself as version 1.0.4_1; whatever that means, it doesn’t sound at all like 1.2.1, the latest Linux version. FreeBSD ports bug 82347 was submitted back in June with diffs to update to the current version, but it appears nothing’s been done with them.

I’m trying to apply the aforementioned ports bug diff, with mostly success but a few problems.

Hunk #1 in Makefile failed to apply, and I can’t tell why. For testing I’ll make the changes in that hunk manually; mostly they’re changing the version number, dist name, etc.

The only hunk that failed is in pkg-plist, and seems to reflect a significant refactoring of the libupnp code.

Hmm, no, this patch is still pretty fucked up. The post-download patches themselves are broken now. I’ll just build and install libupnp without a port.

Note to self: from the README:

For the UPnP library to function correctly, Linux networking must be configured
properly for multicasting. To do this:

route add -net 239.0.0.0 netmask 255.0.0.0 eth0

where ‘eth0′ is the network adapter that the UPnP library will use. Without
this addition, device advertisements and control point searches will not
function.

Keep this in mind if you ever get it to build.

Hmm, that didn’t get me very far:

 $ gmake
 gmake[1]: Entering directory `/home/anelson/libupnp-1.2.1/ixml'
 gmake[2]: Entering directory `/home/anelson/libupnp-1.2.1/ixml/src'
 gcc -Wall -I./ -I../inc -I../../pil/inc -fPIC -c -Wall -Os -DNDEBUG -I. -I../inc -Iinc -c ixml.c -o obj/ixml.o
 In file included from ../inc/ixml.h:37,
                  from inc/ixmlmembuf.h:36,
                  from ixml.c:32:
 /usr/include/malloc.h:3:2: #error "<malloc.h> has been replaced by <stdlib.h>"
 gmake[2]: *** [obj/ixml.o] Error 1
 gmake[2]: Leaving directory `/home/anelson/libupnp-1.2.1/ixml/src'
 gmake[1]: *** [all] Error 2
 gmake[1]: Leaving directory `/home/anelson/libupnp-1.2.1/ixml'
 gmake: *** [upnp] Error 2

Ok, so those patches in the port really do matter.

The libupnp-1.2.1 makefile has changed pretty dramatically since the port (and presumably the port diff). For example, it is modified to support cross-compilation, and has removed some hard-coded paths. I’ve made some manual mods to it such as setting MAKE to gmake, and am deleting patch-makefile, in the hopes that this will be enough.

Def not. It’s pretty badly broken. Awesome.

Ok, so back to building without the port.

The problem above is ixml.c attempting to #include malloc.h, which is deprecated in favor of stdlib.h. I commented it out in ixml/inc/ixml.h, but there’s more. A quick grep yields a few files with this same affliction:

 ixml/inc/ixml.h:/*#include <malloc.h>*/
 threadutil/inc/FreeList.h:#include <malloc.h>
 threadutil/src/LinkedList.c:#include <malloc.h>
 threadutil/src/iasnprintf.c:#include <malloc.h>
 upnp/inc/ixml.h:/*#include <malloc.h>*/
 upnp/src/genlib/util/upnp_timeout.c:#include <malloc.h>
 upnp/src/inc/client_table.h:#include <malloc.h>
 upnp/src/inc/http_client.h:#include <malloc.h>
 upnp/src/inc/service_table.h:#include <malloc.h>
 upnp/src/inc/uri.h:#include <malloc.h>

I’ll fix them all then.

Next up is a pthreads issue:

 ThreadPool.c: In function `SetSeed':
 ThreadPool.c:344: error: invalid use of undefined type `struct pthread'

One of the makefile patches in the port had to do with the path to pthreads.

….

Ok, I’ve gone through, error by error. Most of them were already solved in the FreeBSD port patches, although due to various subtle changes in the library sources, the patches won’t work automatically. For testing, I manually made the changes in the patches, and was able to do a build and install. However, I only applied the changes necessary to build; there may still be runtime problems that I don’t know about. I also had to change the install and uninstall targets, so files go to /usr/local/whatever instead of /usr/whatever/, which complicates the MediaTomb build later.

I’m now going to try to build MediaTomb and see how it runs.

First thing I discover is that the configure script needs alot of help. In order to get it to find iconv.h, I had to add CPPFLAGS=-I/usr/local/include, even though /usr/local/include is supposed to be where it’s looking by default.

Then I discovered I hadn’t fully installed libupnp after all. I kept getting:

 checking for upnp/upnp.h... no
 configure: error: upnp/upnp.h not found. Check libupnp installation

Even though upnp/upnp.h exists in /usr/local/include. The problem with configure in general is that it does its checks by trying simple code snippets in the compiler, and if they fail, concludes the test fails. However, without seeing the compiler error message, it’s hard to debug the problem.

I only figured it out when I read this in the README for MediaTomb:

Installation of this package is not straight forward.
tar zxvf libupnp-1.2.1.tar.gz
cd libupnp-1.2.1
cd upnp
make
make install
cd ../ixml
make
make install
cd ../threadutil
make
make install
cd ..
cp upnp/inc/ixml.h /usr/include/

I only did the make install (actually, gmake install) for upnp, not for the other two folders. Plus, I didn’t manually copy ixml.h. So, the configure test failed not because it couldn’t find upnp.h, but because when it tried to compile upnp.h, it failed since dependent files were missing. Lame.

Once I got configure to admit upnp.h was there, it ran to completion, with the following results:

 ======== CONFIGURATION SUMMARY =========
   sqlite3      : missing
   mysql        : yes
   java-script   : missing
   libmagic     : yes
   id3lib       : missing
   libextractor : disabled
   libexif      : missing

This isn’t acceptable. I want to use sqlite as the database backend, and will definitely need id3 support. java-script would be nice. I suspect there are ports I can install to address this issue.

libexif is in graphics/libexif. No brainer, that. It installed fine.

id3lib is in audio/id3lib. Also an obvious choice. Took a bit, but installed ok too.

From the configure output, it appears the java-script functionality is provided by SpiderMonkey java-script Engine. That’s in lang/p5-java-script-SpiderMonkey. It’s installing now, but might take a while.

While I wait, an interesting remark from the MediaTomb readme:

NOTE: Write operations to sqlite3 database is VERY SLOW, use sqlite3 only if you don’t have another choise.

This is interesting. In my experience with sqlite, there are few database engines faster. I wonder what he’s doing that makes it suck so bad. One thing that I found made a huge performance difference with sqlite is using large transactions. If you’re doing a bulk insert, you simply must do a bunch of them in a single tx; the overhead associated with starting and commiting a transaction is unusually high, but the overhead associated with operatins within a tx is virtually zero, so you owe it to yourself to use large transactions.

I took a peek at the database interface API; sure enough, it’s really lightweight: an exec and a query method, and just a little glue. Without transaction or bulk insert semantics, sqlite probably will suck.

Anyway, spidermonkey is done. Re-ran configure; it picks up everything I added, except SpiderMonkey. I get this:

 checking for jsapi.h... yes
 checking for JS_NewObject in -ljs... no
 checking for JS_NewObject in -lsmjs... no

So it finds the header file, but nfi what’s the deal with this JS_NewObject crap. Actually, I’m getting a similar problem with sqlite:

 checking for sqlite3.h... yes
 checking for sqlite3_open in -lsqlite3... no

Of course, if the sqlite3 library doesn’t have sqlite3_open, it’s totally broken. So, again, I really wish I could see what error configure is getting to make it think that.

Well, I haven’t figured out how to do that, but I did figure out the problem with configure not seeing the libs. Just as I had to pass CPPFLAGS=-I/usr/local/include to get it to see the include files, I also had to pass LDFLAGS=-L/usr/local/lib to get it to find the libs. Now the board is all green:

 $ ./configure CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib
 [snipped]
 ======== CONFIGURATION SUMMARY =========
   sqlite3      : yes
   mysql        : yes
   java-script   : yes
   libmagic     : yes
   id3lib       : yes
   libextractor : disabled
   libexif      : yes

Sweet. Now let’s pull the trigger on the build.

I knew it couldn’t last:

 ../src/string_converter.cc: In member function `zmm::String StringConverter::convert(zmm::String)':
 ../src/string_converter.cc:72: error: invalid conversion from `char**' to `const char**'
 ../src/string_converter.cc:72: error:   initializing argument 2 of `size_t libiconv(void*, const char**, size_t*, char**, size_t*)'

Hmm

The line in question is:

 ret = iconv(cd, input_ptr, (size_t *)&input_bytes,
                 output_ptr, (size_t *)&output_bytes); --right here

I suspect the problem is input_ptr, which is a char **. By convention, input strings are const in C, so I assume the iconv method expects a const char**. I’ll try to cast it explicitly, but I suspect this belies a larger problem with the compiler settings; this really should be a warning, not an error, with all due respect to John Robbins.

Well, that got me past it. Now it fails with my old friend:

 In file included from ../src/zmm/stringbuffer.cc:23:
 /usr/include/malloc.h:3:2: #error "<malloc.h> has been replaced by <stdlib.h>"

I had to fix a bunch of these in libupnp as well; I just replace them with stdlib.h

 $ grep -r malloc.h *
 src/zmm/stringbuffer.cc:#include <malloc.h>
 src/zmm/strings.cc:#include <malloc.h>
 src/zmmf/array.cc:#include <malloc.h>

That didn’t carry me much further:

 ../src/zmmf/exception.cc:24:22: execinfo.h: No such file or directory
 ../src/zmmf/exception.cc: In constructor `zmm::Exception::Exception(zmm::String)':
 ../src/zmmf/exception.cc:36: error: `backtrace' undeclared (first use this function)
 ../src/zmmf/exception.cc:36: error: (Each undeclared identifier is reported only once for each function it appears in.)
 ../src/zmmf/exception.cc:40: error: `backtrace_symbols' undeclared (first use this function)

Looking at the code in exception.cc, it has some conditional logic to exclude the execinfo.h and associated functions if __CYGWIN__ is defined, presumably because Cygwin doesn’t have execinfo. Well, FBSD doesn’t either, so behold my mod to exception.cc:

 //anelson: FreeBSD doesn't have execinfo either, so present we're cygwin
 #define __CYGWIN__

Sometimes I amaze myself.

Next up is a linker error in libupnp:

 /usr/local/lib/libupnp.so: undefined reference to `gethostbyname_r'
 /usr/local/lib/libthreadutil.so: undefined reference to `ftime'

I feel like we’re almost there. gethostbyname_r is a reentrant version of gethostbyname.

From [http://www.unobvious.com/bsd/freebsd-threads.html]():

Some of these functions are available, some (notably the gethost* and other name resolver functions) are not. They are implemented as part of Linuxthreads. Note that the Linuxthreads implementations are “wrappers” around the non-reentrant forms of these functions. This has two implications. First, the performance will not be as good as if they were “natively” implemented (only one call to gethostbyname_r will actually do a lookup at any given time), and second, the Linuxthreads implementation can be used with the user threads library (as they depend on POSIX mutexes to lock the critical section).

That’s awesome, except the Linuxthreads port won’t build on amd64. Doh. Maybe there’s some way to get libupnp to not try to use the reentrant functions? No; the code that makes the call has no conditional compilation. According to this list post, this is a known issue. Awesome. Thanks alot.

I’ve added

 #define gethostbyname_r gethostbyname

To upnp/src/inc/uri.h, in the hopes this will address the issue.

Hmm, no, it’s more complicated than that. The caller in uri. is passing in five or six args, but gethostbyname is only supposed to take two.

I don’t think this is going to work.

Since all the media servers I could find depend in libupnp, and libupnp is totally broken on FreeBSD, I’d say I’m pretty well fucked.

Astonishingly, it appears that there is a NetBSD pkgsrc package for libupnp12. Maybe I can use the diffs there? Then again, maybe NetBSD doesn’t suck as bad as FreeBSD, and has things like gethostbyname_r, etc.

Well, I was able to apply all the NetBSD patches without error. I had to re-fix the references to malloc.h, and remove the LD_PATH added by the NetBSD patches to upnp/src/makefile, but otherwise it worked unmodified.

Now I’m getting link errors in the libmediatomb with pthread functions; I suspect a missing -l switch. I added CXXFLAGS=-pthread to the configure call and it proceeded. Now the problem is:

 /usr/local/lib/libthreadutil.so: undefined reference to `ftime'

This is libthreadutil.so, which is part of the libupnp build.

From a list post, there’s a -lcompat argument that adds legacy stuff like this. I’ll try adding that to the CC command when building libthreadutil.

Ok, when I built libupnp, I did:

gmake EXTRA_LIBS=-lcompat, then did another install with gmake PREFIX=/usr/local install, and it seemed to do the trick. I was able to build mediatomb through to the end with:

  ./configure CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib CXXFLAGS=-pthread
  make

Now I did a make install, and that’s it.

According to the readme, the next step is tomb-install, which will create a configuration section in ~/.mediatomb. tome-install is in /usr/local/bin:

  $ /usr/local/bin/tomb-install

  MediaTomb installer 0.3

  Proceeding normally
  Creating directories... ok
  Creating database... ok
  Writing configuration... ok

  All done. You are now ready to launch MediaTomb!

  WARNING: Because of security reasons the UI is disabled by default!
  Please refer to the README file on how to enable it.

  Once it is enabled:
  When the server is running you can access the UI by opening
  ~/.mediatomb/mediatomb.html in your web browser.

The readme says it will default to sqlite3, since that can be done automatically, but that you should really use mysql instead. I don’t care; I want to see that it’s working first.

The script you’re meant to start with is mediatomb-service, but it’s based on init.d scripts in /etc/rc.d/init.d, which isn’t how FreeBSD does things.

So, I’ll try my hand at the command line:

 $ mediatomb -d -u anelson -g anelson -P /home/anelson/mediatomb.pid -l /home/anelson/mediatomb.log
 Pid file: /home/anelson/mediatomb.pid

That didn’t work. The process died for some reason; I imagine it doesn’t like not being run as root.

  $ tail mediatomb.log
  2005-10-27 13:57:35 INFO: Config: option not found: /import/metadata-charset using default value: ISO-8859-1
  2005-10-27 13:57:35 INFO: checking ip..
  2005-10-27 13:57:35 INFO: Config: option not found: /server/ip using default value:
  2005-10-27 13:57:35 INFO: Config: option not found: /server/bookmark using default value: mediatomb.html
  2005-10-27 13:57:35 INFO: Config: option not found: /server/port using default value: 0
  2005-10-27 13:57:35 INFO: Config: option not found: /server/alive using default value: 180
  2005-10-27 13:57:35 INFO: Config: option not found: /import/magic-file using default value:
  2005-10-27 13:57:35 INFO: Configuration check succeeded.
  2005-10-27 13:57:35 INFO: Config: option not found: /server/ip using default value:
  2005-10-27 13:57:35 INFO: got ip: (null)

Nothing suggesting a failure…Ah, look at this from /var/log/messages:

 Nov 27 13:57:35 aenea kernel: pid 95269 (mediatomb), uid 1001: exited on signal 11

Great. I’m out of my depth when it comes to debugging this stuff.

Time for a crash course (pun accidental) in gdb.

First, I just start gdb with the name of the image to run:

 gdb mediatomb

It starts, and gives me the standard prompt:

 (gdb)

I can type run followed by any arguments, and it’ll run the process under the debugger. When I do, I get this interesting output:

  (gdb) run -d -u anelson -g anelson -P /home/anelson/mediatomb.pid -l /home/anelson/mediatomb.log
  Starting program: /usr/local/bin/mediatomb -d -u anelson -g anelson -P /home/anelson/mediatomb.pid -l /home/anelson/mediatomb.log
  warning: Unable to get location for thread creation breakpoint: generic error
  [New LWP 100174]
  Pid file: /home/anelson/mediatomb.pid
  [New Thread 0x59e000 (LWP 100174)]

  Program exited normally.
  (gdb) Fatal error 'mutex is on list' at line 540 in file /usr/src/lib/libpthread/thread/thr_mutex.c (errno = 0)

My developer sense tells me the meaningful error is: Fatal error 'mutex is on list'. Google, what say you?

Nothing but complaints yet, but I learned about bt, which produces what I in my Microsoft Visual Studio nomenclature would call a ‘call stack’. It is:

 (gdb) bt
 No stack.

WTF?

Amazingly, the only list posting seems to be in reference to this happening whilst running the JDK, and no responses whatsoever. Outstanding.

Looking at the system source code thr_mutex.c line 540, this message is coming from an assert. From my read, it appears it happens if trying to lock a mutex that is already locked.

I’m going to go out on a limb and conclude that mediatomb isn’t using pthreads as FreeBSD expects. From this great article on programmming threads in FreeBSD, I conclude that I should try to build with libc_r instead.

Sadly, I can’t figure out how to do that. I did come across a useful tool: ldd, when given the path to an image, dumps the shared libraries that image links to. mediatomb links to libpthread.so.2.

Never mind; this 5.0 roadmap doc makes clear that libc_r is being deprecated as thread support is going into the kernel directly.

At any rate, I’m pretty clearly screwed. I’m out of my depth, and may have to slink back to Linux to run my media server.

Delicious Bookmarks

Recent Posts

Meta

Current Location