Playing video

You can use the MediaPlayer to play video files and streams. The MediaPlayer allows your app to play video files, and also provides seek, rewind, and fast forward functionality. You can create one MediaPlayer for every video you want to play, but it's not necessary unless you intend to play more than one video at the same time.

When you use BlackBerry 10 OS version 10.1 to perform the concurrent playback of multiple media items, such as local video files or remote streams, you’re only guaranteed to have a maximum of 8 items playing at the same time.

Permissions

You must add the access_shared permission to allow your app to access and play media files that are stored in the shared areas of the device.

For more information about adding permissions to your bar-descriptor.xml file, see The bar-descriptor.xml file.

For more information about the shared data areas of the device, see File system access.

Flow and states

The architectural flow diagram below illustrates the various states of the MediaPlayer class. The MediaPlayer must proceed through various states before going to the Started state where it can play media content.

Unprepared state

The MediaPlayer begins in an Unprepared state, which means that it cannot play content because it doesn't have the necessary resources to do so.

Prepared and Started state

When the MediaPlayer acquires the resources it needs to play the media content, it's in a Prepared state. The Prepared state usually moves quickly into the Started state where it can play media content.

It's not unusual to have an app go from the Unprepared state directly to the Started state and begin playing content. In this case, it may appear that the Prepared state was skipped, but it's not skipped, it's just occurring briefly.

A flow diagram showing the media player states.

Playing a video file

You can use the MediaPlayer to play video by following these steps:

  1. Create a MediaPlayer object.
  2. Import the multimedia library by using the import bb.multimedia 1.2 import statement.
  3. Set the path as the source URL to the video that you want to play. The source path to the video you want to play can point to a local or remote video file.
  4. Create a ForeignWindowControl object and set its windowId property.
  5. Provide the windowId of the ForeignWindowControl to the MediaPlayer. The MediaPlayer uses this ForeignWindowControl object to display the video.
  6. Set the videoOutput property of the player, which determines the video out device display. The video output device can be VideoOutput::None, VideoOutput::PrimaryDisplay, or VideoOutput::SecondaryDisplay.
  7. Call the player's play() function to play the video file.
  8. Call the player's stop() function to stop the video file.

Using closed captioning

The MediaPlayer provides support for closed captioning content. You can use embedded closed captioning, or an external caption file to display text or other visual aid on a device screen. To use this functionality, you must create a second ForeignWindowControl to hold the caption screen, and overlay it directly on top of your video window. These two video surfaces are the same size, but you can resize them independently if you want. Keeping them both the same size and overlaying them means that the captioning content scales with the video content if users pinch, pan, zoom in to, or zoom out from the video.

Adding closed captioning content to your video files requires the use of third-party software. The owner of the media content is responsible for creating and embedding the closed captioning data into their video content.

Using embedded captioning streams

Here are the overview steps for using an embedded stream for your closed captioned content:

  1. Create a second ForeignWindowControl object and set its windowId property.
  2. Before you trigger your video to play, set your closed captioning content, in the form of an index of an embedded stream, with the setClosedCaptionIndex(unsigned int index) function.
  3. Enable your closed captioning screen by setting the MediaPlayer closedCaptionWindowId property to the ID of the ForeignWindowControl being used for the closed captioned content.
  4. Play your video.

When your media source uses embedded captioning, you can switch closed captioning streams by setting, or resetting, the value of the index parameter in the setClosedCaptionIndex(unsigned int index) function.

There is no way for your app to know which streams are available. The API defaults to the stream at index 0, but you can set it to a particular stream by using that stream's index. If a stream isn't available at the specified index then no captions are displayed.

import bb.cascades 1.2
import bb.multimedia 1.2
 
Page {
    Container {
        layout: AbsoluteLayout {
        }
         
        attachedObjects:[
            MediaPlayer {
                id: vidPlayer
                sourceUrl: "/path/to/video.mp4"
 
                videoOutput: VideoOutput.PrimaryDisplay
 
                // The name of the window to create
                windowId: fwcVideoSurface.windowId
                closedCaptionWindowId: fwcCaptionSurface.windowId
            }
        ]
         
        ForeignWindowControl {
            id: fwcVideoSurface
            windowId: "myVideoSurface"
 
            updatedProperties: WindowProperty.Size |
                               WindowProperty.Position |
                               WindowProperty.Visible
 
            preferredWidth: 1280
            preferredHeight: 768
        }

        // ForeignWindowControl for captioning content
        ForeignWindowControl {
            id: fwcCaptionSurface
            windowId: "myCaptionSurface"
 
            updatedProperties: WindowProperty.Size |
                               WindowProperty.Position |
                               WindowProperty.Visible
 
            preferredWidth: 1280
            preferredHeight: 768
        }
         
        Button {
            text: "Play Video"
            onClicked: {
            
            // Used for external captioning, this must happen 
            // before the player goes into either the prepare
            // or play state
            vidPlayer.closedCaptionIndex = < Stream >; 
                if (vidPlayer.play() != MediaError.None) {
                    // Put your error handling code here
                }
            }
        }
         
        Button {
            text: "Stop Video"
            onClicked: {
                if (vidPlayer.stop() != MediaError.None) {
                    // Put your error handing code here
                }
            }
        }
    }
}

Using a formatted XML file

Here are the overview steps for using a formatted XML file for your closed captioned content:

  1. Create a second ForeignWindowControl object and set its windowId property.
  2. Before you trigger your video to play, set your closed captioning content in the form of an URL to your formatted XML file with the setClosedCaptionUrl() function.
  3. Enable your closed captioning screen by setting the MediaPlayer closedCaptionWindowId property to the ID of the ForeignWindowControl being used for the closed captioned content.
  4. Play your video.
import bb.cascades 1.2
import bb.multimedia 1.2

Page {
    Container {
        layout: AbsoluteLayout {
        }
        
        attachedObjects:[
            MediaPlayer {
                id: vidPlayer
                sourceUrl: "/path/to/video.mp4"

                videoOutput: VideoOutput.PrimaryDisplay

                // The name of the window to create
                windowId: fwcVideoSurface.windowId

                // Closed Captioning code
                closedCaptionWindowId: fwcCaptionSurface.windowId
            }
        ]
        
        ForeignWindowControl {
            id: fwcVideoSurface
            windowId: "myVideoSurface"

            updatedProperties: WindowProperty.Size | 
                               WindowProperty.Position | 
                               WindowProperty.Visible

            preferredWidth: 1280
            preferredHeight: 768
        }
        
        ForeignWindowControl {
            id: fwcCaptionSurface
            windowId: "myCaptionSurface"
            
            updatedProperties: WindowProperty.Size |
            WindowProperty.Position |
            WindowProperty.Visible
            
            preferredWidth: 1280
            preferredHeight: 768
        }
        
        
        Button {
            text: "Play Video"
            onClicked: {
                // Used for external captioning
                // This URL must be set before prepare/play
                vidPlayer.setClosedCaptionUrl(1, "/path/to/cc.xml");                
                
                if (vidPlayer.play() != MediaError.None) {
                    // Put your error handling code here
                }
            }
        }
        
        Button {
            text: "Stop Video"
            onClicked: {
                if (vidPlayer.stop() != MediaError.None) {
                    // Put your error handing code here
                }
            }
        }
    }
}

Using a video rendering surface

The MediaPlayer creates a rendering surface, outside of Cascades, to play video files. However, the MediaPlayer itself cannot be used as a rendering surface to play videos. Instead, the MediaPlayer uses a ForeignWindowControl as its rendering surface. The ForeignWindowControl is used to embed a window from the Screen Graphics Subsystem (Screen window) into the Cascades scene graph. You must set the windowId and/or windowHandle properties, as well as the windowGroup property to bind the ForeignWindowControl to a Screen window.

The windowId of the ForeignWindowControl is set in the MediaPlayer, which allows the ForeignWindowControl to provide a rendering surface for the MediaPlayer to use. It also allows the ForeignWindowControl to connect to, scale, and manage the rendering surface that is used by the MediaPlayer to play video files.

The ForeignWindowControl has direct access to the graphics hardware of the device. In other words, this allows your app to use the rendering surface of the ForeignWindowControl as a low-level platform window. As a rendering surface, this platform window is very efficient at displaying video. The platform window, or Screen, can also access all of the functionality provided in the libscreen library.

This code sample shows you how to use the ForeignWindowControl. When you use C++ code to set up a ForeignWindowControl, you must set a value for the windowGroup property to make it all work. You can use the setWindowGroup() function to set the windowGroup.

Setting the windowGroup property alone, without setting the windowId or windowHandle property, is not sufficient for binding the ForeignWindowControl to a Screen window. Both the windowGroup and windowId or windowHandle properties must be set in order to bind a Screen window to the ForeignWindowControl.

Here's the C++ header file which shows you how to set everything up.

#ifndef ApplicationUI_HPP_
#define ApplicationUI_HPP_

#include <QObject>
#include <bb/cascades/ForeignWindowControl>
#include <bb/cascades/Container>
#include <bb/multimedia/MediaPlayer>
#include <bb/system/SystemToast>

namespace bb {namespace cascades {class Application;}}

class ApplicationUI: public QObject {
	Q_OBJECT
public:
	ApplicationUI(bb::cascades::Application *app);
	virtual ~ApplicationUI() {}

private slots:
	void onForeignWindowBoundingChanged(bool isBound);
	void showToast(QString message);

private:
	bb::cascades::Container *m_container;
	bb::cascades::ForeignWindowControl *m_foreignWindowControl;
	bb::multimedia::MediaPlayer *m_mediaPlayer;
};

#endif /* ApplicationUI_HPP_ */

Here's the C++ header file which shows you how to set the windowId and windowGroup properties to bind the ForeignWindowControl to a Screen window.

#include "applicationui.hpp"

#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/AbstractPane>
#include <bb/multimedia/VideoOutput>
#include <bb/multimedia/MediaError>
#include <bb/cascades/Window>

using namespace bb::cascades;

ApplicationUI::ApplicationUI(bb::cascades::Application *app) :
    QObject(app) 
{
    // Create scene document from main.qml asset, the parent is set
    // to ensure the document gets destroyed properly at shut down
    QmlDocument *qml = QmlDocument::create("asset:///main.qml")
        .parent(this);

    // Create root object for the UI
    AbstractPane *root = qml->createRootObject<AbstractPane>();

    m_container = root->findChild<Container*>("mainContainer");

    // Set created root object as the application scene
    app->setScene(root);

    // Find the Container where the ForeignWindowControl will be
    // created
    if (m_container) {
      // Create the ForeignWindowControl
      m_foreignWindowControl = ForeignWindowControl::create()
          .windowId("MyWindow")
          .updatedProperties(WindowProperty::Size
              | WindowProperty::Position
              | WindowProperty::Visible);

      // Add the ForeignWindowControl to the main container
      m_container->add(m_foreignWindowControl);

      m_foreignWindowControl->
          setVisible(m_foreignWindowControl->isBoundToWindow());

      // If any Q_ASSERT statement(s) indicate that the slot
      // failed to connect to the signal, make sure you know
      // exactly why this happened. This is not normal, and 
      // will cause your app to stop working
      bool connResult;

      // Since the variable is not used in the app, this is
      // added to avoid a compiler warning
      Q_UNUSED(connResult);

      connResult = connect(m_foreignWindowControl,
          SIGNAL(boundToWindowChanged(bool)),
          this, SLOT(onForeignWindowBoundingChanged(bool)));

      Q_ASSERT(connResult);

      m_mediaPlayer = new bb::multimedia::MediaPlayer(this);
      m_mediaPlayer->setWindowId(m_foreignWindowControl->windowId());

      // Set the windowGroup property
      m_mediaPlayer->
        setWindowGroupId(m_foreignWindowControl->windowGroup());
      m_mediaPlayer->
       setVideoOutput(bb::multimedia::VideoOutput::PrimaryDisplay);
      m_mediaPlayer->setSourceUrl(QUrl("asset:///myVideo.wmv"));

      // Play the video
      m_mediaPlayer->play();
    }
    else {
        // Could not find Container
        showToast("An error has occurred");
    }
}

// Displays a message in a toast
void ApplicationUI::showToast(QString message) 
{
  bb::system::SystemToast *toast = new bb::system::SystemToast(this);

  toast->setBody(message);
  toast->setPosition(bb::system::SystemUiPosition::MiddleCenter);
  toast->show();
}

void ApplicationUI::onForeignWindowBoundingChanged(bool isBound) 
{
    m_foreignWindowControl->setVisible(isBound);

    if (!isBound) {
        showToast("ForeignWindowControl is not bound");
    }
}

The MediaPlayer has a series of signals that you can connect signal handlers to in order to manage your video player's behavior. These signals give you the opportunity to handle signals like onMediaStateChanged, which indicates that the state of the media player has changed. A state change can happen when the media player is playing a video and it's paused or stopped by the user. It can also happen if the media player loses the resources that it requires to continue playing the video.

The MediaPlayer emits many other signals that you can use to control its behavior. Here are code samples that show you how to work with the onMediaStateChanged signal handler. This code sample uses a custom onMediaStatedChanged signal handler and a switch statement with a case for each MediaState type. The code sample uses only the Unprepared() signal. In this case, it hides the ForeignWindowControl to make it disappear.

MediaPlayer {
    // ...
    
    onMediaStateChanged: {
        switch (vidPlayer.mediaState) {
            case 0:
                // Unprepared = 0.
                fwcVideoSurface.visible = false;
                break;

            case 1:
                // Prepared = 1.
                break;

            case 2:
                // Started = 2.
                break;

            case 3:
                // Paused = 3.
                break;

            case 4:
                // Stopped = 4.
                break;
        }
    }
    
    // ...
}

Last modified: 2014-09-30



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus