Share this project


Share this project

A free software fixed media format for HD video, based on open standards, with metadata and localization but no DRM restrictions
A free software fixed media format for HD video, based on open standards, with metadata and localization but no DRM restrictions
467 backers pledged $21,421 to help bring this project to life.

Recent updates


F8578d2e108dd9ed0b6c53351cff7ec9 original
6939f2caa7bd8d9c9b18d1f1baa12fe7 original
2c02c7ab46846bf7c24531d08d227fa5 original
99d2cb22db20bdb295aff49d08cebbd4 original
213aa49cbefe5f7d06489a3133a4e478 original
9ad410f7d2ed02651e5cd93e9bd979c1 original
34942c0cb0db485098d2ca0220e40ca3 original
3efcdefe7fde0131c2553db814eae247 original

Localized Menus in Rocket


This week, I have got the Rocket library loading and correctly displaying menus with localization. This involved a considerable amount of work to get character sets, fonts, and the internal representation for locales to work correctly. There's still quite a bit of polishing to do, but I think I've identified the trouble spots and worked around them.

Russian locale menu.
Russian locale menu.

Obviously, we need Unicode fonts with very good coverage of character sets. For my test page, I used Google's "Noto" fonts, which are designed for maximal coverage with fairly consistent styles.

Spanish locale, showing both types of language localization.
Spanish locale, showing both types of language localization.

In some cases, we need to use different fonts for the same characters. For example, there is "traditional Chinese", preferred in Taiwan and "simplified Chinese", preferred in mainland China.

Chinese text with CN locale to select "simplified" font. There's no explicit Chinese translation on the test page, so default text is shown at the bottom.
Chinese text with CN locale to select "simplified" font. There's no explicit Chinese translation on the test page, so default text is shown at the bottom.

Since Rocket provides no way to allow one font to fall back to another for characters that aren't supported, we can't use a single font for a whole page with multiple languages on it. Instead, we specify languages at the XML element level, and get Rocket to select an appropriate font based on the language.

This was necessary to get Devanagari and Arabic locales to display along with the untranslated English original on my test page.

Arabic text tests right-to-left rendering, and also mixing fonts on screen to support different languages for translated and untranslated text.
Arabic text tests right-to-left rendering, and also mixing fonts on screen to support different languages for translated and untranslated text.

Two Types of Language Support

There are two different localization systems being demonstrated in this test. The first is player-based localization, in which the player provides the translation for known "labels". For example, the PLAY button at the top looks like this in the test menu file:

<a href="../media/feature.lrv"><h1 label="PLAY">Play</h1></a>

The "label" doesn't strictly translate the button at all, but just tells Lib-Ray what the label means. The player then provides the correct translation or else falls back to the original text ("Play").

This has some advantages:

  • The volume author does not need to know or provide multiple languages.
  • New languages added to the player will localize pre-existing releases.
  • Different releases will use the same vocabulary for their functions, avoiding potential confusion for users.

And a few disadvantages:

  • The volume author is limited to the available labels, or must use some that won't be translated.
  • The problem of translation for labels lies with player developers.
  • Can't be used for more sophisticated text than button labels.
Hindi version (I hope).
Hindi version (I hope).

My initial release includes 13 concept labels with 12 translations for each, into the "language system" languages (the theory being that most people in the world will understand at least one of these, even if it is not their native language): English, French, Portuguese, Spanish, Russian, German, Arabic, Swahili, Chinese, Japanese, Hindi, Indonesian (Malay). Future releases might include more concept labels.

I did make an effort to get correct in-context translations for each of the labels, but it's practically inevitable that I will have made errors. I'm hoping that native speakers will be able to provide improvements to these where needed. And of course, I'll be relying on submissions for additional languages.

The player will not attempt to translate more sophisticated text. Instead, the release author will have the option to provide alternate translations where needed.

The last button in the examples above is localized in this way. Translations were provided for English, French, Spanish, and Russian in the test page. So if one of those locales is used, then the appropriate text shows up, but if not, then the default text ("No Translation Provided") is presented, as you can see in some versions.

Here's what the full block looks like in the test menu:

<span langblock="single">
<h2 lang="en">This is English.</h2>
<h2 lang="fr">Ceci est Français.</h2>
<h2 lang="es">Esto es Español.</h2>
<h2 lang="ru">Это русский.</h2>
<h3>No Translation Provided.</h3>

Roadmap to Release

The next few steps of development should get us to a minimally-functional player:

  • Scan for 'href' attributes and assign callbacks (both the scan and callback assignment have been tested, so this shouldn't be hard at all).
  • Parse and distinguish the different classes of URLs: other menu pages, extras pages, remote URL links, and player actions. This is a simple application of Python's 'urlparse' library.
  • Spatial navigation for keyboard or remote-control support (i.e. pushing the "left" arrow will go to the button that is presented to the left of the currently-focused button -- this sounds really trivial but is actually kind of tricky to implement).
  • Write an outer loop to scan for and load Lib-Ray volumes when they are inserted.
  • Implement infrared remote control support via LiRC/PyLiRC.

Seems simple enough.

I hope it goes quickly. After that and a bit of polishing, we should have a minimally functional Lib-Ray player, and I can focus on the other parts of the Kickstarter delivery: in particular documentation, mastering the test releases, and fulfilling rewards for backers.

After that, I'll continue with extended goals:

  • A wizard to aid in creating Lib-Ray volumes.
  • Creating an "archive" or "library" mode to browse Lib-Ray titles.
  • Packaging the software to simplify installation.
  • Creating an inexpensive embedded player.

Right now, I can't see any reason why this should take more than a month to get to the player software release, or more than a few months to finish the whole project, but I've been very wrong before, so I'll try to just stick to reporting what's done.

Not my proudest moment as a programmer, but...


All right. I have, I believe, resolved the nasty bug I was dealing with, and now understand how to do event callbacks with Rocket. The API for this is seriously twisted, and I could rant about how bad the design is, but the important thing is that I am able to use it anyway.

Still, I suppose I'll rant a little anyway.

The API works by pushing the context variables from the page context in which the call was made into the global namespace. There's two variables: "event" (which contains the data from the event that happened) and "self" (which contains the Rocket "RML Element" object which generated the call. There's supposed to be a third variable, "document", but my tests didn't show it being defined (maybe it's not always available? I'm not sure).

You read that right, Python programmers: the call sticks an object named "self" into the global namespace.

If the callback is a method, as happens to be necessary in my use case, then you have to use a non-standard method call with some other name for "self" and grab the "element" from the global namespace.

What happens next is a style decision, and it's not clear to me which is clearer. Either you can just keep using the non-standard name for "self" (that is, the name for the parent object of the method). Or you can delete the global "self" and rename the local variable back to "self" so that the rest of the method works. (The snippet below uses the latter solution, but I don't know if I'll keep it that way or not).

It's ugly as hell, and such an API erodes my confidence in this package a bit. But I think (and hope) the rest of the package is more sensible.

Wow. I did not expect to need this.
Wow. I did not expect to need this.

Anyway, it DOES work.

It also does work even when VLC is playing video -- I wasn't sure whether that would work or if I'd have to implement a listener through VLC as well. So that's a relief.

I'll need to do a little clean up of all the introspection and instrumentation code I added to figure out what was going on. But after that, I should be able to move onto implementing menus correctly.

So... yay!

Status: Bug Hunting


It's been awhile since I posted anything, and I had expected to have the player more finished by now, but I ran into some bugs with the underlying C++ libraries (or one bug from there and one I haven't isolated yet, but I suspect will turn out to be similar).

The first bug, in the Rocket Standalone module, took some time to ferret out. It turns out you can have some really subtle Python bugs when the module isn't actually written in Python -- this one was a reference-counting error which resulted in executing a random function as a callback (just whatever happened to be in that memory location).

And now I'm stuck on one with the event handling system in the Rocket library proper.

For some reason I'm getting a  "Keyerror" traceback which is supposed to mean that Python is attempting to do a dictionary look-up and not finding anything, but the weird thing is that the line it reports the error in doesn't even mention a dictionary look-up. Most likely this is another case of something not being done quite right in the C++ module, and it'll either have to be patched, or I'll have to find a work-around to avoid triggering it.

If this sounds trivial, well it does to me too, but it still blocks me from moving forward, as I really need a working event system to do testing of the menu system.

Right now, I just need to isolate the bug, and I think it's going to take several hours of careful testing to find it, since the traceback is apparently not going to be very helpful. I expect to block some time next Tuesday-Wednesday to do that. Really hoping that'll be enough time to find it.

Somewhat related (in that it's the reason I'm having to deal with this problem now): There's a small error in the architecture diagram I drew earlier. It turns out I can't just use the Python standard library to do the keyboard input, because it depends on the window focus. Instead, I'm using Rocket's event system to monitor the keyboard. I'm not sure yet whether it will be possible to use that while VLC is running or if I will have to switch back and forth depending on the player mode. I'm prepared for either case, though.

I'll post again once I figure out what's going on with that. Thanks for your enormous patience, once again.

Rocket/VLC-Standalone Python Extension Module Completed


Last year, I announced the decision to use the Rocket library for menus rather than WebKit, primarily for security reasons, although it's also worthwhile to make the package simpler to write and easier to read -- which is important if it's to do a good job as a reference implementation.

Rocket solves these problems by being a much simpler, thinner interface layer (it really is just the renderer -- actually following links will be handled by callbacks to my code, which allows me to completely control the browsing process, making it both easier to write and more secure).

What I did not mention in my previous post (because I did not realize it until I started running tests in early December), was that Rocket is actually not a complete Python extension module. This is not really clear from the documentation (I may have to do something about that!), and I just figured I was having some trouble initializing it correctly (after all, the demos ran fine). Well, it turns out that, for me, "Rocket is only half a rocket-ship". The other half has to be provided by a backend rendering library -- something like OpenGL or SDL2. And that work has to be done in C++, you can't fix it from Python.

So I needed to get the other half of the rocketship built.

I was stuck on that for awhile, but ended up commissioning Tom Mason, who is the author of "Freeablo" (a free-software alternative to the Diablo game engine), and who therefore has C++ experience with Rocket and the SDL2 backend, which is what I wanted to use.

He released this to me just last week, and I've been running tests with it. Everything seems to be working exactly as it should, and it even works in the same logical X Window as VLC, so integration of menus and playback is very smooth and quick.

So this part is now done.

I wish I could do justice to how much better and easier this makes my situation!

There is still quite a bit of Python code to write, but it is much simpler, more familiar to me, and all focused on getting the desired Lib-Ray player behavior.

This will become a new "ichinose" branch in the SVN, as soon as I get the source organized. Here's a block diagram of this new Rocket-based architecture:

Rocket-Based Lib-Ray Player Architecture
Rocket-Based Lib-Ray Player Architecture

The orange "Rocket-Standalone" is what Tom Mason just completed, which allows all of the C++ stack to fit together. The Python layer above that are existing Python bindings for these libraries. The Lib-Ray Player Python application sits on top of that -- and that's what I'll be working on over the next month. Some pieces of this, like the Volume Object Model and the Localization code can be used with little or no re-writing. The "Menu-Browser" and "Video" modules will pretty much have to be completely re-written (although they will now be a lot simpler and easier to understand).

I will add support for infrared remote controls (probably based on "liRC") as well as mounting and automounting the UDF volume file-systems after the basic stack is all working with pre-mounted volumes and keyboard/mouse control.

I guess the stickiest part now is that I'm going to have to do some translation to go from a more standard XHTML+CSS markup on the Lib-Ray volume to the RML+RCSS markup that Rocket understands. This is mostly a subset, which I could just declare to be standard for Lib-Ray. But there are some oddities that need to be seen to:

1) Background images are handled in a non-standard and more complex way. This is undesirable since Lib-Ray menus will probably use them extensively. So I'll need to fix this in the filtering process.

2) Rocket doesn't actually understand hyperlink ( A-HREF ) tags at all, it just allows you to set event handlers on any element. So my filter will need to find the tags, read the URLs, and assign handlers.

Those are simple, easy-to-test text-filtering tasks. Very familiar territory.

After that, I'll need to implement hyperlink navigation, including dispatching my special URLs for sending messages to the VLC video player. Of course, this fine control over navigation was one of the main reasons to choose Rocket.

I'm hesitant to make projections because of all the ones I've made that have fallen through on this project, but I really do think I'll be done with the player by the end of March now.

Menu Internationalization Testing


I've completed the primary work on internationalization tests for Lib-Ray. After much searching for a better alternative, this is a combination of using ElementTree and simple regular-expression-based corrections to the output. The result works very neatly, as you can see (these are from the test example included in the module -- I'm not quite sure if I can or should try to convert this to a self-checking unit test or not).

BTW, the translations are mostly by me at this point, using online resources, and I obviously don't speak all of the 12 key "language system" languages that will be included in all Lib-Ray players. So I will be soliciting corrections from native speakers sometime soon!

The mechanism is very simple. The most important thing to internationalize in Lib-Ray are the functional button labels. This context allows for unambiguous vocabulary-based translation. An attribute is added to an HTML element, and if recognized, the text within the element is replaced with the localized text. At present, the attribute is "label", although I'm reviewing whether that's the best choice in terms of HTML and XML standards (other possibilities: "id", "libray:label", etc).

One nice thing about this is that it really is not dependent on the author of the release to do the internationalization. The button text is changed by the player itself, so the internationalization and localization is just done there. As long as you have a language pack for your locale installed, all Lib-Ray volumes will have their buttons correct for your locale.

There is one issue for authors to consider: the length of words may be quite different. You can see here that the Russian and Chinese for "PLAY" differ by a lot in size on screen, for example. This will be something to take into account in designing your menus.

A secondary mechanism for authors to provide alternate text for different languages is yet to be developed, but should be possible using the same ElementTree-based manipulation of the source menus (and I think there is already a standard way to do this in HTML, so we'll just honor that, if that's true). However, I'll probably defer that until after we have a working player released.

Testing Menu Localization
Testing Menu Localization