This chapter describes the Movie Library playback facility. It explains how to create and configure a window for playing movies and how to control and synchronize movie playback. The Movie Library can use either OpenGL or IRIS GL rendering to show movies, and you can combine the display of graphics and movies using the techniques described in this chapter.
This chapter also explains how to handle movie events in order to respond to user input or to monitor movie playback for system or I/O errors.
You can play one or more movies at a time, all in one window or in separate windows. Follow these steps to play movies:
If playing more than one movie at a time, set a hint for multiple movie playback by calling mvSetNumMoviesHint().
Open the movie(s) that you want to play, using mvOpenFile(), mvOpenFD(), or mvOpenMem().
Prepare a window for displaying the movie, as described in “Creating and Configuring a Playback Window”.
Set up event handling, as described in “Handling Events”.
Bind the window to the movie, as described in “Binding a Movie to a Window for Playback”. This step tells the Movie Library to display your movie inside the window you have prepared.
Show the window.
Set the movie into motion by calling mvPlay(), as described in “Controlling Movie Playback”.
Process movie and window events as needed, as described in “Handling Events”.
When finished, call mvClose() to destroy the movie instance. You must complete this step before destroying the window in which the movie is playing.
If the application opens more than one movie at a time, set a hint for the Movie Library before opening any movies.
The Movie Library uses certain IRIX system facilities such as multiple processes and shared arenas whose size must be configured in advance. The Movie Library chooses defaults for these resources that are suitable for most applications; however, to play several movies at the same time, you must set a hint at initialization time to indicate how many movies are likely to be played so that the proper system resource allocations can be made.
To set a hint for the number of movies an application will play, call mvSetNumMoviesHint(). Its function prototype is:
DMstatus mvSetNumMoviesHint (int numMovies) |
| numMovies | is the number of movies likely to be played |
To retrieve the number of movies at which the hint is currently set, call mvGetNumMoviesHint(). Its function prototype is:
int mvGetNumMoviesHint (void) |
To open a movie for playback, call mvOpenFile(), mvOpenMem(), or mvOpenFD(), as described in Chapter 29, “File I/O and Editing Movies with the Movie Library.”These calls return an identifier, movieid, for a movie instance that is used to reference the movie in subsequent Movie Library calls.
The Movie Library requires a mixed-model GL/X window (an X window configured for either OpenGL or IRIS GL rendering) in single-buffered RGB mode.
See the OpenGL Programming Guide for information on using the OpenGL window configuration routines, including: glXgetconfig(3g), glXCreateContext(3g), and glXChooseVisual(3g).
There are two methods for creating a GL/X window:
Create a mixed-model window using Xlib calls. You can paste the sample code from glxhelper.c in /usr/people/4Dgifts/examples/dmedia/movie/common/ into your application to create an appropriate window. See Graphics Library Programming Tools and Techniques for details about the calls used in glxhelper.c.
If you are using IRIS IM, create a GLXMDraw widget. See the IRIS IM Programming Notes for details.
Example 30-1 is a code fragment that shows how to create an IRIS GL playback window. (KeyReleaseMask is needed only if the application needs to listen to keystrokes when the mouse pointer is inside the movie window.)
Example 30-1. Creating an IRIS GL Playback Window
/*********
*
* Open a connection to the X server, and create an X window
* suitable for GL rendering here.
*
*********/
static DMstatus createXWindow( Display **dpy, Window *win,int width, int height )
{
XSetWindowAttributes childWinAttribs;
/*
*Open a connection to the X server.
*/
*dpy = XOpenDisplay( 0 );
if ( *dpy == NULL ) {
fprintf( stderr, "%s: Cannot open display.\n", programName );
return DM_FAILURE;
}
childWinAttribs.colormap = DefaultColormap( *dpy, DefaultScreen( *dpy ) );
/*
* Even if we don't use it, childWinAttribs.border_pixel must be something.
*/
childWinAttribs.border_pixel = 0;
/*
* Create an X window configured for GL rendering, using
* the helper functions defined in glxhelper.c
*/
*win = GLXCreateWindow( *dpy, RootWindow( *dpy, DefaultScreen( *dpy ) ),
100, 100, width, height, 0, CWColormap|CWBorderPixel,
&childWinAttribs, GLXrgbSingleBuffer );
XSelectInput( *dpy, *win, ExposureMask | StructureNotifyMask | KeyReleaseMask );
return DM_SUCCESS;
}
|
You can control these settings for the playback display:
| Background | what color fills the space between the movie and the window perimeter | |
| View size | how big the movie frame is | |
| View offset | where the movie frame is relative to the window origin |
You can call the routines that configure the size, offset, and background color of the playback window whether or not a movie is currently playing within that window. Figure 30-1 diagrams the view settings.
The Movie Library automatically centers the movie in the view you specify and fills the areas within the view that are not part of the movie frame with a background color. The default background color is “SGI light gray,” which has the components (170, 170, 170).
To set the background color, call mvSetViewBackground(). Its function prototype is:
void mvSetViewBackground ( MVid movieid, unsigned short red,
unsigned short green, unsigned short blue )
|
| red, green, blue | are color components in the range 0-255 |
To get the background color, call mvGetViewBackground(). Its function prototype is:
void mvGetViewBackground ( MVid movieid,
unsigned short* redreturn,
unsigned short* greenreturn,
unsigned short* bluereturn )
|
| redreturn, greenreturn, bluereturn |
|
This section explains how to specify the size of the viewing area used for rendering a movie within a window. The default view size is the width and height of the movie frame, as it is specified in the movie file.
The Movie Library zooms the movie frame to fill the specified area as closely as possible, while adhering to the pixel zooming restraints imposed by the IRIS GL or Open GL renderer. Any remaining area between the window and the movie viewing area is filled with the background color that you specified.
![]() | Note: Currently, only integer zooming (2×, 3×, and so on) is supported, regardless of the graphics hardware platform. |
To specify the size of the movie viewing area, call mvSetViewSize(). Its function prototype is:
void mvSetViewSize ( MVid movieid, int newwidth,
int newheight,
DMboolean keepaspect )
|
| newwidth | is the requested width, in pixels, of the viewing area | |
| newheight | is the requested height, in pixels, of the viewing area | |
| keepaspect | controls whether the aspect ratio is preserved when zooming the movie: |
To determine the viewing area size that the Movie Library will actually create for a desired width and height, call mvQueryViewSize(). Its function prototype is:
void mvQueryViewSize ( MVid movieid, int width, int height,
DMboolean keepaspect,int* widthreturn,
int* heightreturn )
|
| width | is the requested width, in pixels, of the viewing area | |
| height | is the requested height, in pixels, of the viewing area | |
| keepaspect | controls whether the aspect ratio is preserved when zooming the movie: | |
| widthreturn | is a pointer into which the actual width, in pixels, of the viewing area is returned | |
| heightreturn | is a pointer into which the actual height, in pixels, of the viewing area is returned |
If a movie appears by itself with no other graphics in a GL/X window, you might want to ensure that your code chooses the proper window size by calling mvQueryViewSize() in advance, and using the results to resize the GL/X window to be the same size as the movie—or you can simply choose a background color for the unused portions of the display by calling mvSetViewBackground().
If the movie appears in a GL/X window along with other graphic elements, such as a movie that is embedded in a larger display, you might be more concerned about setting an exact view size, so you should set the view size carefully by first calling mvQueryViewSize() and then using the results as inputs to mvSetViewSize(). This makes sure that the Movie Library draws on the display only over regions that the movie frame actually occupies.
To get the requested view size, call mvGetViewSize(). Its function prototype is:
void mvGetViewSize ( MVid movieid, int *widthreturn,
int *heightreturn)
|
| widthreturn | is a pointer into which the width is returned | |
| heightreturn | is a pointer into which the height is returned |
mvGetViewSize() always returns the last requested size of the movie view, regardless of the movie's actual view size. Thus, you can inspect both the requested size by calling mvGetViewSize(), and the actual frame size by calling mvQueryViewSize().
This section explains how to set and get the movie viewing location offset, as measured from the window origin.
IRIS GL and the X Window System differ in where they assign the coordinates of the origin (0,0). IRIS GL defines (0,0) as the lower left corner of the screen; the X coordinate system defines (0,0) as the upper left corner of the screen. The default offset is (0, 0) in the IRIS GL coordinate system.
To set the viewing location offset, call mvSetViewOffset(). Its function prototype is:
void mvSetViewOffset ( MVid movieid, int offsetx,
int offsety, DMboolean glcoordsystem )
|
| offsetx | is the offset from the origin in the x (horizontal) dimension | |
| offsety | is the offset from the origin in the y (vertical) dimension | |
| glcoordsystem | is a boolean that you use to specify the screen coordinate system for the y offset If glcoordsystem is DM_TRUE, the Movie Library assumes that you are using the IRIS GL screen coordinate system; if glcoordsystem is DM_FALSE, the Movie Library assumes that you are using the X coordinate system. |
To determine the offset that the Movie Library will actually produce for a given offset, call mvQueryViewOffset(). Its function prototype is:
void mvQueryViewOffset ( MVid movieid,
int offsetx,
int offsety,
int* offsetxreturn,
int* offsetyreturn,
DMboolean glcoordsystem )
|
| offsetx | is the requested offset from the origin in the x (horizontal) dimension | |
| offsety | is the requested offset from the origin in the y (vertical) dimension | |
| offsetxreturn | is a pointer into which the actual offset from the origin in the x (horizontal) dimension is returned | |
| offsetyreturn | is a pointer into which the actual offset from the origin in the y (vertical) dimension is returned | |
| glcoordsystem | specifies the screen coordinate system for the y offset: |
To get the requested offset value, call mvGetViewOffset(). Its function prototype is:
void mvGetViewOffset ( MVid movieid, int* offsetxreturn,
int* offsetyreturn,
DMboolean glcoordsystem )
|
| offsetxreturn | is a pointer into which the requested offset from the origin in the x (horizontal) dimension is returned | |
| offsetyreturn | is a pointer into which the requested offset from the origin in the y (vertical) dimension is returned | |
| glcoordsystem | specifies the screen coordinate system for the y offset: |
mvGetViewOffset() always returns the last requested origin of the movie frame, regardless of the movie's actual origin. Thus, you can inspect both the requested origin by calling mvGetViewOffset(), and the actual origin by calling mvQueryViewOffset().
In order to play a movie, you need to bind it to a window. Binding a movie to a window performs internal initialization that prepares the movie for playback in that window; therefore, you must bind the movie to the window before calling any playback routines on that movie.
To bind a movie to an IRIS GL window, call mvBindWindow(). To bind a movie to an OpenGL window, call mvBindOpenGLWindow().
The function prototypes for mvBindWindow() and mvBindOpenGLWindow() are:
DMstatus mvBindWindow(MVid movieid, Display* dpy, Window win)
DMstatus mvBindOpenGLWindow(MVid movieid, Display* dpy,
Window win, GLXContext ctxt)
|
| dpy | is the display on which the movie plays | |
| win | is the window in which the movie plays | |
| ctxt | is the OpenGL graphics context for the movie |
For example, to bind the movie named theMovie to the GL/X window identified by win on the display dpy, using IRIS GL:
if ( mvBindWindow( theMovie, *dpy, win ) != DM_SUCCESS ) {
fprintf( stderr, “%s: Could not bind movie to window.\n”,
programName );
}
|
You can bind a movie to only one window at a time. The movie remains bound to that window until you call mvClose() to close the movie. You can call mvBindWindow() or mvBindOpenGLWindow() only once per movie.
If the specified movie has an audio track, mvBindWindow() or mvBindOpenGLWindow() attempts to open an audio port by calling ALopenport() from the Audio Library. If the attempt is successful, an audio port with the name movipid:movieid is created:
where:
| pid | is the system process ID that is using the audio port | |
| movieid | is the movie ID containing the soundtrack that is being played on the port |
See the ALopenport(3A) man page for more information.
If it is not possible to open an audio port, either because there are no free audio ports or because your system does not have audio capability, the Movie Library plays the image track without playing the accompanying audio track.
![]() | Note: Audio is not part of the X protocol, so the audio track will not follow the DISPLAY variable if the movie is played remotely. |
To play more than one movie in the same window, bind the different movies to the same window by calling mvBindWindow() or mvBindOpenGLWindow() with the same dpy and win values for each movieid.
Example 30-2 is a code fragment that binds several movies to a window, and then shifts the position of each movie so they do not overlap.
Example 30-2. Binding a Window for Playing Multiple Movies
static DMstatus bindWinToMovies( MVid* movieList, int numMovies,Display* dpy, Window win )
{
int i;
MVid currentMovie;
for ( i = 0; i < numMovies; i++ ) {
currentMovie = movieList[ i ];
/*
* Bind the GL window to the movie. This will cause several
* movies to play in the same window.
*/
if ( mvBindWindow( currentMovie, dpy, win ) != DM_SUCCESS ) {
fprintf( stderr, "%s: Could not bind movie to window.\n",
getProgramName() );
return DM_FAILURE;
}
}
/*
* Initially, all the movies appear at the same display location.
* We call a helper function to move them so they do not overlap.
* The helper function calls mvSetViewOffset(3mv) to accomplish
* this task.
*/
if ( setMovieViewOffsets() != DM_SUCCESS ) {
return DM_FAILURE;
}
return DM_SUCCESS;
}
|
The Movie Library provides calls for controlling movie playback that let you start and stop playback, control audio playback, control the loop mode and loop limit, and scrub to a random frame.
To set a movie into motion, call mvPlay(). Its function prototype is:
void mvPlay ( MVid movieid ) |
To halt playback, call mvStop(). Its function prototype is:
void mvStop ( MVid movieid ) |
Playing or stopping a movie does not affect any of the other settings such as playback speed or direction; for example, if you stop a movie that is playing backward, then start it again, the movie will continue to play backward.
If a movie has an audio track, the Movie Library plays the audio for you automatically if it is able to allocate an audio port.
You can control audio muting during playback. Muting controls only whether audio is played; it does not alter the audio volume. To control audio muting, call mvSetEnableAudio(). Its function prototype is:
void mvSetEnableAudio ( MVid movieid, DMboolean onoff ) |
where:
| onoff | controls audio muting: DM_TRUE enables audio, DM_FALSE mutes audio |
To retrieve the current mute setting, call mvGetEnableAudio(). Its function prototype is:
DMboolean mvGetEnableAudio ( MVid movieid ) |
Example 30-3 is an excerpt from manymovieEvents.c , in /usr/people/4Dgifts/examples/dmedia/movie/manymovie, that shows how to use audio muting. See “Playing Multiple Movies” in Chapter 32 for a complete description of manymovie.
Example 30-3. Enabling and Muting Audio Playback
static void toggleAudioMuting( MVid *theMovies , int numMoviesInWin )
{
int i;
for( i = 0;i < numMoviesInWin; i++ ) {
printf("%s: %d, toggle mute to ", getProgramName(), theMovies[i]);
if ( mvGetEnableAudio( theMovies[i] ) ) {
printf( "OFF\n" );
mvSetEnableAudio( theMovies[i], DM_FALSE );
} else {
printf( "ON\n" );
mvSetEnableAudio( theMovies[i], DM_TRUE );
}
}
}
|
If you play several movies at the same time, it is possible that not all of them will have the same audio sample rate. The audio hardware uses the same sample rate for all concurrent audio processes; therefore, if you play two (or more) movies that have different audio sample rates at the same time, audio plays at the wrong speed for movies whose sample rate is different than the current audio hardware rate setting. This happens only when playing more than one movie at a time; the audio sample rate is set to the proper value when playing a movie by itself.
To guarantee the proper playback rate for a certain movie when it is played in conjunction with other movies, call mvSetPrimaryAudio(). Its function prototype is:
void mvSetPrimaryAudio ( MVid movieid ) |
To determine which movie has the primary audio rate control, call mvGetPrimaryAudio(). Its function prototype is:
MVid mvGetPrimaryAudio ( void ) |
If you are writing an application that allows users to select and play one movie at a time from among several movies, you want the audio playback to be correct for each movie, even though not all of the movies have the same sample rate. In that case, call mvSetPrimaryAudio() on the selected movie before calling mvPlay().
Example 30-4 is an excerpt from manymovieEvents.c , in /usr/people/4Dgifts/examples/dmedia/movie/manymovie, which guarantees the proper audio rate for the first movie that the user enters on the command line. See “Playing Multiple Movies” in Chapter 32 for a complete description of manymovie.
Example 30-4. Designating a Movie as the Primary Audio Rate Controller
static void playTheMovies( MVid *theMovies, int numMoviesInWin )
{
int i;
mvSetPrimaryAudio( theMovies[0] );
for( i = 0;i < numMoviesInWin; i++ ) {
printf("%s: %d, play\n", getProgramName(), theMovies[i]);
mvPlay( theMovies[i] );
}
}
|
Looping is the process of repeatedly playing movie frames. You can define the starting and ending frame for movie playback, thereby allowing looping on an entire movie or only a fragment of the movie. You can define only one such loop per movie.
The playback loop mode, which is independent of the default loop mode that is stored in the movie, controls the playback behavior:
| None | The movie (or fragment) plays once through and then stops. | |
| Continuous | The movie (or fragment) plays repeatedly. | |
| Swinging | The movie (or fragment) plays repeatedly back and forth from start to end in a forward direction, then from end to start in a backward direction. |
If the movie was playing backward when looping began, the movie will continue to loop backward.
To set the playback loop mode, call mvSetPlayLoopMode(). Its function prototype is:
void mvSetPlayLoopMode ( MVid movieid, MVloopmode newloopmode ) |
where:
| newloopmode | is the loop mode: MV_LOOP_NONE to play once through (default) |
You can change the loop mode whether or not the movie is playing. If you change the loop mode from swinging to continuous while the movie is on a backward swing, the movie will play backward continuously.
The Silicon Graphics movie file format lets you store a default loop mode setting within the movie file, which is independent of the playback loop mode setting. When opening a movie file, the Movie Library obtains the default loop mode from the movie if it is set; otherwise, it assumes MV_LOOP_NONE is the default loop mode. So, when you first play the movie, it uses the default loop mode until you change the playback loop mode.
To change the default loop mode stored in a movie, call mvSetLoopMode(), as described in “Setting and Getting the Default Movie Loop Mode”.
To retrieve the current loop mode setting for a specified movie, call mvGetPlayLoopMode(). Its function prototype is:
MVloopmode mvGetPlayLoopMode ( MVid movieid ) |
Example 30-5 is an excerpt from manymovieEvents.c , in /usr/people/4Dgifts/examples/dmedia/movie/manymovie, which is described in “Playing Multiple Movies” in Chapter 32, that toggles the loop mode for a movie that is playing.
Example 30-5. Setting and Getting the Loop Mode
static void stepToNextLoopState ( MVid *theMovies,
int numMoviesInWin )
{
int i;
MVloopmode loopMode;
for( i = 0;i < numMoviesInWin; i++ ) {
printf("%s: %d, change loop state to ",
getProgramName(), theMovies[i]);
switch ( mvGetPlayLoopMode( theMovies[i] ) ) {
case MV_LOOP_NONE:
loopMode = MV_LOOP_CONTINUOUSLY;
printf( "CONTINUOUS\n" );
break;
case MV_LOOP_CONTINUOUSLY:
loopMode = MV_LOOP_SWINGING;
printf( "SWINGING\n" );
break;
case MV_LOOP_SWINGING:
loopMode = MV_LOOP_NONE;
printf( "NONE\n" );
break;
}
mvSetPlayLoopMode( theMovies[i], loopMode );
}
}
|
The Movie Library provides a facility for counting the number of times a movie has played. You can use this facility to query how many times the movie has played and to limit the number of times playback can loop.
Initialize the counter by calling mvSetPlayLoopCount(), and provide an upper bound for the number of loops by calling mvSetPlayLoopLimit() if you want to limit the number of times the movie is played.
You can query for the current loop count and reset it if desired. For example, if your application lets the user stop and restart playback, you can either reset the loop count when playback resumes or continue counting from the number of loops that were already completed when the movie was stopped. In swinging mode, each play of the movie forward or backward counts as one loop.
To set the loop count, call mvSetPlayLoopCount(). Its function prototype is:
void mvSetPlayLoopCount( MVid movieid, MVframe newloopcount ) |
where:
| newloopcount | is the value to which you want to set the loop count |
To retrieve the current loop count for a movie, call mvGetPlayLoopCount(). Its function prototype is:
MVframe mvGetPlayLoopCount ( MVid movieid ) |
To limit the number of times a movie can play in continuous or swinging mode, call mvSetPlayLoopLimit(). Its function prototype is:
void mvSetPlayLoopLimit ( MVid movieid, MVframe newlooplimit) |
where:
| newlooplimit | is the number of times to loop or swing. This value must be either an integer or MV_LIMIT_FOREVER to keep playing a movie forever. |
To retrieve the current loop limit value, call mvGetPlayLoopLimit(). Its function prototype is:
MVframe mvGetPlayLoopLimit ( MVid movieid ) |
You can play or loop-play a movie fragment by selecting a start frame and an end frame within the movie. You can define only one such fragment per movie and the fragment must contain at least one frame.
The start frame number must be less than or equal to the end frame number; frame 0 (the first frame of the movie) is the default start frame. If the current frame is outside the range specified, the Movie Library treats the movie as having reached end-of-media.
To define the start frame for a movie (or fragment), call mvSetStartFrame(). Its function prototype is:
void mvSetStartFrame ( MVid movieid, MVframe startframe ) |
where:
| startframe | is the frame number where playback begins |
To define the end frame of a movie (or fragment), call mvSetEndFrame(). Its function prototype is:
void mvSetEndFrame ( MVid movieid, MVframe endframe ) |
where:
| endframe | is the frame number where playback ends |
To get the frame number where playback begins, call mvGetStartFrame(). Its function prototype is:
MVframe mvGetStartFrame ( MVid movieid ) |
To get the frame number where playback ends, call mvGetEndFrame(). Its function prototype is:
MVframe mvGetEndFrame ( MVid movieid ) |
This section explains how to use the Movie Library to respond to an interface such as a scroll bar that lets the user jump to a random location in a movie while it is playing.
The Movie Library has two routines for jumping to a particular frame in a movie: mvSetCurrentFrame() and mvScrubCurrentFrame(). Each routine is designed with a specific use in mind. mvScrubCurrentFrame() provides the quickest response by rapidly locating the nearest frame (keyframe for keyframed formats) to the selected frame; mvSetCurrentFrame() provides the most accurate response, by precisely locating and then showing the frame exactly as selected.
Their function prototypes are:
void mvScrubCurrentFrame( MVid movieid, MVframe newframe ) void mvSetCurrentFrame( MVid movieid, MVframe newframe ) |
where:
| newframe | is the frame to which you want to jump |
One way to use these routines is to call mvScrubCurrentFrame() to make coarse jumps while the user is dragging the scroll bar, then call mvSetCurrentFrame() to go to and display the exact frame when the user releases the mouse button. The triangular pointer in the Movie Player uses this technique.
To retrieve the location of the current frame, call mvGetCurrentFrame(). Its function prototype is:
MVframe mvGetCurrentFrame ( MVid movieid ) |
Movies play in real time, slaved to a time base that is expressed in frames per second (FPS). The time base is either synchronized to the audio track or to a software-based timer that you set with mvSetImageRate().
You can specify a multiplier for modifying a movie's natural frame rate. The natural frame rate is stored in the movie file and can be retrieved with mvGetImageRate(). The default playback speed is 1.0 times the natural frame rate.
To alter the normal playing speed, call mvSetPlaySpeed(), which expresses the new playback speed as a multiple of the movie's natural image frame rate. Its function prototype is:
void mvSetPlaySpeed ( MVid movieid, double newplayspeed ) |
where:
| newplayspeed | is an integer (for speeding up playback) or the reciprocal of an integer (for slowing down playback) |
For example, if you specify a newplayspeed of 2.0, the movie plays twice as fast as it normally would. If you specify a negative value for newplayspeed, the movie plays in reverse.
You can measure the resulting frame rate by calling mvGetActualFrameRate(), as described in “Measuring the Current Frame Rate”.
For movies with audio, the Movie Library attempts to resample the audio to match the requested playback speed. This changes the pitch of the audio. The current implementation uses software techniques to perform audio resampling, so you should be aware that changing the speed of playback for a movie with an audio soundtrack consumes more processor time.
mvSetPlaySpeed() has no effect if the movie has been set to play every frame with mvSetPlayEveryFrame().
To determine the current setting for playback speed, call mvGetPlaySpeed(). Its function prototype is:
double mvGetPlaySpeed ( MVid movieid ) |
You can obtain the actual playback frame rate for a movie as it is playing; the Movie Library computes the frame rate over the last second of playback. The current frame rate is the movie's natural frame rate, in frames per second (FPS) modified by the speed factor set by mvSetPlaySpeed().
To determine the current frame rate, call mvGetActualFrameRate(). Its function prototype is:
DMstatus mvGetActualFrameRate ( MVid movieid,
double* ratereturn )
|
where:
| ratereturn | is a pointer into which the actual frame rate is returned |
If your application needs more detailed information about the rate of movie playback, you should check the timestamp information associated with movie frame events as described in “Handling Events”.
If the playback rate lags behind the time base, the Movie Library will drop (not display) image frames to stay in sync.
You can set a threshold value for the slowest acceptable playback speed and have the Movie Library notify you through the movie event queue if the playback speed falls below this minimum accepted speed by requesting MV_SLOW_PLAY events using mvSetSelectEvents().
The threshold value must be a number between 0 and 1, which is a fraction of the movie's current speed. The current speed of a movie is its natural frame rate, as specified in the movie file, multiplied by the speed factor set by mvSetPlaySpeed(). The threshold setting is global—it applies to all movies in your application. The default threshold is .5, meaning 50% of the current speed.
To set a minimum playback rate threshold, call mvSetSlowThreshold(). Its function prototype is:
void mvSetSlowThreshold ( double slowthresh ) |
where:
| slowthresh | is the threshold value for minimum playback speed |
To retrieve the current minimum playback rate threshold, call mvGetSlowThreshold(). Its function prototype is:
double mvGetSlowThreshold ( void ) |
See “Checking and Correcting for Slow Playback” for an explanation of how to check for slow playback events and correct for them.
Some applications need to display every frame in a movie; for example, visualization applications where the user is looking for data patterns or trends must show every frame to ensure accuracy. The audio track is ignored (the movie plays silently) when you play every frame. In addition, the slow playback threshold is ignored when you play every frame, and you won't receive MV_SLOW_PLAYBACK events either.
To force the Movie Library to show every frame in sequence as fast as it can, call mvSetPlayEveryFrame(), passing in a value of DM_TRUE. Its function prototype is:
void mvSetPlayEveryFrame( MVid movieid, DMboolean sync ) |
where:
| sync | determines whether the Movie Library should play every frame or drop frames to stay in pace with the movie's time base: DM_TRUE plays every frame; DM_FALSE (default) drops frames when it becomes necessary in order to maintain a desired frame rate |
To find out if playback is set to play every frame, call mvGetPlayEveryFrame(). Its function prototype is:
DMboolean mvGetPlayEveryFrame( MVid movieid ) |
You can use the Movie Library with the IRIS Graphics Library to combine the display of movies and graphics. You can draw IRIS GL graphics in the same window where a movie is playing, or you can have a movie playing in one window while drawing graphics in another window on the same screen.
The Movie Library has two different methods for combining movies and graphics—which method you use depends on whether the graphics overlap any portion of the movie frame. In one method, the display of movie frames is automatically controlled for you, in the other method, you control the display of movie frames manually from within your application. Frame display is a global setting that applies to all currently open movies.
If the graphics you want to draw using the IRIS GL do not overlap any portion of the movie display, for example, if your application displays movies and graphics side-by-side in one window or in separate windows, you can use the mvGrabIrisGL() and mvReleaseIrisGL() routines. These routines essentially put movie rendering on hold while the application performs IRIS GL rendering. Their function prototypes are:
void mvGrabIrisGL ( void ) void mvReleaseIrisGL ( void ) |
Use mvGrabIrisGL() and mvReleaseIrisGL() to surround the block of code that performs the IRIS GL drawing. Call mvGrabIrisGL() at the beginning of a block of IRIS GL calls, then call mvReleaseIrisGL() when the IRIS GL drawing is completed.
Example 30-6 is an excerpt from moviescreenGl.c , in /usr/people/4Dgifts/examples/dmedia/movie/moviescreen, that shows how to use mvGrabIrisGL() and mvReleaseIrisGL(). It uses the IRIS GL bgnpolygon() and endpolygon() routines to draw a box that covers up the distracting visual effects caused by moving the movie around the screen. See “Creating a Movie Screensaver Application” in Chapter 32 for more details about the moviescreen program.
Example 30-6. Using mvGrabIrisGL() and mvReleaseIrisGL()
void undrawSaverPicture()
{
int width;
int height;
MVid imageTrack;
MVid theMovie = getMovieID();
if ( isFullScreen() )
return;
/*
* Determine current size of movie on display
*/
mvFindTrackByMedium( theMovie, DM_IMAGE, &imageTrack );
width = mvGetImageWidth( imageTrack );
height = mvGetImageHeight( imageTrack );
width *= getZoom();
height *= getZoom();
/*
* Draw two black boxes to erase trailing garbage as
* movie dances around the display.
*/
mvGrabIrisGL();
bgnpolygon();
{
short vctr[2];
if ( dx > 0 ) {
vctr[0] = offsetx - 1;
vctr[1] = offsety - 1;
v2s( vctr );
vctr[0] = offsetx +dx;
vctr[1] = offsety - 1;
v2s( vctr );
vctr[0] = offsetx + dx;
vctr[1] = offsety + height + 1;
v2s( vctr );
vctr[0] = offsetx - 1;
vctr[1] = offsety + height + 1;
v2s( vctr );
} else {
vctr[0] = offsetx + width;
vctr[1] = offsety - 1;
v2s( vctr );
vctr[0] = offsetx + width + dx + 1;
vctr[1] = offsety - 1;
v2s( vctr );
vctr[0] = offsetx + width + dx + 1;
vctr[1] = offsety + height + 1;
v2s( vctr );
vctr[0] = offsetx + width;
vctr[1] = offsety + height + 1;
v2s( vctr );
}
if ( dy > 0 ) {
vctr[0] = offsetx - 1;
vctr[1] = offsety - 1;
v2s( vctr );
vctr[0] = offsetx + width + 1;
vctr[1] = offsety - 1;
v2s( vctr );
vctr[0] = offsetx + width + 1;
vctr[1] = offsety + dy;
v2s( vctr );
vctr[0] = offsetx - 1;
vctr[1] = offsety + dy;
v2s( vctr );
} else {
vctr[0] = offsetx - 1;
vctr[1] = offsety + height;
v2s( vctr );
vctr[0] = offsetx + width + 1;
vctr[1] = offsety + height;
v2s( vctr );
vctr[0] = offsetx + width + 1;
vctr[1] = offsety + height + dy + 1;
v2s( vctr );
vctr[0] = offsetx - 1;
vctr[1] = offsety + height + dy + 1;
v2s( vctr );
}
}
endpolygon();
mvReleaseIrisGL();
}
|
The disadvantage of the automatic mvGrabIrisGL() – mvReleaseIrisGL() method is that you can't really synchronize the graphics and movie displays, so some things might not display exactly when you want them to.
This section describes a method that lets you disable the Movie Library's automatic display so that your application can control the display of movie frames. When you have explicit control over the display of movie frames, you can tell the Movie Library exactly when to show a frame. This technique lets you draw IRIS GL graphics on top of the movie frame.
To have your application (rather than the Movie Library) control the display of movie frames, call mvSetFrameDisplay(DM_FALSE). Its function prototype is:
void mvSetFrameDisplay ( DMboolean showframes ) |
where:
| showframes | is either DM_TRUE, to allow automatic frame display, or DM_FALSE, to disable automatic frame display for all open movies |
To retrieve the current frame display setting, call mvGetFrameDisplay(). Its function prototype is:
DMboolean mvGetFrameDisplay ( void ) |
When you disable the frame display, you have to tell the Movie Library when to display a frame by calling mvShowCurrentFrame() for every frame that you want to display. Its function prototype is:
void mvShowCurrentFrame ( MVid movieid ) |
To play a movie with manual frame display, you should include MV_EVENT_FRAME events in your event mask and call mvShowCurrentFrame() in response to those events.
Another way to use mvShowCurrentFrame() is to repaint a movie frame in response to an expose event from the window manager. See “Handling X Window Events” for more details.
You must use one of the two methods described in this section if you want to draw with the IRIS GL while using the Movie Library. The IRIS GL and the Movie Library use separate threads of execution, and the routines discussed in this section supply internal synchronization between the processes. The results of any other method of combining graphics and movies are unpredictable. See Appendix E, “Using Graphics and Share Groups,” in the Graphics Library Programming Guide for more information.
![]() | Tip: Advanced programmers might want to turn off the frame display in order to use a double-buffered RGB window. In this case, you have to create a window with the graphics configuration that you want, and you must call swapbuffers() from the GL and mvShowCurrentFrame() from the Movie Library yourself for every frame. This method is only for special cases; it is not the recommended way to combine graphics and movies. |
Example 30-7 is an excerpt from moviescreenEvents.c , in /usr/people/4Dgifts/examples/dmedia/movie/moviescreen, that uses mvSetFrameDisplay() to temporarily disable movie display while initialization routines are carried out.
Example 30-7. Initializing Movie Playback
static void initSaverMovie( )
{
MVid newMovie;
char *movieName;
movieName = pickMovieAtRandom( );
if ( mvOpenFile( movieName, O_RDONLY, &newMovie ) ==
DM_FAILURE ) {
printf("%s: Could not open movie %s\n",
getProgramName(),movieName );
exit( EXIT_FAILURE );
}
setMovieID( newMovie );
/*
* Disable movie display temporarily,
* to avoid visual glitches.
*/
mvSetFrameDisplay( DM_FALSE );
setupMovieWindow( newMovie );
setPlaybackLoopmode( newMovie );
setVolumeLevel( newMovie );
/*
* play the movie
*/
mvSetCurrentFrame( newMovie, 0 );
mvSetFrameDisplay( DM_TRUE );
mvPlay( newMovie );
}
|
If you've used the select() or poll() system calls, or X event handling, the Movie Library event handling routines will be familiar to you. Even if your application has no user interface, it is recommended that you implement movie event handling because it is the best way to find out about system or I/O errors that occur while a movie is playing.
The Movie Library has an event queue, similar to the X event queue, that stores movie events such as playing a frame, stopping playback, and errors. One queue stores movie events for all currently open movies by storing the identifier of the movie instance responsible for an event as part of the event.
The Movie Library event queue is separate from the X event queue; so if your application handles X events as well as movie events, you'll probably want to create two separate functions, one for handling X events and one for handling movie events, and call them both from main().
To add movie event handling to your application, follow these basic steps:
Choose which movie events you want to process by creating an event mask, as described in “Preparing an Event Mask”.
Get the file descriptor associated with the movie event queue, as described in “Getting a File Descriptor for the Movie Event Queue”.
Use one of the following methods to listen for movie and X11 events.
If you are using Xlib, prepare a file descriptor set that contains both the Movie Library and X11 file descriptors using the FD_SET macro, and then code a select() loop that listens for movie events and X events, as described in “Preparing a File Descriptor Set”.
If you are using Xt or IRIS IM, pass the Movie Library file descriptor to the X toolkit using XtAppAddInput(3Xt). This call associates an Xt callback function that you have written for your application with the Movie Library file descriptor. You can then process movie events inside this Xt callback, which gets called whenever one or more events appear on the Movie Library event queue.
As long as there are events on the queue, keep processing them.
These steps are described in detail in the sections that follow.
An event mask specifies which events the application is interested in processing. The event mask is a bitwise or of the events.
Table 30-1 lists and describes the Movie Library events.
Table 30-1. Movie Library Events
Event | Description |
|---|---|
MV_EVENT_FRAME | A frame has been played. |
MV_EVENT_STOP | The movie has stopped playing. |
MV_EVENT_ERROR | An error has occurred during playback. |
MV_EVENT_SLOW_PLAY | The last second of playback measured by the Movie Library was slower than the threshold set in mvSetSlowThreshold(). |
To set an event mask, call mvSetSelectEvents(). Its function prototype is:
void mvSetSelectEvents ( MVeventmask eventmask ) |
where:
| eventmask | is a bitwise OR of one or more of the events in Table 30-1 |
For example, to receive all events, use the following:
mvSetSelectEvents( MV_EVENT_MASK_FRAME | MV_EVENT_MASK_STOP |
MV_EVENT_MASK_ERROR | MV_EVENT_MASK_SLOW_PLAY )
|
To retrieve the current setting of the event mask, call mvGetSelectEvents(). Its function prototype is:
MVeventmask mvGetSelectEvents ( void ) |
Movie events are contained in an MVevent structure, which is a union of all of the Movie Library event structures. Each event structure contains fields that store information about the event. Some events have additional fields containing event-specific information.
Table 30-2 lists and describes the fields in the movie events.
Table 30-2. Event Structure Fields
Type | Field | Description |
|---|---|---|
MVeventtyp e | type | Type of event. |
MVtime | time | X11-style millisecond timestamp indicating when the event happened. |
MVid | id | Movie instance that produced the event. |
MVframe | frame | Current frame number, ranging from zero to one less frame than the length of the movie's image track. |
int | errcod e | Integer value that represents a Movie Library error code that can be retrieved by calling mvGetErrno(). Applies to MV_EVENT_ERROR events only. |
int | reason | Integer value that indicates what caused the slow play event. Applies to MV_SLOW_PLAYBACK events only and is currently unused. |
See “Handling Movie Events” for an explanation of how to handle movie events, and see Example 30-9 for a code fragment that branches depending on the event type and then extracts information from the event fields.
The Movie Library provides a file descriptor that becomes active when you can read events from the movie event queue. To get a file descriptor for the movie event queue, call mvGetEventFD(). Its function prototype is:
DMstatus mvGetEventFD ( int *fdreturn ) |
where:
| fdreturn | is a pointer into which the Movie Library event file descriptor is returned |
After you have set up the event mask and obtained a file descriptor for the movie event queue, you can create the event loop. You'll probably want to define separate window and movie event handling functions and call them from main().
Use one of the following methods to listen for movie and X11 events.
If you are using Xlib, prepare a file descriptor set that contains both the Movie Library and X11 file descriptors using the FD_SET macro, and then code a select() loop that listens for movie events and X events, as described in “Preparing a File Descriptor Set”.
If you are using Xt or IRIS IM, pass the Movie Library file descriptor to the X toolkit using XtAppAddInput(3Xt). This call associates an Xt callback function that you have written for your application with the Movie Library file descriptor. You can then process movie events inside this Xt callback, which gets called whenever one or more events appear on the Movie Library event queue.
If you are using Xt or IRIS IM, you'll use the Movie Library routines for handling movie events that are described in this section, but you'll get the events from the X11 queue, as described in “Handling X Window Events”, so skip ahead to “Waiting for Movie Events” in this section to learn how to handle movie events.
If you are using Xlib, you need to create a loop that uses select() or poll() to wait for Movie Library events using a file descriptor set (fd_set). The basic structure of this loop is:
/* wait for libmovie events */
select(...);
/* process all libmovie events on queue before waiting again */
while ( mvPendingEvents() != 0 ) {
mvNextEvent(&event);
switch( event ) {
/* ... */
}
}
|
File descriptor sets provide a way for an application to wait for available input from several files at once. The Movie Library event queue is separate from the X11 event queue, so you must obtain the file descriptor for each queue and put them both into a file descriptor set.
To get the file descriptor corresponding to the X11 event queue for a specified display, use the Xlib ConnectionNumber(3X) macro.
After you have obtained file descriptors for the movie event queue and the X11 event queue, put them into a file descriptor set using the FD_SET macro.
Example 30-8 shows how to prepare a file descriptor set.
Example 30-8. Preparing a File Descriptor Set
static DMstatus setupFDSet( Display *dpy, int *movieFD,
int *xFD, fd_set *theFDSet )
{
if ( mvGetEventFD( movieFD ) != DM_SUCCESS ) {
fprintf( stderr, "%s: Could not get movie event FD.\n",
programName );
return DM_FAILURE;
}
*xFD = ConnectionNumber( dpy );
FD_ZERO( theFDSet );
FD_SET( *movieFD, theFDSet );
FD_SET( *xFD, theFDSet );
return DM_SUCCESS;
}
|
The structure of this code outline and the reasoning behind it are explained in the sections that follow.
When creating a movie event loop, it is important that you process all the events on the queue to prevent it from stalling.
The Movie Library provides the mvPendingEvents() call to check the queue for events, with which you can create a while loop. Its function prototype is:
int mvPendingEvents ( void ) |
You must use the while statement to set up the loop properly; don't use an if statement.
When your application wakes up to handle movie events, there may be more than one event in the queue. It is critical that you process all the events in the queue before waiting for more events. As long as events are pending, your application should keep getting events from the queue until it is empty.
To extract the next event from the queue, call mvNextEvent(). Its function prototype is:
void mvNextEvent ( MVevent* eventreturn ) |
where:
| eventreturn | is a pointer into which the next event is returned |
You can determine which event is waiting next in the queue, without popping it off the queue, by “peeking” at the next event. To peek at the next event, call mvPeekEvent(). Its function prototype is:
void mvPeekEvent ( MVevent* eventreturn ) |
where:
| eventreturn | is a pointer into which the next event is returned without being removed from the queue |
You can query the fields in the event structure to get specific information about an event, such as the event type. After you know what type of event it is, you can use the structure definitions for the specific event types to extract further information, such as when or why the event occurred.
The first field of every event is a type field, which lets you query for the event type without knowing it in advance. After determining the event type, you can examine the fields for each type of event to extract specific information about the event. The typical way to code this is with a switch statement.
Example 30-9 is an excerpt from manymovieEvents.c , in /usr/people/4Dgifts/examples/dmedia/movie/manymovie, that uses a movie event loop in which a switch statement is used to examine the event type, then extracts event-specific information such as the frame number that caused the event (event.mvframe.id), from the fields in the event structure. (The print statement under MV_EVENT_FRAME is commented out in the sample program.)
Example 30-9. Handling Movie Frame, Stop, and Error Events
static DMboolean handleMovieEvents( )
{
MVevent event;
while ( mvPendingEvents() != 0 ) {
mvNextEvent( &event );
switch ( event.type ) {
case MV_EVENT_FRAME: /* a frame played */
/* Uncomment and recompile to see which frames are played.
*
*
* printf( "%s: Played frame %d of movie %d.\n",
* getProgramName(), event.mvframe.frame,
* event.mvframe.id );
*/
break;
case MV_EVENT_STOP: /* end of movie */
printf( "%s: Playback of movie %d stopped.\n",
getProgramName(), event.mvstop.id );
break;
case MV_EVENT_ERROR: /* error */
fprintf( stderr, "%s: Error during playback: %s.\n",
getProgramName(), mvGetErrorStr( mvGetErrno() ) );
return DM_FALSE;
break;
}
}
return DM_TRUE;
}
|
The method for handling the X events depends on whether you use Xlib or an X toolkit:
If you are using Xt or IRIS IM, you need to write Xt callback functions for the GLXMDraw widget that get called when it is resized and when it needs repainting. The X toolkit invokes the Xt callback procedures in your application whenever one or more events appear on the X11 event queue. Call XtAppAddInput(3Xt) to add the Movie Library file descriptor to the X11 file descriptors that invoke your Xt callback function.
If you are using Xlib, you need to get the file descriptor corresponding to the X11 event queue for a specified display, by calling the Xlib ConnectionNumber(3X) macro. Put this file descriptor into a file descriptor set along with the Movie Library file descriptor and wait on both of them using select(), as described in “Handling Movie Events”.
The two window events of greatest concern to Movie Library programmers are expose and resize events.
If a window containing a movie gets exposed, you must repaint the window; if it gets resized, you must resize and repaint the window.
To resize the window identified by win on the display dpy, call mvResizeWindow(). Its function prototype is:
void mvResizeWindow ( Display* dpy, Window win ) |
To repaint a window, call mvShowCurrentFrame(). Its function prototype is:
void mvShowCurrentFrame ( MVid movieid ) |
Example 30-10 is an excerpt from simplemovie.c , in /usr/people/4Dgifts/examples/dmedia/movie/misc, that shows how to handle window events from the X11 queue, in particular, expose and resize events. See “Creating a Simple Keyboard Interface for Playing Movies” in Chapter 32 for more information about simplemovie.c.
Example 30-10. Handling X11 Expose and Resize Window Events
static DMboolean handleXEvents( MVid theMovie, Display *dpy, Window win )
{
XEvent event;
while ( XPending( dpy ) != 0 ) {
XNextEvent( dpy, &event );
switch ( event.type ) {
case Expose: /* repaint display */
mvShowCurrentFrame( theMovie );
break;
case ConfigureNotify: /* window was resized */
{
XWindowAttributes winAttrs;
int actual_width, actual_height;
mvResizeWindow( dpy, win );
XGetWindowAttributes( dpy, win, &winAttrs );
mvQueryViewSize( theMovie, winAttrs.width,
winAttrs.height, DM_TRUE,
&actual_width, &actual_height );
printf( "%s: actual width = %d, height = %d\n",
programName, actual_width, actual_height );
mvSetViewSize( theMovie, winAttrs.width,
winAttrs.height, DM_TRUE );
}
break;
…
}
}
return DM_TRUE;
}
|
The Movie Library checks the playback speed once per second and sends a slow play event to the movie event queue if it finds that the last second of playback was too slow.
To check for slow playback, you must:
set a minimum playback speed threshold
set an event mask to request MV_SLOW_PLAYBACK events
To check playback speed more frequently, you can choose to receive frame events and compute the difference between the two timestamps, which is the real time in milliseconds that has elapsed between the two events.
Playback may be slow because:
the I/O bandwidth from a hardware device is not high enough
the filesystem is fragmented
the system CPU cannot decompress data fast enough
the movie is unoptimized (see mvOptimize(3mv))