SDK Usage Guidelines
This section describes the object-oriented design of the C-API as well as the thread management considerations when using the SDK.
Object Model
This section describes usage of the C interfaces for the ExpressPlay SDK, which provide access to the ExpressPlay SDK framework supplying all the functionality needed by Marlin-related media applications. The ExpressPlay SDK can be used to download media files and streams; parse files; access file information; find, download, manage, and check licenses; obtain decryption keys; and decrypt Marlin-protected content.
The ExpressPlay SDK includes C interfaces to all the functionality it provides, and JavaScript and Java interfaces for portions of the functionality. The document you are reading concentrates on the C interfaces, although the general information provided, and descriptions of what the various components do, will be relevant no matter which interfaces are used.
Note: This document summarizes and provides usage information for the API classes and their methods. It does not provide a detailed API reference documenting all the data structures, method parameters, etc. That information is contained in the SDK API reference section of the Developer Center.
A complete C interface to the ExpressPlay SDK is provided, composed of a few top-level functions and a number of object classes.
Even though the interface is an ANSI C interface, it adopts an object-oriented style.
The header files declare object classes. An object class defines the functional interface to the class (a set of methods).
Once an instance of a particular type of object is created, subsequent calls to methods operating on that instance have an initial argument that is a reference to the instance. The data type that represents references to object instances is a pointer to an opaque C struct. It may be considered as analogous to a pointer to a C++ object. For example, for a class named WSB_Example, the data type WSB_Example is the name of a C struct. A method of WSB_Example may be called SomeMethod. The function name for that method is WSB_Example_SomeMethod(), and the function takes a WSB_Example* as its first parameter.
Obtaining Class Instances
An instance of a class is obtained by declaring a pointer to an object for the class and passing the address of that pointer to a particular function. The function creates the instance and sets the pointer to refer to it.
For example, a WSB_MediaFile object represents information about a particular media file. A WSB_MediaFile instance is obtained by calling WSB_MediaFile_Open, which is declared as follows:
WSB_EXPORT WSB_Result
WSB_MediaFile_Open(const char* name, const char* mime_type, WSB_MediaFile** file);
The first parameter is a filename and path or an HTTP URL specifying the file location. The second parameter is optional and is used to specify the MIME type, if known. The final parameter is the address of a WSB_MediaFile pointer. This method opens the specified media file, and sets the pointer to refer to a newly-created WSB_MediaFile object referencing the media file. Here is a sample call:
WSB_MediaFile* media_file = NULL;
WSB_Result result;
result = WSB_MediaFile_Open("file:///media/movie.mp4", NULL, &media_file);
Making Method Calls
In the C object-oriented style for the API, a call to a method of a particular instance is done by calling a function and passing a pointer to the instance as the first parameter.
For example, once a WSB_MediaFile instance is created, as shown in the previous section, all the WSB_MediaFile methods can be called to operate on that instance. One such method is the WSB_MediaFile_GetMetadata method, which is used to obtain all the metadata for the media file referenced by the WSB_MediaFile instance. This method is declared as follows:
WSB_EXPORT WSB_Result
WSB_MediaFile_GetMetadata(WSB_MediaFile* file, SHI_Attribute** metadata);
Assuming media_file is the pointer previously set by WSB_MediaFile_Open to refer to the WSB_MediaFile instance it created, WSB_MediaFile_GetMetadata can be invoked by the following:
WSB_Result result;
SHI_Attribute* metadata = NULL;
result = WSB_MediaFile_GetMetadata(media_file, &metadata);
Note: This method call also illustrates the creation of a SHI_Attribute object instance, whose (metadata) pointer can then be used as the first parameter to SHI_Attribute methods.
DRM Engine
SDK Initialization
The first function that must be called by each application before calling any methods of the ExpressPlay SDK API is WSB_Runtime_InitializeEx. That function initializes the SDK for the application. It must be called once for each application that will utilize the SDK.
In case there is any doubt as to whether WSB_Runtime_InitializeEx has already been called, an application can first call WSB_Runtime_IsInitialized and only call the initialization function if WSB_Runtime_IsInitialized returns WSB_FALSE.
Obtaining SDK Information
The ExpressPlay SDK contains two levels of DRM abstraction. The higher level WSB_ APIs are more commonly used within application code to control the DRM functions. There is also a lower-level SHI_ API set (or Sushi APIs) which provide more fine-grained control over the DRM engine, but in many cases it requires that the developer understand the inner-workings of the DRM engine. After WSB_Runtime_InitializeEx is called, WSB_GetComponentInfo may be called to retrieve information about either the ExpressPlay SDK or the underlying Sushi APIs. The information is placed in a WSB_ComponentInfo structure.
See the following example call:
WSB_ComponentInfo info;
WSB_Result result;
result = WSB_GetComponentInfo(WSB_COMPONENT_NAME_WASABI, &info);
That call obtains information about the Wasabi SDK. The following call would obtain information about the Sushi-level APIs:
result = WSB_GetComponentInfo(WSB_COMPONENT_NAME_SUSHI, &info);
The information returned indicates the SDK version and build numbers, as well as further details (such as a date or copyright, for example) provided in both short and long strings suitable for display. Please consult the WSB_ComponentInfo structure for details.
Creating a SHI_Engine Object
Some applications will need to use the Sushi component at the core of ExpressPlay for DRM-related operations such as device personalization, user and device registration, etc. Others, such as those implementing media playback only, do not need to use Sushi.
One of the first things an application that needs to use Sushi should do, after calling WSB_Runtime_InitializeEx, is to call SHI_Engine_Create to create a SHI_Engine object. The SHI_Engine object is the top-level object used by the application to interact with the Sushi SDK.
Ensure you call WSB_Runtime_InitializeEx before calling SHI_Engine_Create, and call SHI_Engine_Create before calling any other Sushi functions.
SHI_Engine_Create and the Sushi SDK API are documented in a separate Sushi SDK API. Please contact support@expressplay.com if you think you need access to this functionality. If you are using ExpressPlay, ignore the instruction to call SHI_Application_Initialize prior to calling SHI_Engine_Create. The WSB_Runtime_InitializeEx function calls SHI_Application_Initialize for you. Similarly, ignore information about SHI_Application_Terminate; Wasabi will call this for you when you terminate ExpressPlay execution.
Note also that any ExpressPlay components that internally need to use Sushi will create and destroy their own SHI_Engine. The only situation in which you should create and later destroy (via SHI_Engine_Destroy) a SHI_Engine is if your code will directly need to utilize Sushi interfaces.
Obtaining Help for Handling Errors
Most ExpressPlay API methods return a WSB_Result, whose values are defined in WsbResults.h. There are over 50 different error codes. The global method WSB_ExplainResult, given a WSB_Result error code, tries to guess the cause and possible best remedy for the error. It returns a WSB_ResultExplanation, which is defined as follows:
typedef struct {
WSB_Boolean understood;
const char* text;
const char* recommendation_text;
WSB_ResultRecommendation recommendation;
} WSB_ResultExplanation;
The understood field gives an indication as to whether or not WSB_ExplainResult understands the WSB_Result well enough to have a good chance of providing a reasonable recommended action.
The text field provides a human-readable version of the WSB_Result error code. The recommendation field identifies the recommended action for the user to take, one of the following:
typedef enum {
WSB_RECOMMEND_TRY_AGAIN = 0,
WSB_RECOMMEND_REINSTALL,
WSB_RECOMMEND_CHECK_NETWORK,
WSB_RECOMMEND_CONTACT_SUPPORT,
WSB_RECOMMEND_REFRESH_CONTENT,
WSB_RECOMMEND_FIX_DRM,
WSB_RECOMMEND_GOTO_BROWSER,
WSB_RECOMMEND_REBOOT
} WSB_ResultRecommendation;
The recommendation_text field provides human-readable text for the recommended action.
Terminating the SDK
After an application is finished using the Wasabi SDK, it should call WSB_Runtime_Terminate. No SDK API methods or functions can be called after calling WSB_Runtime_Terminate.
Threading
Use the following guidelines to ensure thread safety in Wasabi.
- It is perfectly safe to have two separate processes use Wasabi (including its Sushi component) at the same time.
- You may use multiple Wasabi/Sushi objects in the same process. Each thread of the process may create and use multiple Wasabi/Sushi objects. However, objects created on one thread must only be accessed on that same thread. Internally Wasabi, Sushi, and third-party dependencies rely on thread-local storage.
- A potential race condition may occur if two separate applications start at the same time and the Sushi database and/or the License Store database have not yet been created. It is up to the applications to prevent this possibility, for example using an OS system-wide mutex or other approach.
- The WSB_Runtime_InitializeEx is not thread safe, and an application should serialize ExpressPlay initialization. Also, WSB_Runtime_InitializeEx does not count the number of initialization and number of shutdown calls through WSB_Runtime_Terminate. It is the client code responsibility to ensure the run-time remains initialized during execution of any ExpressPlay modules.
Memory Management
The API includes a number of methods that return (a pointer to) an object. Many objects have either a Destroy method or a Release method for freeing the memory they utilize. (Objects that are reference counted are released, those that are not are destroyed.) Some objects, such as those that reference media files or the Wasabi License Store database, are created via Open methods and freed via Close methods.
You must free an object before you free the object that returned it to you. Otherwise, a crash or a memory leak may occur.
For example, the WSB_MediaFile_GetMetadata function returns a SHI_Attribute object. You must call SHI_Attribute_Release for the SHI_Attribute before calling WSB_MediaFile_Close for the WSB_MediaFile.