Managing video windows

Updated: April 19, 2023

If you want to render video in a certain area on the display (which you typically want to do when other UI apps are running), you can use the functions from the Screen Graphics Subsystem library to configure the video window and hence, control when and where the content is displayed.

The following example shows how to create a window and window group using the Screen API, instruct mm-renderer to direct the video output to that Screen window and window group, and get a handle to the window and manipulate the output using Screen API functions.

Creating the Screen window and window group

We begin by creating a Screen context, then use this context to create an application window, then use the new window's handle to create the window group:

screen_context_t g_screen_ctxt;

if (screen_create_context(&g_screen_ctxt, SCREEN_APPLICATION_CONTEXT) != 0) {
    /* Error-handling code (e.g., printing an error message) goes here */
    return EXIT_FAILURE;
}

screen_window_t g_screen_win;

if (screen_create_window_type(&g_screen_win,
                              g_screen_ctxt,
                              SCREEN_APPLICATION_WINDOW) != 0) {
    /* Error-handling code goes here */
    return EXIT_FAILURE;
}

// Create a window group -- pass in NULL to generate a unique group name
if (screen_create_window_group(g_screen_win, NULL) != 0) {
    /* Error-handling code goes here */
    return EXIT_FAILURE;
}

Directing mm-renderer video output to the new window and window group

Next, we retrieve the window group name created by Screen and define a window name to use as the window ID. We then use these two properties to define the output URL:
char *window_group_name = (char*)malloc(WINGRP_NAME_MAX_LEN);

// Get the window group name
rc = screen_get_window_property_cv( g_screen_win, 
                                    SCREEN_PROPERTY_GROUP, 
                                    WINGRP_NAME_MAX_LEN,
                                    window_group_name );
if (rc != 0) {
    /* Error-handling code goes here */
    return EXIT_FAILURE;
}

static char video_device_url[PATH_MAX_LEN];
const char *window_name = "appwindow";

// Construct the output URL
rc = snprintf( video_device_url, 
               PATH_MAX_LEN,
               "screen:?winid=%s&wingrp=%s", 
               window_name, 
               window_group_name );

if (rc < 0) {
    /* Error-handling code goes here */
    return EXIT_FAILURE;
}
else if (rc >= PATH_MAX_LEN) {
    /* Error-handling code goes here */
    return EXIT_FAILURE;
}

Defining the video and audio outputs

After the window group is created, we connect to the mm-renderer service and create a playback context. We then attach the video output to the context (by specifying the URL that we defined earlier):
/* Code to connect to mm-renderer and create a context goes here 
   (see "Playing audio content" for an example of doing this) */

const mmr_error_info_t* errorInfo;

// Define the video and audio outputs
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);
    /* Remaining error-handling code goes here */
    return EXIT_FAILURE;
}

/* Code to attach the audio output goes here; see "Playing audio content" */
The video device URL we defined earlier specifies to use Screen for the output and names the window group and window ID, but there are other parameters you can set. In general, Screen output URLs have this form:
screen:?wingrp=window_group&winid=window_id&initflags=invisible
where:
  • window_group is the window group name of the application's top-level window
  • window_id is the ID for the window where the content will be rendered
  • The parameter setting initflags=invisible is optional and causes the window to be invisible upon creation. Enabling this flag allows you to adjust window properties such as size, position, and z-order before making it visible.

Checking that the ID of a newly created window matches our window's name

Next, we 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 indicated in the event matches our output video window. For more complicated applications, this is necessary to distinguish between our video window and another child window belonging to the same window group.

All functions used here are from the Screen API:
// Create the screen context, which is needed to retrieve the event
screen_context_t screen_ctx = 0;
if ( screen_create_context( &screen_ctx, 
                            SCREEN_APPLICATION_CONTEXT) != 0 ) {
    /* Error-handling code goes here */
    return EXIT_FAILURE;
}

// Create an event object that will hold data from the event
screen_event_t screen_event;
screen_create_event(&screen_event);

// Set a timeout of -1 to block until an event is received
screen_get_event(screen_ctx, screen_event, -1);

// Get the type of the event just received
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)) {

    // Store a pointer to the window
    rc = screen_get_event_property_pv( screen_event, 
                                       SCREEN_PROPERTY_WINDOW,
                                       (void**)&video_window );
    if (rc != 0) {
        /* Error-handling code goes here */
        return EXIT_FAILURE;
    }
    // Get the window ID using the new pointer
    char id[256];
    rc = screen_get_window_property_cv( video_window, 
                                        SCREEN_PROPERTY_ID_STRING, 
                                        256, 
                                        id );
    if (rc != 0) {
        /* Error-handling code goes here */
        return EXIT_FAILURE;
    }
    // Compare the ID with our window's name
    if (strncmp(
            id, window_group_name, strlen(window_group_name)) != 0) {
        /* Error-handling code goes here */
        return EXIT_FAILURE;
    }
    // At this point, we know the new window's ID matches the expected 
    // name, so this window is the one we want to use
}

Manipulating the video output through Screen

After we have the video window handle, we can manipulate the output with Screen API 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;
zorder = (app_window_above ? 1: -1);

if (screen_set_window_property_iv( video_window, 
                                   SCREEN_PROPERTY_ZORDER, 
                                   &zorder ) != 0) {
    /* Error-handling code goes here */
    return EXIT_FAILURE;
}

visible = 1; // Set the video window to be visible
if (screen_set_window_property_iv( video_window, 
                                   SCREEN_PROPERTY_VISIBLE, 
                                   &visible) != 0 ) {
    /* Error-handling code goes here */
    return EXIT_FAILURE;
}

For a full list of the windows properties that can be set, see the Screen property types section in the Screen API reference.