Developer documentation

jabbors edited this page Oct 14, 2014 · 20 revisions

The Kolibre client libraries consist of 6 independent libraries and 1 library utilizing the functions from the other libraries resulting in a client core library which can be used to build a client for playback of accessible media.

libkolibre-narrator

libkolibre-narrator is a library for playing audio prompts. It provides necessary interface methods to play numbers, dates, time, durations and strings stored in a message database. The package includes a set of utilities that can be used to generate the messages database.

Features

  • Narration of pre-recorded audio messages
  • Narration of numbers from 0-9999
  • Narration of date and time
  • Spelling of mixed strings
  • Voice manipulation

libkolibre-player

libkolibre-player is a library for using gstreamer for playback of audio content such as mp3, ogg or wav. It provides necessary interface methods to open a selected segment of content from a file source or http source and to adjust the tempo, pitch and volume of the playback.

Features

  • Pure lightweight streaming of online or offline content
  • Pause and resume
  • Voice manipulation

libkolibre-xmlreader

libkolibre-xmlreader is a library for fetching and parsing XML data. It is a SAX wrapper library for libxml2. The library can load XML/XHTML/HTML documents from disk or http. A caching function is built in to speed up re-parsing of previous XML documents. It also has an optional dependency on libtidy, which can be used as a fallback for cleaning up messy and malformed XML files. These features makes the usage of xmlreader simple, fast and reliable.

Features

  • Online and offline parsing of XML files
  • Caching of recently parsed files
  • Auto correction of malformed XML files

libkolibre-amis

libkolibre-amis is a branch of the amis2 code base. The library code base is refactored to suit the needs of the Kolibre client as a daisy navigator and parser. The library uses libkolibre-xmlreader for parsing XML files. It provides useful functionality as, caching remote resources and a tidy fallback for cleaning up messy HTML files.

Features

  • Daisy 2.02 and Daisy 3 support
  • Bookmark management
  • Continuation from last position

libkolibre-daisyonline

libkolibre-daisyonline is a library built for utilizing the daisy online protocol defined by the Daisy Consortium. The library uses defined data structures that are populated via methods defined in the daisy online protocol. This allows the programmer to focus on the goal of the communication, rather than how to create, send and receive the specific messages.

Features

  • Daisy Online compliant
  • 14 of 15 API operations supported (getKeyExchangeObject not implemented)
  • SOAP Fault support

libkolibre-naviengine

libkolibre-naviengine is small library providing a engine and basic nodes for building a simple traversable menu three. The engine defines the interface methods and implements basic operations for traversing in the tree. The nodes can easily be extended to suite your needs. The library also defines interface methods for rendering the nodes visually or audibly.

Features

  • Simple and easy to extend
  • Uniform navigation controls

libkolibre-clientcore

libkolibre-clientcore is a core library for listing accessible media, i.e. digital talking books, from online and offline sources. The library also supports playback, navigation and bookmark management for supported content formats. The library uses voice narration and can thus easily be utilized to create an easy-to-use playback client for visually impaired.

Features

  • Daisy Online compatible client with support for the required operations.
  • Multiple Daisy Online services
  • Offline playback
  • DBus monitoring for external storage devices (UDisks2)
  • Pure streaming client
  • Voice manipulation
  • Bookmark management and continuation form last position
  • Sleep timer and automatic shutdown
  • Uniform navigation
  • Page, Percent, Time or Uri jump-to during playback

Classes

This library is built upon the libkolibre-naviengine library which allows us to focus on the logic for fetching and playing content and simultaneously maintain an easy traversable three of content and context menus. Most of the classes in the component either extend a MenuNode or a VirtualMenuNode defined in the naviengine namespace, but it also contain classes for storing user settings and handling commands within this component, (see figure below). The purpose of each class is described in more detail below.

Inheritance diagram for classes in clientcore component

ClientCore (public)

This is the public interface of the component. Everything in this component is controlled via this class.

The public interface contains a few getters and setters which are used to change the default configuration for the object as well as to view the current configuration. There are also getters and setters to control the sleep timer.

When the object is configured properly the instance is mostly operated via the pushCommand, jumpToSecond or jumpToUri method. These operators do not respond with direct feedback about the outcome of the operation. Instead the user must listen on signals defined in this interface. These signals (callbacks in other terms) inform the user what has changed or what happened in case of an error. Some of these signals are empty and some even contain data. Refer to the doxygen documentation for more information about the signals and their purpose. One does not have to listen on these signals at all, but when connecting this library to a GUI it is a must.

The commands which can be push via the pushCommand can be categorized into three groups. We have commands for traversing the menu three and opening context menus, voice manipulation and sleep timer control. Refer to the doxygen documentation for more information about these commands.

CommandQueue (private)

This is a thread safe command queue allowing the application to send different types of commands, with or without data, via a general command handler to one or more command handlers. It is a singleton class and visible to all private classes.

The command queue is a First-In-First-Out queue and when its dispatch method is invoked the first command in the queue is popped and the handle method of the command handler listening on the command is invoked. Zero or more handlers may be listening on a command. If several handlers are registered for a specific command the command handler invokes the handle method of the handler that was first registered for the command.

Navi (private)

This class is an implementation of the semi-abstract NaviEngine class in the naviengine namespace. This class is the core of this component and keeps track of all nodes added to the menu three and sends commands to the correct node.

MediaSourceManager (private)

This class a is utility to manage online and offline sources which have been added via the ClientCore API. It is a singleton class and visible to all private classes.

Settings (private)

This class is a utility for storing user settings in a SQLite3 database so that the settings can be remembered between sessions. It is a singleton class and visible to all internal classes that need to store or read settings.

DaisyNavi (private)

This class contains logic for opening and navigating in a Daisy 2.02 publication. It also manages bookmarks for the publications.

RootNode (private)

This class is the main menu node and has one or many children of type DaisyOnlineNode/FileSystemNode. Via this class, the user can navigate between different types of sources available to the user.

DaisyOnlineNode (private)

This class contains logic for communicating with a Daisy Online service. When a session has been established new content on the service is automatically issued and finally a list of issued content is presented for the user. Each content item available from the service is added as a DaisyOnlineBookNode child to this node.

FileSystemNode (private)

This class contains logic for browsing offline content stored on the file system. Each supported content found in the file system path is added as a DaisyBookNode child to this node.

DaisyBookNode (private)

This class contains logic for presenting offline books to the user. All other actions in this node is directed via the DaisyNavi class to allow playback to the offline book.

DaisyOnlineBookNode (private)

This class contains logic for fetching a list of online content resources via the Daisy Online interface. All other actions in this node is directed via the DaisyNavi class to allow playback to the online book.

TempoNode (private)

This class let the user change playback speed via the context menu.

SleepTimerNode (private)

This class lets the user activate or deactivate the sleep timer via the context menu.

AutoPlayNode (private)

This class lets the user enable or disable automatic opening/playing of nodes via the context menu.

GotoPageNode (private)

This class lets the user jump to a page in a Daisy publication via the context menu.

GotoPercentNode (private)

This class lets the user jump to a specific position given in percentage in a Daisy publication via the context menu.

GotoTimeNode (private)

This class lets the user jump to specific position given in hours, minutes and seconds in a Daisy publication via the context menu.

BookInfoNode (private)

This class presents information about a Daisy publication to the user.

Communication flow

The libkolibre-clientore library is multi-threaded library. The library itself only creates one thread when a ClientCore object is created, but more threads are created when the other libraries are initialized. libkolibre-narrator and libkolibre-player uses threads for its audio playback and libkolibre-amis also uses a thread for its parsing and navigation.

User actions (invoking of pushCommand, jumpToSecond or jumpToUri) enters via the ClientCore thread and depending on the type of action it is either handled in the main thread or in other libraries threads. See the sequence diagrams below illustrating the flow for some typical user scenarios.

Sequence diagram for push commands

In this user scenario the DaisyOnlineNode is the active node and it has several children of DaisyOnlineBookNode type.

When command NEXT is pushed to ClientCore it pushed directly to the CommandQueue2. At some time the dispatch method will be invoked causing an invoke of the handle method in the command handler for the command. This handler exists in ClientCore and re-map the NEXT command to the internal COMMAND_RIGHT command. This command is then passed as an argument to Navi's process method which will invoke the next method on the current node which in this case is the DaisyOnlineNode. This will cause a change in the naviengine the current child is changed (this is not visualized here in the sequence diagram).

When command DOWN (a request to open the current child) is pushed to ClientCore we see the same flow of actions, except for an invoke of the select method instead of next. This causes a change in naviengine (not visualized here) and the current child becomes the current node which will result in further invoking of more open related methods.

Finally when the last NEXT command is pushed to ClientCore it will result in the invoke of next for DaisyOnlineBookNode which now is the current node which will invoke the next method for DaisyNavi.

Sequence diagram for jump commands

The communication flow in this user scenario is very similar to the scenario above. The only difference here is that invoke of jumpToSecond or jumpToUri is translated to a jumpCommand. The jumpCommand is actually two different types of commands, one command contains an integer value and the other contains an string value. These two command has two handler, one for integers and the other for strings and they exist in different object. The integer handler exists in DaisyNavi and the string handler in Navi.

You may also have noticed that the communication flow also differs for the jumpToUri invokes. In the first scenario DaisyOnlineBookNode is the current node which also is a VirtualMenuNode. Since it is virtual node, Navi is not aware of its children and must invoke the selectByUri command and let the node handle the select itself. In the second scenario DaisyOnlineNode is the current node which is a regular MenuNode. Navi is now aware of its children an can handle the select itself.

How to use libkolibre-clientcore

All the libkolibre libraries uses the log4cxx framework for logging but the none of them configures the log level or log format, thus the first you should do is setup the logging. Every library component has its own parent logger name kolibre., thus the parent logger for libkolibre-narrator is named kolibre.narrator. The root logger is named kolibre.

Step 1: Create a root logger outside main

#include <log4cxx/logger.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/helpers/exception.h>

// create a root logger
log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("kolibre"));

Step 2: Configure the logger either using the PropertyConfigurator or the Basic

// Setup logging
char *logConf = NULL; // path to log configuration file
try
{
    if (logConf != NULL)
    {
        // set up a user defined configuration
        log4cxx::PropertyConfigurator::configure(logConf);
    }
    else
    {
        // set up a simple configuration that logs on the console
        log4cxx::BasicConfigurator::configure();
    }
}
catch (log4cxx::helpers::Exception&)
{
    printf("Configuration of logger failed\n");
    return -1;
}

Step 3: Set the language using the static setLanguage method. Language can not be changed during runtime.

ClientCore::setLanguage(language);

Step 4: Initialze the ClientCore object and add at least one online or offline source.

ClientCore *clientcore = new ClientCore();
clientcore->addDaisyOnlineService("name", "http://yourdomain.com/daisyonline/service.php", "username", "password");
clientcore->addFileSystemPath("home", "/home/user/");

Step 5: Start threads and loop until the client is not running anymore

clientcore->start();
while (clientcore->isRunning()) usleep(100000);

Now all you need to do is implement the logic for pushing commands to ClientCore. Please refer to the sample client application for an example how it can by done with the use of boost signals.