Playing video on BlackBerry

Hi everyone,

I’m implementing video playback on a game that uses cocos2d-x on the PlayBook.

I am aware that the native SDK has an API to play video on the BB graphical context. You can look at the site where everything is explained step by step - https://developer.blackberry.com/native/documentation/Video_playback_overview_1935223_11.html - and there is also an example bundled with the Momentics IDE.

But I’m not sure where to start integrating this API inside cocos2d-x. Would I need to subclass a CCNode to incorporate the video playback routines? I’m not sure if the context the RIM screen API points to is the same OpenGL context that cocos2d-x uses.

I’m a bit kinda lost on this, so maybe if you could point me in the right direction to start implementing this I would really appreciate it :slight_smile: Thanks!

An update on this issue. Just to make it clear, I’m working with cocos2d-2.0-x-2.0.2.

I integrated the VideoWindow example code on cocos2dx, and I’m getting somewhere to play video, but I’m having a problem with the multimedia renderer, which won’t allow to attach an input of the video I’m trying to play.

I also had to modify the blackberry CCEGLView object to make it expose the m_screenWindow property.

This is the init() function of the extended CCLayer that will play the video.

The problem I’m having now with this code is that the mmr_input_attach function fails with a MMR_ERROR_UNKNOWN error code, which has quickly exhausted my options regarding on how to solve this issue. The CCEGLView object has only been modified to add the getScreenWindow() function which only returns the otherwise private m_screenWindow property. Thanks for helping!

// on "init" you need to initialize your instance
bool VideoScene::init()
{
    int rc;
    I18n::init();

    if ( !CCLayer::init() )
    {
        return false;
    }
    CCSize size = CCDirector::sharedDirector()->getWinSize();

    srand(time(0));
    app_id = rand();

    // I/O devices
    static char *audio_device_url = (char *)"audio:default";
    static char video_device_url[PATH_MAX];
    rc = snprintf(video_device_url, PATH_MAX, "screen:?winid=spdintrowindowgroup&wingrp=spdintrowindowgroup");
    if (rc >= PATH_MAX) {
        fprintf(stderr, "URL too long\n");
    }

    // Name of video context...with a random number appended.
    static char video_context_name[PATH_MAX];
    rc = snprintf(video_context_name, PATH_MAX, "spdintrocontextname");
    if (rc >= PATH_MAX) {
        fprintf(stderr, "Video context name too long\n");
    }

    // Window group name...with the same random number appended.
    static char window_group_name[PATH_MAX];
    rc = snprintf(window_group_name, PATH_MAX, "spdintrowindowgroup");
    if (rc >= PATH_MAX) {
        fprintf(stderr, "Video context name too long\n");
    }

    // Video file bundled with our app
    static const char *video_file_relative_path = "app/native/Resources/intro.mp4";

    /* Create the window group for our window, this is important, as we pass the group name
     * to MMR which uses it to 'parent' it's CHILD_WINDOW which contains the video
     */
    if (screen_create_window_group(CCEGLView::sharedOpenGLView()->getScreenWindow(), window_group_name) != 0) {
        return false;
    }

    /*
     * Configure mm-renderer.
     */
    mmr_connection = mmr_connect(NULL);
    if (mmr_connection == NULL) {
        return false;
    }

    mmr_context = mmr_context_create(mmr_connection, video_context_name, 0, S_IRWXU|S_IRWXG|S_IRWXO);
    if (mmr_context == NULL) {
        return false;
    }

    /*
     * Configure video and audio output.
     */
    video_device_output_id = mmr_output_attach(mmr_context, video_device_url, "video");
    if (video_device_output_id == -1) {
        return false;
    }

    audio_device_output_id = mmr_output_attach(mmr_context, audio_device_url, "audio");
    if (audio_device_output_id == -1) {
        return false;
    }

    // Build up the path where our bundled resource is.
    char cwd[PATH_MAX];
    char media_file[PATH_MAX];
    getcwd(cwd,PATH_MAX);

    rc = snprintf(media_file, PATH_MAX, "file://%s/%s", cwd, video_file_relative_path);
    if ((rc == -1) || (rc >= PATH_MAX)) {
        return false;
    }

    /*
     * Attach the input media.
     */
    if (mmr_input_attach(mmr_context, media_file, "track") != 0) {
        const mmr_error_info_t *mmr_error_code = mmr_error_info(mmr_context);
        printf("%d\n", *mmr_error_code);
        return false;
    }


    int video_speed = 1;
    /*
     * Set the speed to 0 to pause the video initially
     */
    if (mmr_speed_set(mmr_context, video_speed) != 0) {
        return false;
    }

    /*
     * Change to the play state, although speed is zero
     */
    if (mmr_play(mmr_context) != 0) {
        return false;
    }

    return true;
}

I finally could connect the video to a cocos2d-x node. Here’s the thing. You have to modify a bit some internals of cocos2d-x.

Download the cocos2d-x from out of the box. You have to modify cocos2dx/platform/blackberry/CCEGLView.cpp to include this snippet (in bold) at the end of the CCEGLView::initGL() function.

bool CCEGLView::initGL()
{
  //...
  m_obScreenSize.width = width;
  m_obScreenSize.height = height;
  // Set vsync.
  // eglSwapInterval(m_eglDisplay, screenSwapInterval);
  if (screen_create_window_group(m_screenWindow, "videowindowgroup") != 0) { //Add
    return false;                                                            //these
  }                                                                          //lines
  return true;
}

Have a node subclass with this code:

#include 
#include 
#include 
#include 
#include 
#include "VideoScene.h"

USING_NS_CC;
// on "init" you need to initialize your instance
bool VideoScene::init()
{
  int rc;
  I18n::init();
  if ( !CCLayer::init() )
  {
    return false;
  }
  CCSize size = CCDirector::sharedDirector()->getWinSize();
  // I/O devices
  static char *audio_device_url = (char *)"audio:default";
  static char video_device_url[PATH_MAX];
  rc = snprintf(video_device_url, PATH_MAX, "screen:?winid=%s&wingrp=%s", "videowindowgroup", "videowindowgroup");
  if (rc >= PATH_MAX) {
    fprintf(stderr, "URL too long\n");
  }
  // Name of video context...with a random number appended.
  static char video_context_name[PATH_MAX];
  rc = snprintf(video_context_name, PATH_MAX, "spdvideocontext");
  if (rc >= PATH_MAX) {
    fprintf(stderr, "Video context name too long\n");
  }
  /*
   * Configure mm-renderer.
   */
  mmr_connection = mmr_connect(NULL);
  if (mmr_connection == NULL) {
    return false;
  }
  mmr_context = mmr_context_create(mmr_connection, video_context_name, 0, S_IRWXU|S_IRWXG|S_IRWXO);
  if (mmr_context == NULL) {
    return false;
  }
  /*
   * Configure video and audio output.
   */
  video_device_output_id = mmr_output_attach(mmr_context, video_device_url, "video");
  if (video_device_output_id == -1) {
    return false;
  }
  audio_device_output_id = mmr_output_attach(mmr_context, audio_device_url, "audio");
  if (audio_device_output_id == -1) {
    return false;
  }
  // Build up the path where our bundled resource is.
  char cwd[PATH_MAX];
  char media_file[PATH_MAX];
  getcwd(cwd,PATH_MAX);
  rc = snprintf(media_file, PATH_MAX, "file://%s/%s", cwd, CCFileUtils::sharedFileUtils()->fullPathFromRelativePath("yourvideo.mp4"));
  if ((rc == -1) || (rc >= PATH_MAX)) {
    return false;
  }
  /*
   * Attach the input media.
   */
  if (mmr_input_attach(mmr_context, media_file, "track") != 0) {
    return false;
  }
  int video_speed = 1000;
  /*
   * Set the speed to 1 to play the video initially
   */
  if (mmr_speed_set(mmr_context, video_speed) != 0) {
    return EXIT_FAILURE;
  }
  /*
   * Change to the play state, although speed is zero
   */
  if (mmr_play(mmr_context) != 0) {
    return false;
  }
  return true;
}
CCScene* VideoScene::scene()
{
  // 'scene' is an autorelease object
  CCScene *scene = CCScene::create();
  // 'layer' is an autorelease object
  VideoScene *layer = VideoScene::create();
  // add layer as a child to scene
  scene->addChild(layer);
  // return the scene
  return scene;
}
void VideoScene::draw() {
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //Video is behing OpenGL context
}

This is the corresponding header:

#ifndef VIDEOSCENE_H_
#define VIDEOSCENE_H_
#include 
#include "cocos2d.h"
class VideoScene: public cocos2d::CCLayer {
public:
  // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
  virtual bool init();
  // there's no 'id' in cpp, so we recommand to return the exactly class pointer
  static cocos2d::CCScene* scene();
  virtual void draw();
  // implement the "static node()" method manually
  CREATE_FUNC(VideoScene);
  // Renderer variables
  mmr_connection_t* mmr_connection;
  mmr_context_t* mmr_context;
  // I/O variables
  int video_device_output_id;
  int audio_device_output_id;
};
#endif /* VIDEOSCENE_H_ */

You are free to move the “videowindowgroup” to a #include if you want to prevent repeating the string everywhere. You should also use the BB event system to capture when the video finishes playing, but that’s outside of this post :slight_smile:

Im exchangin message with the creator of this thread in the blacberry foruns…
http://supportforums.blackberry.com/t5/Native-Development/Playing-video-with-cocos2d-x-cocos2d-2-0-x-2-0-2/td-p/1912433/highlight/false/page/1
Considering that i need to make the OpenGL context transparent what should i do?
The video renderer in the playbook runs behind the opengl context.
On the playbook example VideoWindow it do play a video behind the opengl context, but how i do it in the cocos2d-x main loop (or layer draw function)???

PS: im using cocos2d-x 2.0.3;

Hi:
I know that this question has been posted since a long time ago, but I think that posting an answer will help someone else facing this same problem.
I was able to fix the issue by modifying the value of the screen transparency when configuring the window in the CCEGLView::initGL() function

Change the value of

int screenTransparency = SCREEN_TRANSPARENCY_NONE;

to

int screenTransparency = SCREEN_TRANSPARENCY_SOURCE_OVER;

and after that compile and run your game and you’ll be able to watch the video on screen.