Content Access Framework (CAF) provides services that enable agents to publish content in a generic manner that is easy for applications to use. Applications use the Consumer API to render a content and multimedia plug-ins.
Note: Rendering is a process of reading data from a file, transforming it, then playing or displaying it on the device.
Access to content within archive files
An archive file contains content objects and other containers within the file. Each container within the file may contain more content objects or further containers. Common examples of archive files are zip and tar files.
CAF allows applications to open archive files and read content
from inside them. The content objects and containers inside the file
can be traversed using the ContentAccess::CContent
class. The class allows applications to use the content within these
container files without needing to understand any specifics of the
compression or storage mechanism used by the archive.
Before you start, you must understand:
Content Access Framework also allows applications to open archive
files and read content from inside them. The content objects and containers
inside the file can be traversed using the ContentAccess::CContent
class. This class allows applications to use the content within
the container files without needing to understand any specifics of
the compression or storage mechanism used by the archive. See Files Containing
Multiple Content Objects for more information.
The following diagram is an example of a content file. The same diagram is used in the procedure to illustrate the implementation of Consumer API.
Figure: Example content file
The ContentAccess::CContent
object encapsulates a single file. It allows an application to look
at the structure of the objects within the file and the attributes
of those objects.
There are two ways to create a CContent
object. The application can specify the URI of the content or it
can supply an open file handle, see Objects used to identify a content object within a file.
// Create a CContent with a URI CContent *content = CContent::NewL(uri); // Create a CContent with an existing file handle CContent *content = CContent::NewL(aFs, aFile);
Upon
creation, CContent
selects the agent that handles
the file.
List objects within a container
ContentAccess::CContent
acts like a cursor, only able to list the contents of one container
object at any one time. When CContent
is first opened,
it views the top-level container within the file. The top level container
is actually the file itself. This top level container concept applies
to all files, regardless of how many content or container objects
are inside.
Even a content file such as a jpeg image is a
container, it's just that the file only has the "DEFAULT"
object embedded inside.
So, when the example file shown
earlier is opened, the following objects can be seen by the CContent
:
In this top level container, there is only one embedded
content object visible (the .jpg
file) and two
embedded container objects.
// Create an array to store the results of CContent::GetEmbeddedObjectsL() RStreamablePtrArray<CEmbeddedObject> myArray; CleanupClosePushL(myArray); // Get the embedded content objects in the current container content->GetEmbeddedObjectsL(myArray, EContentObject); i = myArray.Count(); // One content object // clear the contents of the array myArray.ResetAndDestroy(); // Get the number of container objects in the current container content->GetEmbeddedObjectsL(myArray, EContainerObject); i = myArray.Count(); // Two container objects // clear the contents of the array myArray->ResetAndDestroy();
To investigate the objects inside a container, ContentAccess::Content
must first open the container. This
changes CContent
's focus from the current container
to the container specified in the ContentAccess::CContent::OpenContainer()
function.
Open container 1 from the top level of the file
// Get the container objects in the top level of the file content->GetEmbeddedObjects(myArray, EContainerObject); // Find the Unique Id of the first container TPtrC UniqueId = myArray[0]->UniqueId() // Open the first container content->OpenContainer(UniqueId);
Now CContent
can see the contents of Container 1:
At this point, listing the objects that CContent
can see gives six MP3 files and one container object.
// Get the embedded content objects in the current container content->GetEmbeddedObjectsL(myArray, EContentObject); i = myArray.Count(); // Six content objects myArray.ResetAndDestroy(); // Get the number of container objects in the current container content->GetEmbeddedObjectsL(myArray, EContainerObject); i = myArray.Count(); // One container object myArray.ResetAndDestroy();
Note: The same process can be followed again to see the contents of Container 1.1.
Open the metadata specific to an agent
Some agents may expose metadata so
they can be read using a ContentAccess::CData
object.
The format of these meta-data objects is not specified by the Content
Access Framework but can be useful for applications familiar with
the agent to read meta data this way.
CData
objects for agent specific metadata can be opened in the same way
content objects are opened, using the ContentAccess::CContent::OpenContentL()
function.
// Create an array to store the embedded objects ... // Get the embedded "Agent Specific" objects in the current container content->GetEmbeddedObjectsL(myArray, EAgentSpecificObject); // Get the unique Id of the first meta-data object TPtrC uniqueId = myArray[0]->UniqueId(); // create a CData object to read the meta data CData *myMetaData = content->OpenContent(EPeek, uniqueId);
Display content object information
The ContentAccess::CContent::DisplayInfoL()
function allows the application to display file information, such
as last modification date and DRM rights information.
... content->DisplayInfoL(TDisplayInfo aInfo); ...
Retrieve the attributes of a content
Content and container
objects have properties or attributes associated with it. Applications
can retrieve single or mulitple attributes using the ContentAccess::TAttributeCAF
enumeration and the ContentAccess::TStringAttribute
enumeration.
The following example finds the author attribute of a content object.
// define a buffer to store the attribute value string TBuf <100> buf; // retrieve the attribute err = content->GetAttribute(EAuthor, buf, uniqueId); // Display the author's name on screen DisplayAuthor(buf);
Search for a MIME type within a file
If an application
wants to find all the content with a particular MIME type within a
file, it must use ContentAccess::CContent::Search()
. This function produces a list of all content objects with the specified
MIME type that are stored under the current container.
// Create an array for storing the result of the search RStreamablePtrArray<CEmbeddedObject> myArray; // Find how many MP3 files are in Container 1 TInt numMp3 = content->Search(myArray, _L("mpeg/audio"), EFalse);
Read data from a content object
The functions described earlier can be
used to locate a particular content object within a file. ContentAccess::CContent::OpenContentL()
can be used to
read the content object. The UniqueId
parameter can
be used to identify a particular object within the file.
The
call to ContentAccess::CContent::OpenContentL()
leaves if the intent is not permitted. This could occur if the file
is DRM-protected but no rights are present.
If the file is
DRM protected and the call to OpenContentL()
succeeds,
the rights are not consumed at this point. CAF just checks that it
is possible to use the content.
// Open the content object specified by uniqueId with the EPlay intent CData* data = content->OpenContentL(EPlay, uniqueId);
If the application already knows the URI and unique ID of the content
object it wants to read from, it can create a CData
object directly.
CData* data = CData::NewL(TVirtualPathPtr(uri, uniqueId), EPlay, EContentShareReadOnly);
Once the CData
object has been constructed,
it allows the content object to be used as if it were a standalone
unprotected file. The client must call ContentAccess::CData::ExecuteIntent()
when the rights must be consumed. If the file is not DRM-protected,
the call is ignored by the agent handling the file.
TBuf8 <256> buf; data->ExecuteIntent(EPlay); data->Seek(ESEEK_START,SomePosition); data->Read(buf);
There are several overloaded versions
of the ContentAccess::CData::Read()
function. Only
one is illustrated above for example purposes.
When consuming content it is possible that the content data and rights data can be corrupted. This may be due to various reasons including file or disk corruption, transmission errors and so on.
If an agent detects corruption in a content object or in a rights
object, it ends the current DRM operation. The KErrCACorruptContent
, KErrCACorruptRights
or KErrCorrupt
error code is sent to clients, depending on the type of corruption
detected and how the client chooses to handle it.
Note: It is recommended that clients be designed to handle such an error if received, as it can usually be considered a fatal error.
// Create a CData object to read the content ... TRAP(err, data = content->OpenContentLC(EDisplay)); if (err != KErrNone) { if (err == KErrCACorruptRights) { //Agent may have performed some error handling. //Client may need to perform some client specific operations(e.g. cleanup). } else if (err == KErrCorrupt) { DisplayErrorMsg('Unable to decode rights because it is corrupt!'); } else if (err == ...) ... } ...
Agents display the errors on screen when
they occur. Once the error has been acknowledged by the user, the
agent returns the error code to the client application. If a client
application does not want errors displayed on screen, it can request
to disable the agent UI using the ContentAccess::CContent::SetProperty()
function.
// Disable UI content->SetProperty(EAgentPropertyAgentUI , 0);
Some agents may present confirmation dialogs to the
user before allowing them to perform certain operations. For example,
when a user inadvertently tries to delete a DRM-protected file. The
agent can present a dialog asking "are you sure?". The agent can then
return the outcome of the delete to the application, that is KErrCancel
, if the user presses cancel. The ContentAccess::CContent::SetProperty()
function can also be used to disable the confirmation dialogs.
There are two functions available that give the application some control over the rights:
Request rights
ContentAccess::CContent::RequestRights()
allows the application to ask the agent to undertake whatever steps
are necessary to obtain rights for the given content object. Some
agents may not support this mechanism, in which case they returns KErrCANotSupported
.
The request rights call includes
a TRequestStatus
parameter, which allows the application
to be notified of the outcome of the rights request.
content->RequestRights(status, uniqueId);
Display information
ContentAccess::CContent::DisplayInfoL()
allows the application to ask the agent to display the file and/or
rights information for the given content object. The call returns
when the display is dismissed.
Some agents may not support
this mechanism, in which case they return KErrCANotSupported
.
content->DisplayInfoL(EFileProperties, uniqueId);
Get content object notifications
The CContent
interface
supports notification requests for content objects within files. The
events for which an application can request notification are given
by the enumeration ContentAccess::TEventMask
.
The following example requests and cancels notification for rights becoming available:
// Request notification when rights become available for a particular content object content->NotifyStatusChange(ERightsAvailable, status, uniqueId); // Cancel notification request content->CancelNotifyStatusChange(status, uniqueId);
To close a container, use the ContentAccess::CContent::CloseContainer()
function.
For example, Container 1.1 is closed and Container 1 is still open:
//Close Container 1.1 content->CloseContainer(); // Get the embedded content objects in the current container content->GetEmbeddedObjectsL(myArray, EContentObject); i = myArray.Count(); // Six content objects myArray.ResetAndDestroy(); // Get the number of container objects in the current container content->GetEmbeddedObjectsL(myArray, EContainerObject); i = myArray.Count(); // One container object myArray.ResetAndDestroy();