The first step in integrating your datatype into RealSystem is building a file format plug-in that converts your datatype into a stream of RealSystem packets. RealServer uses your plug-in to stream your datatype directly from its native file format. The RealSystem client then assembles the packets and displays the data through a rendering plug-in. Clients and tools can also use the file format plug-in to read files locally. RealPlayer, for example, uses the plug-in to read a file of your datatype saved to the client's local disk.
A file format plug-in does not read files directly from disk or any other data source. It simply interacts with a file object given to it by RealSystem. The file object, which is created by a file system plug-in, provides a virtual file system with standard interfaces that the file format plug-in can use to perform read and seek operations without regard to the platform or storage device that holds the file.
In addition to the general plug-in design considerations described in "Designing a Plug-In", keep the following points in mind as you develop your file format plug-in:
![]() |
Additional Information |
---|
See "Chapter 11: Adaptive Stream Management". |
![]() |
Additional Information |
---|
See "Getting Relative File Objects". |
The alternative method is to make your file format a container datatype for the other datatypes. Content producers do not then need to keep track of multiple files for each presentation and the file format plug-in does not need to cross-reference URLs with file sets. Using a container file format eliminates the advantages of streaming from native file formats, however. RealSystem supports either implementation. This design choice is your decision.
A file format plug-in typically implements the following interfaces:
IRMAPlugin
. Header file: rmaplugn.h
. Every plug-in implements this interface, which RealSystem uses to determine the plug-in's characteristics.
IRMAFileFormatObject
. Header file: rmaformt.h
. A file format plug-in must implement this interface, which provides the basic methods that RealSystem uses to instruct the plug-in to send it file headers and packets.
IRMAFileResponse
. Header file: rmafiles.h
. This response interface to IRMAFileObject
lets the file format plug-in receive asynchronous notification that the file object has finished an operation.
IRMAASMSource
. Header file: rmaasm.h
. The file format plug-in implements this interface to receive rule subscription and unsubscription information for Adaptive Stream Management (ASM).
The file format plug-in may also implement the following interfaces, depending on which RealSystem features it needs to support or use:
IRMAFileStatResponse
. Header file: rmafiles.h
. The file format plug-in implements this response interface to IRMAFileStat
to receive the asynchronous response data about requested file statistics from a file object.
IRMAFileSystemManagerResponse
. Header file: rmafiles.h
. This response interface to IRMAFileSystemManager
lets a file format plug-in receive file object pointers for files related to a requested file.
IRMADirHandlerResponse
. Header file: rmafiles.h
. This response interface to IRMADirHandler
lets a plug-in receive directory object pointers for directories related to a requested file.
IRMAPendingStatus
. Header file: rmapends.h
. The file format plug-in can implement this interface to provide the RealSystem client with the status of its pending operations.
IRMAPacketFormat
. Header file: rmaformt.h
. The file format plug-in can implement this interface to support multiple packet datatypes.
IRMABackChannel
. Header file: rmaasm.h
. The file format plug-in can implement this interface to receive data from its corresponding rendering plug-in through the back channel.
![]() |
Additional Information |
---|
See "Sending BackChannel Packets". |
The following sections explain how RealSystem (RealServer or client) and a file format plug-in use the RealSystem interfaces to stream data to a client. The sample files included with this SDK illustrate many of these features. You can use these sample files as a starting point for building your own plug-in. Refer to the RealSystem SDK header files for more information on function variables and return values.
When RealSystem starts up, it loads each file format plug-in:
RMACreateInstance
to create a new instance of the file format plug-in. The system calls this function at start-up and each time it receives a request for a file type handled by the plug-in.
![]() |
Additional Information |
---|
See "Creating a Plug-In Instance" for more on this function. |
IRMAPlugin::GetPluginInfo
, which returns descriptive information about the plug-in, including its copyright and "more information" URL. The bLoadMultiple
attribute should be TRUE
. This causes RealServer to launch multiple instances of the plug-in in separate processes. A RealSystem client using the file format plug-in ignores this attribute.
IRMAFileFormatObject::GetFileFormatInfo
, which returns functional information about the plug-in:
pFileMimeTypes
parameter indicates which MIME type or types the file format plug-in handles. The corresponding rendering plug-in or plug-ins must have the same MIME type or types defined for them.
pFileExtensions
parameter gives the file extensions for the files the plug-in handles.
pFileOpenNames
parameter gives the file type descriptions and file extensions that appear in the "Files of type" pull-down in the client's Open File... dialog.
When the system receives a request for a file, it identifies the appropriate file format plug-in based on the requested file's extension and the pFileExtensions
values returned by the file format plug-ins during start-up. If two or more plug-ins handle the same file extension, the system uses the first plug-in for that file type that it loaded during start-up. The system then initializes the chosen plug-in:
IRMAPlugin::InitPlugin
, passing the file format plug-in a pointer to the system context. Within the IRMAPlugin::InitPlugin
method, the plug-in should peform any necessary initialization. It should also use the system context pointer to store a reference to IRMACommonClassFactory
so that it can later create RealSystem objects used to send data to the system.
IRMAFileFormatObject::InitFileFormat
method to pass it pointers to these interfaces:
IRMAFormatResponse
interface that receives notification of plug-in actions. This is typically the interface that instantiated the file format plug-in (RealServer or client).
IRMARequest
interface that the plug-in can use to access the request response headers.
![]() |
Additional Information |
---|
See "Modifying the Response Headers". |
IRMAFileObject::Init
to pass this file object a pointer to the IRMAFileResponse
interface, which receives asynchronous notification when file actions complete. The response object is typically the file format plug-in itself.
The IRMAFileObject::Init
call also passes flags to indicate the file type. For example, the file format plug-in uses the following method to initialize the file object for reading a binary file:
m_pFileObject-Init(PN_FILE_READ | PN_FILE_BINARY, this);
![]() |
Additional Information |
---|
See rmafiles.h for the flag
definitions. Note too that a file format plug-in can open multiple file
objects to access multiple points in the file simultaneously. This is useful
if the file contains separate audio and video tracks, for example. See
"Getting Relative File Objects" for more information.
|
IRMAFileResponse::InitDone
method to return a status code. Within this method, the plug-in notifies the system through IRMAFormatResponse::InitDone
that its initialization has completed successfully or not.
![]() |
Additional Information |
---|
See "Status Codes". |
![]() |
Additional Information |
---|
See "Retrieving File Information". |
IRMAFileFormatObject::GetFileHeader
to get an object that contains the file header data. The plug-in must then use the file object interface to retrieve an IRMABuffer
of this data. Because of its asynchronous nature, RealSystem uses several routines to complete this operation.
![]() |
Additional Information |
---|
See "Interacting with a File Object" for more information. |
IRMABuffer
data, the file format plug-in creates an IRMAValues
interface that contains the file header data. This interface contains a value for the required property StreamCount
, as well as any other opaque data the plug-in needs to send the renderer before sending the file streams.
IRMAFormatResponse::FileHeaderReady
method to pass the system a pointer to the file header object. It then uses IUnknown::Release
to release the interface.
After sending the system the file header for the requested file, the file format plug-in sends it the stream headers for each stream in the file:
IRMAFileFormatObject::GetStreamHeader
method. The plug-in then uses the file object to get for each stream an IRMABuffer
interface that contains the stream header data. Because of its asynchronous nature, RealSystem uses several routines to complete this operation.
![]() |
Additional Information |
---|
See "Interacting with a File Object" for more information. |
IRMABuffer
of the retrieved stream header data, the file format plug-in creates an IRMAValues
interface that contains the stream header data. This interface contains any opaque data the plug-in needs to send to its renderer, as well as values for the properties listed in the following table
![]() |
Note |
---|
These stream header properties are used for both the RDT (RealSystem G2) and PNA (RealSystem 5.0 and below) packet formats. A plug-in that supports other packet formats such as RTP sets additional stream header properties. For more on this, see "Supporting Multiple Packet Formats". |
IRMAFormatResponse::StreamHeaderReady
to pass the system a pointer to the header object for each stream. It then uses IUnknown::Release
to release each object.
IRMAASMSource::Subscribe
method to indicate which rules for which streams the client has subscribed to.
![]() |
Additional Information |
---|
See "Chapter 11: Adaptive Stream Management". |
After receiving the stream header or headers, the system requests packets for each stream:
IRMAFileFormatObject::GetPacket
method to get the first packet for a stream. Because of the asynchronous nature of RealSystem, retrieving the packet data requires several routines.
![]() |
Additional Information |
---|
See "Interacting with a File Object" for more information. |
IRMAFileFormatObject::GetPacket
call, the file format plug-in creates an IRMAPacket
interface that contains the opaque data passed to the renderer, as well as values for the properties listed in the following table.
![]() |
Additional Information |
---|
See "Using IRMAPacket to Create Stream Packets" for the basics of packet creation. See also "Chapter 11: Adaptive Stream Management". |
IRMAFormatResponse::PacketReady
to pass the system a pointer to the packet interface. It then releases the packet interface with IUnknown::Release
.
IRMAFileFormatObject::GetPacket
again and the above procedure repeats until the plug-in has delivered all packets. At that point the plug-in calls IRMAFormatResponse::StreamDone
to notify the system that the stream has finished.
A file format plug-in should generally create packets of 430 to 500 bytes for the opaque data. Staying under 500 bytes decreases the likelihood of packet fragmentation. RealNetworks also recommends that you write code that lets the plug-in quickly change the size of the packets it sends.
For each stream, a file format plug-in should not accept requests for another packet or header until the previous request has been satisfied. The plug-in should return the status code PNR_UNEXPECTED
if it receives an IRMAFileFormatObject::GetPacket
or GetHeader()
call before returning IRMAFormatResponse::PacketReady
or HeaderReady()
from the previous request. If the plug-in supports multiple streams, however, the plug-in must be able to support a IRMAFileFormatObject::GetPacket
call for one stream while it is preparing a packet for another stream.
![]() |
Additional Information |
---|
See "Status Codes". |
During a RealSystem presentation, the user may move the client slider to begin playing the presentation back from a different place in the timeline. When this happens, the system calls the file format plug-in's IRMAFileFormatObject::Seek
method, passing the plug-in the requested time in milliseconds. The file format plug-in notifies the system of success or failure with IRMAFormatResponse::SeekDone
.
In performing the seek, the file format plug-in needs to determine what packet to send and perform the necessary actions to prepare and send it. A plug-in for an audio datatype may need only to send the packet that corresponds to the current place in a new timeline. A plug-in for a video datatype, however, may have to locate the keyframe before the new timeline.
![]() |
Additional Information |
---|
See "Interacting with a File Object" for more information on seeking and reading. "Creating Stream Packets" explains the packet creation procedures. |
When the file playback has finished or is stopped, the system calls the plug-in's Close
method. The plug-in must then release all references to objects and deallocate memory. This includes calling IRMAFileObject::Close
for all file objects in use. When it releases the file resources, the file object returns a status code through IRMAFileResponse::CloseDone
.
![]() |
Additional Information |
---|
See "Status Codes". |
A file format plug-in can support packet formats in addition to the RealSystem default packet format of RDT. This lets the plug-in deliver its datatype to a client based on an standardized packet format. RealSystem currently supports the RTP (Real Time Protocol) packet standard. Supporting RTP also requires using the ASM Marker property.
![]() |
Additional Information |
---|
See "RTP Marker Bit Property". |
RealServer calls IRMAPacketFormat::GetSupportedPacketFormats
to determine which formats the plug-in supports. The method returns a null-terminated list of supported formats ("rdt", "rtp", and so on). RealServer then calls IRMAPacketFormat::SetPacketFormat
to inform the plug-in of the format to use. If the plug-in does not implement IRMAPacketFormat
, RealServer assumes the plug-in supports only RDT.
In addition to the regular stream header properties, file format plug-ins supporting RTP need to set the header properties listed in the following table.
The audio Codec properties allow the construction of an interoperable stream description called SDP
, which allows RTP clients to play streams from dynamic payload types. The following table summarizes the supported RTP payload types.
![]() |
Additional Information |
---|
For more on payload types, refer to RFC 1890, which you can find on the IETF Web site at http://www.ietf.org/html.charters/avt-charter.html. |
The SDPData
property in the file or stream header allows a file format or encoder to provide a direct input to the Session Description Protocol (SDP). This property also allows a renderer to receive the non-universal SDP attributes.
A file format plug-in can specify the SDPData
String property as part of the stream and/or file header. The content of the SDPData
must be a series of properly-formatted and properly-delimited SDP attribute records ("a=") refer to RFC2327 for formatting requirements. The attribute records a=rtpmap and a=control are the exception and must not be specified as part of SDPData
. The SDPData
information is integrated in the SDP describe request response verbatim. If specified in the header file, it is appended to the session description; if specified in the stream header, it is appended to the media description.
On the client side, the reversal of this process occurs. RealPlayer or RealServer, when receiving packets from the encoder, places any non-universal (and thus not recognized by the transport layer) SDP attributes in the SDPData
and passes it on in the file or stream header, depending on where the non-universal attributes are located in the session or media description.
Even though a MIME type could conceivably assume any string, to enable interoperability it should follow the following format:
<media>/<encoding name>
Refer to RFC2327 for more information.
The Real Media Architecture uses milliseconds as the integral unit of time. In RTP streaming, the time stamp might contain a non-integral number of milliseconds. This results in an aliasing effect when RTP time stamps are converted to milliseconds. The IRMARTPPacket
interface can be used to avoid the aliasing effect and preserve the accuracy of RTP time stamps.
The IRMARTPPacket
interface that is derived from IRMAPacket
contains the IRMARTPPacket::GetRTP
, IRMARTPPacket::GetRTPTime
, and IRMARTPPacket::SetRTP
methods. These methods are analogous to IRMAPacket::Get
, IRMAPacket::GetTime
, and IRMAPacket::Set
, and provide for the addition of RTP time to the packet. The RTP time is scaled by Samples/Second, which a file format plug-in supporting RTP is required to specify using the SamplesPerSecond property:
<time in milliseconds> = 1000.0 * RTPTime / SamplesPerSecond
The RTP time stamps the RTP packet sent out by the server. The RealPlayer forms packets supporting the IRMARTPPacket
interface whenever a packet is received through RTP. Thus the renderers can take advantage of the raw RTP time stamp if needed.
![]() |
Note |
---|
The server is not using the RTP time for scheduling purposes. The standard packet time in milliseconds (RMA time) is the time the server uses as delivery time. |
A packet supporting the IRMARTPPacket
interface is required to implement IRMAPacket
. Thus, the additional functionality of the IRMARTPPacket
interface can be ignored if it is not needed. However, if a packet supporting IRMARTPPacket
is set using the IRMARTPPacket::Set
method, the RTP time of that packet is set to the same value as the standard RMA time (in milliseconds). This is significant since the server, when using RTP, uses packet RTP times stamps verbatim whenever the packet supports the IRMARTPPacket
interface.
![]() |
Note |
---|
If a stream contains a packet supporting the IRMARTPPacket interface,
then all packets in the stream must support IRMARTPPacket .
|
Optionally, file format and file system plug-ins can implement IRMAPendingStatus
to inform the RealSystem client of their pending operations. To learn the component's status, the client calls IRMAPendingStatus::GetStatus
, which returns three parameters:
uStatusCode
This status code, which is not related to the system status codes, indicates the plug-in's current activity:
RMA_STATUS_INITIALIZING
RMA_STATUS_BUFFERING
File system or file format plug-in is buffering data. RealPlayer uses buffering statistics from all components to calculate the buffering "percent complete" displayed in its user interface.
RMA_STATUS_CONTACTING
File system plug-in is contacting host. For example, the system's HTTP file system plug-in reports this status when contacting the HTTP host.
RMA_STATUS_READY
File system or file format plug-in ready for operation.
![]() |
Additional Information |
---|
See "Status Codes". |
pStatusDesc
An optional pointer to an IRMABuffer
interface that contains a text description of the current status. This buffer might contain, for example, the address of a host being contacted.
ulPercentDone
An integer value that represents the percentage of the task complete. This is used primarily for buffering. If the plug-in is contacting a host or initializing, the returned value is 0 when the operation is underway, 100 when complete. A percent done value for the RMA_STATUS_READY
state is ignored.
When a client requests a file, RealSystem generates a request object that contains the URL and the request headers. This object exists as long as the request is being served. A file format plug-in or a file object can send the client any information about the file by attaching response headers to the request object before RealServer begins to stream the file:
IRMARequestHandler::SetRequest
to pass the pointer to the file object.
IRMARequest::GetURL
to get the fully qualified path for the requested file.
IRMAValues
interface and adds the necessary response header values to the object.
IRMARequest::SetResponseHeaders
to give the request object a pointer to the values interface containing the response headers. (Multiple values interfaces can be associated with the request object.)
IRMAValues
interface.
Instead of reading files directly, a file format plug-in accesses data through file objects created by a file system plug-in. RealSystem determines which file system plug-in to use and hands the file format plug-in a file object during initialization. The file format plug-in can then get file data through IRMAFileObject
, receiving responses through IRMAFileResponse
without needing to know the location or format of the file data.
To retrieve the file object's name without any path information, the file format plug-in can call IRMAFileObject::GetFilename
, which returns a pointer to the file name. The plug-in can use the request object to get the fully qualified path of the request URL.
![]() |
Additional Information |
---|
See "Modifying the Response Headers". |
If the file object implements IRMAFileStat
, a file format plug-in can call the IRMAFileStat::Stat
method, which passes the file object a pointer to the file statistics response object. The file object then gathers file statistics and returns them to the response object, which is typically the file format plug-in, through IRMAFileStatResponse::StatDone
. Statistics include the following, which are defined in rmafiles.h
:
When the file format plug-in receives an IRMAFileFormatObject::Seek
, IRMAFileFormatObject::GetFileHeader
, IRMAFileFormatObject::GetStreamHeader
, or IRMAFileFormatObject::GetPacket
call, it uses the IRMAFileObject
interface to seek for and read the file through the file object. The following actions occur when a file format plug-in must perform a file seek:
IRMAFileFormatObject::GetFileHeader
, IRMAFileFormatObject::GetStreamHeader
, or IRMAFileFormatObject::GetPacket
) when it receives notification that the seek has finished.
IRMAFileObject::Seek
. This method takes a file offset value and a bRelative
parameter that, when set to TRUE, specifies that the value is relative to the last seek. When set to FALSE, the bRelative
parameter makes the seek an absolute offset.
PNR_OK
or PNR_FAIL
through IRMAFileResponse::SeekDone
. Do not assume, however, that when a call to IRMAFileObject::Seek
returns, IRMAFileResponse::SeekDone
has been called.
![]() |
Additional Information |
---|
See "Status Codes". |
The following actions occur after a seek has completed successfully:
IRMAFileFormatObject::GetFileHeader
, IRMAFileFormatObject::GetStreamHeader
, or IRMAFileFormatObject::GetPacket
) when the subsequent read action completes.
IRMAFileObject::Read
, which takes a value for the length of the data buffer to read.
IRMAFileResponse::ReadDone
method, returning a pointer to the IRMABuffer
interface that contains the data. Do not assume, however, that when a call to IRMAFileObject::Read
returns, IRMAFileResponse::ReadDone
has been called.
IRMABuffer
interface.
Note the following about using IRMAFileObject::Read
:
IRMAFileObject::Read
a second time without having received an IRMAFileResponse::ReadDone
from the first call, this second IRMAFileObject::Read
returns the status code PNR_UNEXPECTED
. The first read will still be pending.
![]() |
Additional Information |
---|
See "Status Codes". |
IRMAFileObject::Seek
but have not yet received an IRMAFileResponse::ReadDone
callback from a previous read, IRMAFileResponse::ReadDone
returns with the status code PNR_CANCELLED
. The seek will still be pending.
![]() |
Additional Information |
---|
See "Status Codes". |
Before releasing a file object, the file format plug-in calls IRMAFileObject::Close
to release resources associated with the object. The plug-in can then call IUnknown::Release
.
During initialization, the file format plug-in initializes a file object for the requested file. In addition, it can access another file or set of files in locations relative to that specified by the requested URL. It can even create additional file objects for the same file to, for example, access multiple streams in the file. Using the relative file object methods greatly reduces the amount of overhead the system performs when creating multiple file objects.
By implementing IRMAFileSystemManagerResponse
, the file format plug-in can do the following to create relative file objects:
IRMAFileSystemManager
interface from the Common Class Factory. For example:
m_pCommonClassFactory-CreateInstance(CLSID_IRMAFileSystemManager
,
(void**)&m_pFSManager);
IRMAFileSystemManager::Init
, identifying itself as the response object.
IRMAFileSystemManager::GetRelativeFileObject
, passing a pointer to the original file object and the relative path to the new file. For example, if the requested URL is for:
/home/rmserver/rafiles/alanis.ra
/home/rmserver/rafiles/extra/alanis.rae
the relative path passed in IRMAFileSystemManager::GetRelativeFileObject
is
extra/alanis.rae
The relative path must contain a file name and, optionally, a subdirectory path. Hence the relative file must reside at or below the directory level of the original file. To create a new file object for the same file, the plug-in simply passes in the file name, which it can retrieve through IRMAFileObject::GetFilename
, as the relative path.
IRMAFileSystemManagerResponse::FileObjectReady
method, returning a pointer to the new file object.
![]() |
Note |
---|
Do not try to create a second file object with the file manager object before the first file object is ready. |
IRMAFileObject::Init
as described above in "Initializing".
A file format plug-in can create a file object relative to another relative file object. With the simple file system (the basic file system used to locate files on RealServer or the client's local disks), all relative file objects are relative to the file object passed as a pointer in IRMAFileSystemManager::GetRelativeFileObject
. The following example illustrates this:
/home/rmserver/rafiles/alanis.ra
/home/rmserver/rafiles/extra/alanis.rae
IRMAFileSystemManager::GetRelativeFileObject
method used to create the third object passes a pointer to the relative file object defined in the preceding step, as well as the path alanis.bork.) Because this file object is relative to the second file object, it points to this file:
/home/rmserver/rafiles/extra/alanis.bork
Note, however, that this is not necessarily true for file systems other than the simple file system. Other file system plug-ins supported by RealSystem's virtual file system architecture may create the third file object relative to the first file object:
/home/rmserver/rafiles/alanis.bork
The RealSystem SDK includes sample file format plug-ins that you can use as a starting point for building your own plug-in:
A basic file format plug-in that streams data to a corresponding, basic rendering plug-in. Build these sample files to learn basic concepts about streaming and rendering RealSystem packets.
An intermediate sample file that creates a single data stream.
An intermediate sample file that creates multiple data streams. Multiple data streams are needed for container data types such as QuickTime.
/samples/intermed/pmffplin/pmixplin.cpp
An intermediate sample file that creates pre-mix and post-mix audio streams.
Carry out the following steps to modify either of the intermediate sample files. These steps assume your company name is "Foo Bar, Inc.", your file extension is ".foo", and the MIME type of your file format is "application/x-foobar
".
CExampleFileFormat
with CFooFileFormat
, and rename the files fooffpln.cpp and fooffpln.h.
zm_pDescription
, zm_pCopyright
, and zm_pMoreInfoURL
. For the Foo example, you could change the values as follows:
char* CFooFileFormat::
zm_pDescription = "Foo File Format Plug-in";
char* CFooFileFormat::
zm_pCopyright = "(c)1997 Foo Bar";
char* CFooFileFormat::
zm_pMoreInfoURL = "http://www.foobar.com";
zm_pFileMimeTypes
, zm_pFileExtensions
, and zm_pFileOpenNames
. For the Foo example, you could change the values as follows:
char* CFooFileFormat::
zm_pFileMimeTypes = {"application/x-foobar", NULL};
char* CFooFileFormat::
zm_pFileExtensions = {"foo", NULL};
char* CFooFileFormat::
zm_pFileOpenNames = {"Foo File Format (*.foo)", NULL};
application/x-pn-examplestream
to the new value for your stream. The stream MIME type is usually the same as the file MIME type.
StreamCount
value in pHeader
to the number of streams your plug-in creates. The sample ex2strms.cpp demonstrates this.
![]() |
Additional Information |
---|
"Compiling a Plug-In". |