previous next

Chapter 4: Development Framework

RealSystem G2, also called the RealMedia Architecture (RMA), is based on the Component Object Model (COM) jointly developed by Microsoft Corporation and Digital Equipment Corporation. RealSystem components use the COM IUnknown::QueryInterface method to expose their interfaces. The RealSystem COM-style interfaces begin with the prefix "IRMA," which stands for "Interface RealMedia Architecture."

Additional Information
Visit http://www.microsoft.com/com/default.asp for more information on COM.

RealSystem does not employ all aspects of COM, however. It implements a subset of COM functions to provide cross-platform operation without requiring Windows libraries or Windows-emulation code on UNIX and Macintosh platforms. RealSystem thus eliminates the need for heavyweight Windows components like the registry and the COM and OLE runtime libraries. The following sections describe how RealSystem diverges from COM.

Creating a Plug-In Instance

RealSystem does not use the Windows CoCreateInstance function to create plug-in objects. Instead, each RealSystem plug-in implements RMACreateInstance, a "C-style" entry point that exposes the IRMAPlugin interface. RealSystem then uses IRMAPlugin to determine the plug-in's characteristics. The following, taken from a RealSystem SDK sample file, illustrates the entry point:


STDAPI
RMACreateInstance(IUnknown** ppExFileFormatObj)
{
*ppExFileFormatObj = (IUnknown*)(IRMAPlugin*)new CMyExampleFileFormat();
if (*ppExFileFormatObj != NULL)
{
(*ppExFileFormatObj)-AddRef();
return PNR_OK;
}

return PNR_OUTOFMEMORY;
}

Note
On Windows, the plug-in must export RMACreateInstance in its .def file.

Creating a RealSystem Object

A RealSystem component can use the C++ new operator to create objects that it alone manipulates. To create objects passed to other RealSystem components, however, a component should use IRMACommonClassFactory. When RealSystem initializes a component, it passes the component a pointer to the system context. The component can then use this pointer to call IRMACommonClassFactory::CreateInstance and create new RealSystem objects. The following extract from a RealSystem SDK sample file illustrates a call to this method:


m_pClassFactory-CreateInstance(CLSID_IRMABuffer, (void**)&pStringObj);

Using IUnknown::AddRef and IUnknown::Release

Because RealSystem objects are often utilized by other RealSystem objects, objects must correctly implement reference counting. The COM functions IUnknown::AddRef and IUnknown::Release control reference counting and therefore determine each object's lifetime. The following rules describe when RealSystem components need to AddRef and Release objects:

Here are some examples:

Operating Asynchronously

RealSystem components function asynchronously, using response interfaces to return data for calls made to them. For example, a file format plug-in may request file data from a file system plug-in through IRMAFileObject. The file system plug-in then uses IRMAFileResponse, which the file format plug-in implements, to return the requested data. This asynchronous architecture lets the file format plug-in perform other actions while the file system plug-in prepares the requested data. Given RealSystem's asynchronous nature, a plug-in must be able to handle any call made to it while it is processing data or waiting for a response from another component.

Using IRMABuffer to Create Data Buffers

The IRMABuffer interface, implemented by the RealSystem core and defined in rmapckts.h, lets RealSystem objects create data buffers managed through COM reference counting. Typically, RealSystem objects use these buffer objects to pass data. For example, a file system plug-in passes stream header data to a file format plug-in through a buffer object.

Additional Information
See "Creating Stream Headers" for more information.

A RealSystem component does the following to create a buffer object:

  1. Creates the IRMABuffer interface through IRMACommonClassFactory as described in "Creating a RealSystem Object".

  2. Calls IRMABuffer::SetSize to set the buffer size. This performs the memory allocation.

  3. Calls IRMABuffer::GetBuffer to obtain a pointer to the buffer object.

  4. Uses IRMABuffer::Set to write data to the buffer object.

    Any RealSystem component can call IRMABuffer::Set to modify buffer data (the IUnknown::AddRef used during object creation means only that the object is not deleted). It is intended, however, that only the buffer creator sets the buffer's contents. Other components that need to modify the buffer should create a new buffer object. If the file format plug-in needs to modify the contents of a stream header buffer passed to it by the file system plug-in, for example, it should create a new buffer object for the modified data.

  5. Passes a buffer pointer to other components as necessary.

  6. Releases the buffer object with IUnknown::Release.

When a component receives a pointer to a buffer object, it calls IRMABuffer::GetSize to retrieve the buffer size and IRMABuffer::Get to retrieve the buffer data.

Using IRMAValues to Create Indexed Lists

Like IRMABuffer, the IRMAValues interface, also implemented by the RealSystem core architecture and defined in rmapckts.h, is widely used in RealSystem. The IRMAValues interface lets RealSystem components create general lists that pair names with values. For example, when a file format plug-in receives a buffer of stream header data from a file system plug-in, it creates an IRMAValues object that contains the buffer data along with other data needed by the rendering plug-in. Each value in an IRMAValues name/value pair is one of the following:

A RealSystem component does the following to create an IRMAValues object:

  1. Creates the IRMAValues interface through IRMACommonClassFactory as described in "Creating a RealSystem Object".

  2. Calls the IRMAValues::SetPropertyULONG32, IRMAValues::SetPropertyBuffer, or IRMAValues::SetPropertyCString method as many times as necessary to add name/value pairs to the object.

    Note
    As with IRMABuffer, it is intended that only the creator of the IRMAValues interface sets its contents.

  3. Passes an object pointer to other components as necessary.

  4. Releases the interface with IUnknown::Release.

The following example, taken from an SDK sample file, illustrates how to create and populate an IRMAValues interface that contains an unsigned long and a pointer to an arbitrary buffer:


IRMAValues* pHeaderObj = NULL;
m_pClassFactory-CreateInstance(CLSID_IRMAValues, (void**)&pHeaderObj);
if (pHeaderObj != NULL)
{
IRMAPluginpHeaderObj-SetPropertyULONG32("StreamCount", 1);
IRMAPluginpHeaderObj-SetPropertyBuffer("OpaqueData", pHeaderDataReadFromFile);

After receiving a pointer to the IRMAValues interface, a RealSystem component can retrieve a value by name with IRMAValues::GetProperty<type>, where <type> is ULONG32, Buffer, or CString. A component can go through the name/value list with IRMAValues::GetFirstProperty<type> and IRMAValues::GetNextProperty<type>.

Using IRMAPacket to Create Stream Packets

RealSystem components use the IRMAPacket interface, defined in rmapckts.h, to create data packets streamed between RealServer and its clients. A file format plug-in, for example, prepares packets that RealServer streams to the client. As well, a client's rendering plug-in can use the system's back channel to send packets of information back to its file format plug-in.

Additional Information
See "Sending BackChannel Packets".

A RealSystem component does the following to create a packet:

  1. Creates the IRMAPacket interface through IRMACommonClassFactory as described in "Creating a RealSystem Object".

  2. Calls IRMAPacket::Set to pass the packet a pointer to an IRMABuffer interface and define the packet properties, such as delivery time and Adaptive Stream Management rules. The packet properties help RealSystem stream packets efficiently. See rmapckts.h for more information on packet properties.

    Note
    As with IRMABuffer, it is intended that only the creator of the IRMAPacket interface sets its contents. This is critical with packets because more than one renderer may use a packet object.

  3. Passes a packet pointer to other components as necessary.

  4. Releases the packet interface with IUnknown::Release.

The following example, taken from an SDK sample file, illustrates how to create an IRMAPacket interface:


IRMAPacket* pPacketObj = NULL;
m_pClassFactory-CreateInstance(CLSID_IRMAPacket, (void**)&pPacketObj);
if (pPacketObj != NULL)
{
IRMAPluginUINT32 deliveryTime = m_NextPacketDeliveryTime;
IRMAPluginUINT16 streamNoIRMAPlugin = MY_STREAM_NO;
IRMAPluginUINT8 ASMFlags = RMA_ASM_SWITCH_ON;
IRMAPluginUINT16 ASMRuleNo = 0;
IRMAPluginpPacketObj-Set(pPacketDataReadFromFile, deliveryTime, streamNo, ASMFlags, ASMRuleNo);

After receiving a pointer to the packet interface, a RealSystem component, typically a rendering plug-in, retrieves the packet data with IRMAPacket::Get. Other methods let a component retrieve specific packet properties. A component can also call IRMAPacket::IsLost to determine if the packet has been lost. See the rendering plug-in chapter for more information.

Designing a Plug-In

Keep the following general points in mind as you develop your plug-ins. Each section on building a plug-in may also have design considerations specific to that type of plug-in.

  1. Keep your plug-ins as simple, self-contained, and universal as possible. Carefully consider the impact on channel opportunities and audience size if, for example, you add requirements for special client APIs or hardware.

  2. Keep the asynchronous nature of RealSystem in mind. Your plug-in must be able to handle any call made to it while it is processing data or waiting for a response from another object. Do not code your plug-in so that it expects a specific sequence of events to occur as it interacts with RealSystem.

  3. Try to keep plug-in file size under 300 KB uncompressed.

  4. Design your datatype delivery to satisfy all users, from 28.8 Kbps modem users on up. Either keep the streaming bit-rate low overall, or design solutions that deliver an acceptable experience to low-end users and scale with increasing sophistication for users with high-speed connections.

  5. Design code to run on Macintosh, Windows, and UNIX platforms. The largest Internet audience of RealPlayer is on Windows 95, followed by MacPPC and UNIX (Sun, SGI, Linux, and others). You should design for Windows 3.1 and Macintosh 68k depending on your intended audience.

  6. Do not rely on MMX to improve performance of client playback. RealSystem consumers do not currently own a significant number of MMX machines.

  7. If you are developing plug-ins for an audio datatype, use RealSystem's Audio Services. This powerful feature handles audio mixing, resampling, volume control, and instantaneous sounds.

    Additional Information
    See "Chapter 13: Audio Services".

  8. Because RealPlayer and RealServer are localized in many languages, we recommend using a resource table and designing your datatype's interface and documentation for localization. For example, use double-byte characters or Unicode where appropriate.

  9. Provide good documentation on your datatype and plug-ins. Online format is ideal, as long as users can print pages easily. To minimize client component sizes, though, do not include documentation with the client download. Make it available on a Website instead.

  10. Design your content creation tools to consider the bit-rate of the stream over time, and let designers make decisions on how to produce the content. A well-designed datatype will use the Adaptive Stream Management features to level the bit-rate of its stream. If you want to interact with other datatypes, such as streaming images, text, RealAudio or RealVideo, check if other SDKs let you add content creation functionality for a datatype to your tools.

    Additional Information
    See also "Creating a Plug-In Instance" before you start to build your plug-in.

Compiling a Plug-In

The following sections give pointers on compiling your plug-in. Before you start building your own plug-in, you should test-compile some RealSystem G2 SDK sample files. The simplest sample file is the "Hello World" sample.

Testing the "Hello World" Sample Code

The RealSystem SDK includes many sample files that you can use as a basis for writing your own plug-ins. The simplest is the "Hello World" plug-in, which prints "Hello World" on the RealServer console. RealNetworks recommends that, before you start building your plug-ins, you learn the basic RealSystem features by modifying, compiling, and testing the "Hello World" sample:

This sample introduces the IUnknown::QueryInterface, IUnknown::AddRef, and IUnknown::Release methods of COM. It also shows how RealServer instantiates a plug-in and discovers its features through the IRMAPlugin interface. When RealServer starts up, it loads the plug-in as follows:

  1. RealServer calls RMACreateInstance to create a new instance of the plug-in. See "Creating a Plug-In Instance" for more on this method.

  2. RealServer calls IRMAPlugin::GetPluginInfo, which returns descriptive information about the plug-in, including its copyright and "more information" URL. The LoadMultiple attribute must be set to TRUE for RealServer to launch multiple instances of the plug-in.

  3. RealServer calls the plug-in's IRMAPlugin::InitPlugin method, passing it a pointer to the RealServer context. The plug-in uses this pointer to query for IRMAErrorMessages on RealServer and, if it finds that interface, prints "Hello World" on the system console and in the log file.

The sample file contains comments that explain each function. Carry out the following steps to modify and test the sample:

  1. Copy the sample code to a working directory.

  2. Optionally, change the sample's file names and class names.

  3. Modify the plug-in description, copyright information, and more info URL stored in zm_pDescription, zm_pCopyright, and zm_pMoreInfoURL.

  4. Compile and install the plug-in.

    Additional Information
    See "Compiling a Plug-In" and "Installing Plug-Ins for Testing".

Sample File Coding Conventions

All RealSystem sample files use the following conventions.

Sample File Coding Conventions
Convention Designation
pName Pointer
bName Boolean
nName Integer
unName Unsigned Integer
ulName Unsigned Long
m_Name Member variable
zm_Name Static member variable
g_Name Global variable

Building with Microsoft Visual C++ 4.x or 6.0

On Windows, RealNetworks recommends using with Microsoft's Visual C++ 4.2 or 6.0.

Additional Information
http://www.microsoft.com/products/

To Compile with Visual C++:

  1. Open the Microsoft Developer Studio program.

  2. Click Open Workspace on the File menu.

  3. In the Files of type box, select Makefiles (*.mak).

  4. Select the makefile in the win subdirectory of the desired sample directory and click Open. Visual C++ makefiles always have a .mak extension. For example:
    
    sdk\samples\intermed\exffplin\win32\exffplin.mak 
    

  5. In the Select Default Project Configuration box, select a Release build or a Debug build.

  6. Click the Build button or click Build filename on the Build menu.

To build using Visual C++ from the command line, enter the following command:


nmake /f "makefile.mak" CFG="configuration" 

For example:


nmake /f "exffplin.mak" CFG="exffplin - Win32 Debug"
nmake /f "exffplin.mak" CFG="exffplin - Win32 Release"

Building with gcc on UNIX

RealSystem G2 SDK sample files were test-compiled with GNU's gcc for UNIX. The UNIX makefile for each sample file is located in the unix subdirectory under the sample directory. To build the sample with gcc, use this command:


make -f unix/Makefile.<ext> 

where .ext is the extension that identifies your UNIX platform.

Additional Information
http://www.gnu.org/

Building with CodeWarrior

For the Macintosh, RealSystem G2 SDK sample files were test-compiled with Metrowerk's CodeWarrior 11 and CodeWarrior 12 for Macintosh

Additional Information
http://www.metrowerks.com

To Compile with CodeWarrior 11 or CodeWarrior 12

  1. Open the CodeWarrior project file located in the mac folder within the sample folder.

  2. Build the project. Be sure to include the files MacRMAMem.cp and PNMM.lib in the project for all Macintosh plug-ins.

  3. Move the shared library into the System:Extensions:Real folder.

Debugging a Plug-In

You should be able to debug your plug-ins by using your standard debugger. In Visual C++ you can simply set up the debugging settings in your DLL's makefile or project to use realplay.exe as your application for debugging.

Testing a Plug-In

When you test your plug-ins, do as much "real world" empirical testing of your datatype as possible. Be sure to test under conditions that are less than ideal. For example, try dialing up on 28.8 Kbps modems and playing your datatype over the Internet from a RealServer on a Pentium-75 class machine.

The following are general guidelines for testing how your plug-ins work. This is not a comprehensive list, as testing strategies vary depending on the datatype.

  1. Verify that all file types of the desired extensions and stream MIME types play.

  2. Test that files of each datatype play all the way through successfully.

  3. Verify that the window has the correct default location inside or outside the player console.

  4. Check that seek works properly if it is implemented.

  5. Determine that pause/resume works properly.

  6. Check that stopping midstream works properly.

  7. Verify that the desired behavior occurs during data buffering.

  8. Determine that running out of memory is handled gracefully.

  9. Verify that behavior is acceptable on slower machines.

  10. See if the player can play several streams at once on the same machine. (A SMIL layout is the best way to test this.)

  11. Test for per-packet memory leaks by playing a long stream (upwards of two hours) and verifying that player memory usage doesn't go steadily upwards. You can do this by making a SMIL file with a sequence of many short streams.

Installing Plug-Ins for Testing

  1. To begin testing your plug-ins, copy the server-side plug-ins to the plug-ins directory defined by the server configuration file. This includes the file format plug-in for your datatype and may include file system and broadcast plug-ins as well.

    Additional Information
    See "Appendix E: RealServer Configuration".

  2. On the client side, copy your datatype's rendering plug-in and, if testing with a local file, the file format plug-in to the plug-ins directory on the client computer:

  3. Copy a file of your datatype to a directory in RealServer to test with the server release. Also, copy the file to the client machine to open as a local file.

  4. Test your plug-in by playing a simple file of your datatype in RealPlayer and TestPlay. Then create more extensive multimedia presentations that display your datatype along with other datatypes in a timed presentation.

Testing with RealPlayer

  1. Start RealPlayer in Windows by double-clicking the realplay.exe icon in the RealPlayer directory. On Macintosh, double-click the RealPlayer icon in the RealPlayer folder.

  2. Enter the URL of your file in the Open Location box to stream the file from RealServer.

  3. Use Open File to open the client's local file.

Testing with TestPlay

With TestPlay, you can test streaming datatypes in a client environment other than RealPlayer. For example, you can use TestPlay to mimic the delivery of streaming data directly within a Web browser. After installing TestPlayer as described in "Installing TestPlay", you can run it from a command line with this syntax:


testplay <URL> 

where <URL> is the network or local file to play. Here are some examples:


testplay rtsp://real.myserver.com/clips/newvideo.rm

testplay file:c:\real\rmplayer\welcome.rm

TestPlay opens any required windows and plays the specified presentation. It writes the data from the IRMAClientAdviseSink interface to the console to demonstrate the information available to a custom client application.

Testing with Multimedia

After you have successfully tested your plug-ins by streaming and reading a simple file of your datatype, create multimedia presentations that include your datatype. Then play your presentations using RealPlayer and TestPlay. You define the content of the presentation using SMIL.

Testing with HTTP Streaming

You should test that your datatype streams properly from a Web server. Although HTTP streaming is not as robust as RTSP streaming, it provides a reasonable method to stream short clips. As shown in the following figure, RealPlayer must have the file format plug-in and rendering plug-in installed. The HTTP file system plug-in comes with RealPlayer. No plug-ins are used with the Web server, however.

HTTP Streaming

Do the following to stream your datatype with HTTP:

  1. Define the MIME type of your datatype in the Web server.

  2. Copy your files, including a SMIL file, to the Web server.

  3. In your HTML document, hyperlink to the SMIL file. You can use relative or complete paths. If you use complete paths, you must include the host name. For example:
    
    <A HREF="http://hostname/file.smi">
    

  4. Click the HTML hyperlink to start the HTTP streaming.

    Note
    HTTP streaming does not provide all the features of RTSP streaming, such as searching. When you perform a user seek by moving the RealPlayer slider forward to a new place on the presentation timeline, RealPlayer stops rendering the datatype, but continues to proceed through the timeline at the normal pace. Rendering resumes once RealPlayer reaches the timeline position designated by the seek.


Copyright © 2000 RealNetworks
For technical support, please contact supportsdk@real.com.
This file last updated on 05/17/00 at 12:50:17.
previous next