Playing video

The ability to play video completes the full multimedia experience. You can play video using Cascades MediaPlayer, or the mm-renderer service.

Playing video with Cascades

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.

You cannot use the Cascades multimedia suite to record video. To record video, you must use the Cascades Camera APIs that are available in the BlackBerry 10 Native SDK. For more information about recording video, see Camera.

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 eight items playing at the same time.

Play a video file

Follow these steps to play a video with the MediaPlayer:

  1. Create a MediaPlayer object.
  2. Import the multimedia library by using the import bb.multimedia 1.4 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.

For information about recording video, see Recording video in the Camera documentation.

Support for accessible apps

You can use the MediaPlayer, which has support for closed captioned content, to make your apps more accessible. You can use embedded closed captioning, or an external caption file to display text or other visual aids 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. Keeping them both the same size and overlaying them allows your captioning content to scale with the video content as 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.

Use embedded captioning streams

Follow these steps to use an embedded stream for your closed captioned content:

  1. Create a second ForeignWindowControl object and set its windowId property.
  2. Before you start your video playback, 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.

Use a formatted XML file

Follow these steps to use a formatted XML file for your closed captioned content:

  1. Create a second ForeignWindowControl object and set its windowId property.
  2. Before you start your video playback, set your closed captioning content in the form of a URL that points 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.

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 windowIdor 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 used by the MediaPlayer to play video files.

The ForeignWindowControl has direct access to the graphics hardware of the device. In other words, your app can 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.

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.

Playing video with multimedia renderer

You can play video with the multimedia renderer (mm-renderer) using the Screen Graphics Subsystem library.

You cannot use the mm-renderer service to record video. To record video, you must use the Camera APIs that are available in the BlackBerry 10 Native SDK. For more information about recording video, see Camera.

For more information about playing audio or video, see Play audio or video.

The following code sample shows you how to give mm-renderer a window group and window ID to use in creating a window on the app's behalf, configure mm-renderer for audio and video output, and get a handle to the window and use the Screen API functions to manipulate the output.

Not applicable

Not applicable

This code sample omits steps for the sake of simplicity. For the full code sample, see the Video overlay sample app.

Define a window name to use as the window ID and retrieve the unique group name created by screen_create_window_group(). You can use these two properties to set the output URL called video_device_url.

const char *window_name = "appwindow";            
char *window_group_name;
int MAX_WINGRP_NAME_LEN = 49;
window_group_name = (char *)malloc(MAX_WINGRP_NAME_LEN);

// Create the video URL for mm-renderer
static char video_device_url[PATH_MAX];

// Create a window group. Pass NULL to generate a unique window group name
if (screen_create_window_group(g_screen_win, NULL) != 0) {
    return EXIT_FAILURE:
}

// Get the window group name
rc = screen_get_window_property_cv(g_screen_win, SCREEN_PROPERTY_GROUP, PATH_MAX, window_group_name);
if (rc != 0) {
    fprintf(stderr, "screen_get_window_property(SCREEN_PROPERTY_GROUP) failed.\n");
    return EXIT_FAILURE;
}

rc = snprintf(video_device_url, PATH_MAX, 
       "screen:?winid=%s&wingrp=%s", window_name, window_group_name);
if (rc < 0) {
    fprintf(stderr, "Error building video device URL string\n");
    return EXIT_FAILURE;
}
else if (rc >= PATH_MAX) {
    fprintf(stderr, "Video device URL too long\n");
    return EXIT_FAILURE;
}

// Create the video context name for mm-renderer
static const char *video_context_name = "videoContext";
//...

After the window group is created, connect to mm-renderer and create a context. Attach the video output to the context by calling mmr_output_attach(), specifying the URL variable (video_device_url) that we set up earlier. Use the same function to attach the audio output.

// Configure mm-renderer
mmr_connection = mmr_connect(NULL);
if (mmr_connection == NULL) {
    fprintf(stderr, "Error connecting to renderer service: %s\n", 
            strerror(errno));
    return EXIT_FAILURE;
}

mmr_context = mmr_context_create( mmr_connection, 
                                  video_context_name, 
                                  0, 
                                  S_IRWXU|S_IRWXG|S_IRWXO );
if (mmr_context == NULL) {
    fprintf(stderr, "Error creating renderer context: %s\n", 
            strerror(errno));
    return EXIT_FAILURE;
}

// Configure video and audio output
const mmr_error_info_t* errorInfo;
video_device_output_id = mmr_output_attach( mmr_context, 
                                            video_device_url,
                                            "video" );
if (video_device_output_id == -1) {
    errorInfo = mmr_error_info(mmr_context);
    fprintf(stderr, "Attaching video output produced error code \
            %d\n", errorInfo->error_code); 
    return EXIT_FAILURE;
}

audio_device_output_id = mmr_output_attach( mmr_context, 
                                            audio_device_url, 
                                            "audio" );
if (audio_device_output_id == -1) {
    // Call mmr_error_info(), display an error message, and exit
    //...
}

Retrieve the handle of the video window from the screen event received when the window is created and check that the ID of the window returned in the event matches the output video window. This ID allows you to distinguish between the video window and another child window belonging to the same window group.

The screen_event_get_event() function belongs to the BlackBerry Platform Services (BPS) API. All other functions for getting event and window properties belong to the Screen API.

screen_event_t screen_event = screen_event_get_event(event);
int event_type;
screen_get_event_property_iv( screen_event, 
                              SCREEN_PROPERTY_TYPE, 
                              &event_type );

// Check if it's a creation event and the video output window 
// has not yet been initialized
if ((event_type == SCREEN_EVENT_CREATE) && 
        (video_window == (screen_window_t)NULL)) {
    char id[256];

    rc = screen_get_event_property_pv( screen_event, 
                                       SCREEN_PROPERTY_WINDOW,
                                       (void**)&video_window );
    if (rc != 0) {
        fprintf(stderr, "Error reading event window: %s\n", 
                strerror(errno));
        return EXIT_FAILURE;
    }

    rc = screen_get_window_property_cv( video_window, 
                                        SCREEN_PROPERTY_ID_STRING, 
                                        256, 
                                        id );
    if (rc != 0) {
        fprintf(stderr, "Error reading window ID: %s\n", 
            strerror(errno));
        return EXIT_FAILURE;
    }

    if (strncmp(
            id, window_group_name, strlen(window_group_name)) != 0)
        fprintf(stderr, "Mismatch in window group names\n");
        return EXIT_FAILURE;
}
//...

After you have the handle of the video window, you can manipulate the video window directly by using Screen API function calls:

// Set the z-order of the video window to put it above or below 
// the main window. Alternate between +1 and -1 to implement
// double-buffering to avoid flickering of output
app_window_above = !app_window_above;
if (app_window_above) {
    screen_val = 1;
}
else {
    screen_val = -1;
}

if (screen_set_window_property_iv( video_window, 
                                   SCREEN_PROPERTY_ZORDER, 
                                   &screen_val ) != 0) {
    fprintf(stderr, "Error setting z-order of video window: %s\n", 
            strerror(errno));
    return EXIT_FAILURE;
}

// Set the video window to be visible
screen_val = 1;
if (screen_set_window_property_iv( video_window, 
                                   SCREEN_PROPERTY_VISIBLE, 
                                   &screen_val) != 0 ) {
    fprintf(stderr, "Error making window visible: %s\n", 
            strerror(errno));
    return EXIT_FAILURE;
}
//...

Play video

Each platform has its own way of playing video. For example, in Cascades you can use the MediaPlayer to play video files and streams. In the mm-renderer service, you can use function calls to its C API to play video files.

Here are some code samples that demonstrate how to play video from various sources. The first two (in the QML and C++ tabs) show you how to play video in Cascades. The last code sample (in the C tab) shows you how to create a context to play video files with the mm-renderer service.

Here's a code sample that shows you how to use an embedded stream for your closed captioned content:

import bb.cascades 1.4
import bb.multimedia 1.4
 
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
                }
            }
        }
    }
}

Not applicable

Not applicable

Here's a code sample that shows you how to use a formatted XML file for your closed captioned content:

import bb.cascades 1.4
import bb.multimedia 1.4

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
                }
            }
        }
    }
}

Not applicable

Not applicable

Here's a code sample that shows you how to set up a video rendering surface:

Not applicable

Here's the C++ header file.

#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++ source 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 is
    // 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");
    }
}

Not applicable

Here's a code sample that shows you how to use the mediaState property to hide the ForeignWindowControl:

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;
        }
    }
    
    // ...
}

Not applicable

Not applicable

Last modified: 2015-07-24



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

comments powered by Disqus