This chapter describes how to set up a Movie Library application and how to use the Movie Library routines for handling file input/output (I/O), editing, compression, and other basic tasks. Playback is discussed in the next chapter.
The Movie Library provides these basic file I/O capabilities:
creating new movie files
opening an existing movie from a file descriptor, filename, or memory
reading data from and writing data to selected tracks and frames
This section explains the basic program setup you will use. To set up a Movie Library application, you need to:
open a file descriptor for the movie (if you are using a file descriptor)
create a new movie or open an existing movie
test a file to determine whether it is a movie file
set and get movie properties
add, remove, and find tracks
allocate buffers for data passed to and from the Movie Library
Each of these tasks is described in detail in the subsections that follow. It's a good idea to familiarize yourself with the setup procedures described in this section because you'll be performing most of these tasks in every application that works with movies.
Basic routines for creating, verifying, and opening movies are available for each type of interface: filename, file descriptor, and memory-mapped files. Each of these routines features a similar API but has the appropriate arguments for the specified method.
Figure 29-1 shows a diagram of the file I/O routines in the Movie Library.
mvCreateFile(), mvCreateFD(), and mvCreateMem() create a new empty movie, initialized with the given parameters, as set by mvSetMovieDefaults() or by the DM Library routines, and return an identifier for the new movie. Any movie that was already present in the file or memory location will be destroyed.
mvIsMovieFile(), mvIsMovieFD(), and mvIsMovieMem() test whether a movie is present in a file or in memory. Only movies in supported formats (Silicon Graphics and QuickTime) are recognized.
mvOpenFile(), mvOpenFD(), and mvOpenMem() read an existing movie, create a movie instance in memory that holds information about it, and then return an identifier for the new movie.
The following two calls are used when the file I/O or editing is completed:
| mvWrite() | flushes all changes that have been made to a movie and makes sure that they are written to the file, but does not close the file | |
| mvClose() | flushes all changes that have been made to the movie and makes sure that they are written to the file, and then destroys the movie instance |
Each of these calls is described in detail in the sections that follow.
File descriptors can be used to work with movie files on disk, CD-ROM, or DAT, or with embedded or previously opened movie files.
![]() | Note: The Movie Library does not support embedded QuickTime files. |
Use the IRIX open() system call (see the open(2) man page) to open a file descriptor.
The movie instance inherits one of three file access modes, which are associated with the file descriptor:
| O_RDONLY | ||
| O_WRONLY | ||
| O_RDWR |
The file access mode determines which operations are possible for a particular movie. You can't write data to a movie opened as a read-only file. Similarly, you can't play a movie that has been opened for writing.
Before opening the movie, the file pointer associated with the file descriptor should be positioned at the beginning of the movie file. If the movie data is embedded within a file, such as an application-specific file format containing a movie as a data chunk, use the IRIX lseek() system call (see the lseek(2) man page) to seek the file descriptor to the beginning of the movie data. For example, a movie file might be embedded in the file of a word-processing program. When the word processing program is ready to access the movie, it seeks to the position of the movie within its file and passes the file descriptor (fd) to the Movie Library.
You can create a new movie and associate it with a file descriptor, file name, or memory location. There is a function call corresponding to each type.
mvCreateFD() | creates a movie using a file descriptor returned by open()
|
mvCreateFile() | creates a movie using a file name |
mvCreateMem() | creates a memory-mapped movie |
The function prototypes are:
DMstatus mvCreateFD ( int fd,
DMparams* params,
DMparams* returnParamsSetOrNull,
MVid* returnMovie )
DMstatus mvCreateFile ( const char* filename,
DMparams* params,
DMparams* returnParamsSetOrNull,
MVid* returnMovie )
DMstatus mvCreateMem ( void* pointer,
size_t size,
DMparams* params,
DMparams* returnParamsSetOrNull,
MVid* returnMovie )
|
Example 29-1 is a code fragment that shows how to create a movie.
Example 29-1. Creating a Movie
#include <movie.h>
void CreateMovie()
{
DMparams* params;
MVid movie;
if ( dmParamsCreate( ¶ms ) != DM_SUCCESS ) {
/* handle error */
}
if ( mvSetMovieDefaults( params, MV_FORMAT_SGI_3 )
!= DM_SUCCESS ) {
/* handle error */
}
if ( mvCreateFile( "new-movie", params, NULL, &movie )
!= DM_SUCCESS ) {
/* handle error */
}
/* Add tracks, insert frames, etc. */
}
|
Before attempting to open an existing movie file from a file descriptor, file name, or memory, your application should check to see whether a movie is present. There is a separate function call to verify the existence of a movie for each source type.
mvIsMovieFD() | |
mvIsMovieFile() | |
mvIsMovieMem() |
Their function prototypes are:
DMboolean mvIsMovieFD ( int fd ) DMboolean mvIsMovieFile ( const char* filename ) DMboolean mvIsMovieMem ( void* pointer, size_t size ) |
where:
| fd | is the file descriptor of the movie file you are checking | |
| filename | is the name of the movie file you are checking | |
| pointer | is a pointer to a memory location where the movie file you are checking is stored | |
| size | is the size of the block of memory to use—the Movie Library will not read or write beyond this block size |
DM_TRUE is returned if the given file is a movie file; otherwise, DM_FALSE is returned.
The following code fragment determines whether a given file is a movie file and prints an error message if it is not:
if ( !mvIsMovieFile( filename ) ) {
PrintError ("Not a movie file.");
}
|
Movies can be opened from file descriptors, filenames, or memory. There is a function call corresponding to each source type:
mvOpenFD() | opens a movie using a file descriptor that has already been obtained from another source, such as open(2) |
mvOpenFile() | |
mvOpenMem() |
To open a movie file from a file descriptor, call mvOpenFD(). Its function prototype is:
DMstatus mvOpenFD (int fd, MVid* returnMovie) |
| fd | is a file descriptor that references the movie file you want to open | |
| returnMovie | is a pointer into which the movie identifier is returned |
To open a movie from a filename, call mvOpenFile(). Its function prototype is:
DMstatus mvOpenFile ( const char* filename, int oflag,
MVid* returnMovie )
|
| filename | is the filename of the movie file you want to open | |
| oflag | is the file access mode, either O_RDONLY or O_RDWR | |
| returnMovie | is a pointer into which the movie identifier is returned |
To open a movie that resides entirely in memory, beginning at the location pointed to by pointer, call mvOpenMem(). Its function prototype is:
DMstatus mvOpenMem ( void* pointer, size_t size, MVid*
returnMovie )
|
| pointer | is a pointer to the starting memory location of a movie | |
| size | is the size of the memory buffer—the Movie Library will not read or write beyond this block size | |
| returnMovie | is a pointer into which the movie identifier is returned |
Your application must allocate and free the memory buffer used by mvOpenMem().
The operations that can be performed on a track include:
adding a new track
removing an existing track
finding a track
mapping frames between tracks in the same movie or another movie
reading and writing data in a track
editing operations (copying from one movie to another)
To add a track to a movie, call mvAddTrack(). Its function prototype is:
DMstatus mvAddTrack ( MVid movie, DMmedium medium, DMparams* params,
DMparams* returnParamsSetOrNull, MVid* returnTrack )
|
| medium | is the type of track, either DM_AUDIO or DM_IMAGE | |
| params | is a pointer to a parameter-value list for configuring the new track, which should be filled in using either dmSetImageDefaults() or dmSetAudioDefaults(), depending on the medium type | |
| returnParamsSetOrNull |
| |
| returnTrack | is a pointer into which the ID of the newly added track is returned |
DM_SUCCESS is returned if the new track was created successfully; otherwise DM_FAILURE is returned.
The DM_IMAGE_COMPRESSION parameter that is given when creating an image track is important to get right, because different settings can result in large differences in image quality and in playback performance. See “Getting the Image Compression Scheme” in Chapter 28 for information on choosing a compression setting when configuring an image track.
Example 29-2 shows how to add an audio track to a movie.
Example 29-2. Adding an Audio Track to a Movie
#include <movie.h>
void AddAudioTrack( MVid movie )
{
DMparams* audioParams;
MVid audioTrack;
if ( dmParamsCreate( &audioParams ) != DM_SUCCESS ) {
/* handle error */
}
if ( dmSetAudioDefaults( audioParams,
8, /* bits per sample */
22050, /* sample rate */
1, /* number of channels */
) != DM_SUCCESS ) {
/* handle error */
}
if ( mvAddTrack( movie,
DM_AUDIO,
audioParams,
NULL,
&audioTrack ) != DM_SUCCESS ) {
/* handle error */
}
/* Write audio data to track */
/* with mvInsertFrames(3mv) */
}
|
To remove a track from a movie, which deletes all the data from the track, call mvRemoveTrack(). Its function prototype is:
DMstatus mvRemoveTrack(MVid movie, MVid track) |
| track | is the track you want to remove |
DM_SUCCESS is returned if the track was removed successfully; otherwise, DM_FAILURE is returned.
To get a handle for a track that already exists, call mvFindTrackByMedium(). Its function prototype is:
DMstatus mvFindTrackByMedium ( MVid movie, DMmedium medium,
MVid* returnTrack )
|
| medium | is the medium, either DM_AUDIO or DM_IMAGE | |
| returnTrack | is a pointer into which the track identifier is returned |
mvFindTrackByMedium() returns DM_SUCCESS if a track of the given medium exists in the movie instance identified by movie; otherwise, it returns DM_FAILURE.
Because the image and audio tracks are separate, your application must manage the synchronization between tracks when editing movies; for example, when deleting a portion of the image track, the corresponding portion of the audio track must be located and also deleted.
Figure 29-2 shows an image track and a corresponding audio track. (The audio track would actually have many more samples per frame, but for clarity, only a few samples are shown.)
As shown in Figure 29-2, the frame numbers in one track do not have a one-to-one time correspondence with the frame numbers in another track. When mapping frame numbers from one track to another, the Movie Library chooses the frame that matches the starting time of the given frame. The frame boundaries are not necessarily aligned, so the mapping can differ by one frame, depending on which track you are mapping from. For example, in Figure 29-2, frame 3 in the image track maps to frame 9 of the audio track, but frame 9 of the audio track maps to frame 2 of the image track.
Before performing any operation on a frame in one track that affects its corresponding frame in the second track, you must locate the frame number in the second track that corresponds to the frame number in the first track.
To locate the frames in one track that correspond (in time) to frames from another track, call mvMapBetweenTracks(). mvMapBetweenTracks() determines which frame number in toTrack corresponds to the frame numbered fromFrameIndex in fromTrack and writes the result into the location pointed to by toFrameIndex. Its function prototype is:
DMstatus mvMapBetweenTracks ( MVid fromTrack, MVid toTrack,
MVframe fromFrameIndex,
MVframe* toFrameIndex )
|
where:
| fromTrack | is the track for which you want to locate a corresponding frame in toTrack | |
| toTrack | is the track in which to locate the frame number corresponding to the frame numbered fromFrameIndex in fromTrack | |
| fromFrameIndex |
| |
| toFrameIndex | is a pointer into which the frame number in toTrack that corresponds to the frame numbered fromFrameIndex in fromTrack is written |
DM_SUCCESS is returned if a corresponding frame was located; otherwise, DM_FAILURE is returned.
![]() | Tip: You can also use mvMapBetweenTracks() to find corresponding frame numbers in tracks from two different movies. |
The Movie Library provides these editing operations:
optimizing a movie
inserting raw image or audio frames from a buffer into a track
reading frames from a track into a buffer
deleting frames from a track
reading and inserting compressed images directly
copying frames from one movie to another
![]() | Note: Movies should not be edited during playback. |
When you edit a movie, the Movie Library changes pointers to frames rather than operating on the actual frames themselves. After a series of editing calls, the movie frames might not be arranged in the order in which they play, and there are probably empty spaces in the movie. Such a movie does not provide optimum playback, but you can optimize the movie as described in “Optimizing a Movie File” before closing it.
To get the best playback performance from an edited movie, you should optimize the movie file after an editing session. Optimization streamlines the movie file by coalescing the empty space and by flattening the data structure into the most linear structure possible. This is especially helpful for minimizing the excessive seeks that occur during playback that are caused by editing a movie file repeatedly. Optimization does not occur in place; instead, the Movie Library makes a copy of the movie to optimize.
To optimize a movie, call mvOptimize(). Its function prototype is:
DMstatus mvOptimize ( MVid fromMovie, MVid toMovie ) |
| fromMovie | is the movie you want to optimize | |
| toMovie | is a name for the optimized movie |
This section explains how to use a buffer for editing. The routines described in this section work on uncompressed data; there is a similar group of routines described in “Reading and Inserting Compressed Images” for working with compressed data.
Memory must be allocated to hold audio or image data that is passed to or obtained from the Movie Library. The buffer that is passed to these routines points to a block of memory that holds an array of frames. Your application is responsible for allocating a buffer large enough to hold the desired number of frames.
![]() | Note: A playback-only application that does not perform any file I/O operations need not allocate separate memory. |
Use dmImageFrameSize() to determine the number of bytes needed to hold one frame of raw image data; similarly, use dmAudioFrameSize() to determine the number of bytes needed to hold one frame of raw audio data.
Before allocating memory, determine the required buffer size as demonstrated in Example 29-3. Allocate the appropriate amount of memory by using one of the IRIX system calls for memory allocation, such as malloc(), and then check the malloc() return to make sure there was enough memory. See the malloc(3X, 3C) man page for information about memory allocation. See the IRIX Programming Guide for information about using shared memory.
To determine the buffer size needed to store the uncompressed frames, multiply the number of frames by the frame size, as shown in Example 29-3.
Example 29-3. Determining What Size Buffer to Allocate
static void insertFrames( MVid theEditMovie,
MVid theEditTrack,
MVid theSourceMovie,
MVid theSourceTrack )
{
MVframe editStartFrame;
MVframe sourceStartFrame;
MVframe numFrames = getNumEditFrames();
size_t insBuffSize;
void *insBuff;
if ( getEditTrackType() == DM_IMAGE ) {
insBuffSize = numFrames *
dmImageFrameSize( mvGetParams( theSourceTrack ) );
}
else if ( getEditTrackType() == DM_AUDIO ) {
insBuffSize = numFrames *
dmAudioFrameSize( mvGetParams( theSourceTrack ) );
}
insBuff = malloc( ( int ) insBuffSize );
if ( insBuff == NULL ) {
fprintf( stderr, "%s: Unable to allocate insert buffer.\n",
getProgramName() );
exit( EXIT_FAILURE );
/* insert frames using mvInsertFrames(3mv) */
}
|
You can insert raw image or audio data into an existing movie track—the Movie Library compresses the data as it is inserted into the track. To insert frames from a buffer into a track, call mvInsertFrames(). Its function prototype is:
DMstatus mvInsertFrames ( MVid track, MVframe frameIndex,
MVframe frameCount, size_t bufferSize,
void* buffer )
|
| track | is the track into which you want to insert data | |
| frameIndex | is the frame in front of which you want to insert data | |
| frameCount | is the number of frames to insert | |
| bufferSize | is the size of the buffer | |
| buffer | is a pointer to a buffer that contains the data you want to insert into the track |
When you insert frames into an existing track, the new frames are inserted in front of frameIndex. The existing frames immediately following the insertion point, including frame frameIndex, are shifted to the right to make room for the new frames.
Figure 29-3 shows two frames (N1 and N2) inserted at frameIndex 5 into a movie with 7 frames. Frames 5 and 6 move to make room for the new frames.
To achieve an effect similar to overwriting the existing data, you must first delete the unwanted frames by calling mvDeleteFrames() before inserting the new frames.
To read a specified number of frames from an existing movie into a buffer that you have allocated for storing movie data, call mvReadFrames(). Its function prototype is:
DMstatus mvReadFrames ( MVid track, MVframe frameIndex,
MVframe frameCount,
size_t bufferSize, void* buffer)
|
| track | is the track from which you want to read data | |
| frameIndex | is the first frame in the sequence that is to be read | |
| frameCount | is the number of frames of data to read | |
| bufferSize | is the size of the buffer, obtained by multiplying the number of frames by the value returned from dmAudioFrameSize() for the audio track or by the value returned from dmImageFrameSize() for the image track | |
| buffer | is a pointer to a buffer that you have allocated for storing the data |
![]() | Note: The data is decompressed as it is read into the buffer. Use mvReadCompressedImage(), as described in “Reading and Inserting Compressed Images” to read compressed image frames directly into a buffer. |
To delete frames from a movie track, call mvDeleteFrames():
DMstatus mvDeleteFrames ( MVid track, MVframe frameIndex,
MVframe frameCount )
|
| track | is the track from which you want to delete frames | |
| frameIndex | is the first frame in the sequence that is to be deleted | |
| frameCount | is the number of frames of data to delete |
The Movie Library has a special group of routines for working with compressed images. Performing editing operations on compressed images takes less disk space and less time than editing full resolution images. These routines operate on one frame of compressed data at a time because the size of compressed data can vary from frame to frame. Use these routines if you want to read or write compressed image data frame-by-frame, such as reading a frame at a time from a Cosmo Compress™ board into a movie.
To read a compressed image from an existing movie into a buffer that you have allocated for storing compressed data, call mvReadCompressedImage(). Its function prototype is:
DMstatus mvReadCompressedImage ( MVid track,
MVframe frameIndex,
size_t bufferSize,
void* buffer )
|
| track | is the movie track from which you want to read a compressed image frame | |
| frameIndex | is the frame number of the image you want to read | |
| bufferSize | is the size of the buffer | |
| buffer | is a pointer to a buffer that you have allocated for storing a compressed image frame |
To determine the buffer size (in bytes) needed to hold a compressed image frame, call mvGetCompressedImageSize(). Its function prototype is:
size_t mvGetCompressedImageSize( MVid track,
MVframe frameIndex )
|
| track | is the movie track from which you want to read a compressed image frame | |
| frameIndex | is the frame whose image size you want to know |
mvGetCompressedImageSize() returns the number of bytes that image number frameIndex requires.
Example 29-4 reads a compressed image from track into buffer.
Example 29-4. Reading a Compressed Image from a Movie into a Buffer
void* ReadFirstImage( MVid track )
{
size_t size = mvGetCompressedImageSize( track, 0 );
void* buffer = malloc( size );
if ( buffer = NULL ) { /* handle error */}
if ( mvReadCompressedImage( track,
0,
size,
buffer ) != DM_SUCCESS ) {
/* handle error */
}
return buffer;
}
|
To insert a compressed image from a buffer into an existing image track, call mvInsertCompressedImage(). Its function prototype is:
DMstatus mvInsertCompressedImage ( MVid track,
MVframe frameIndex,
size_t bufferSize,
void* buffer )
|
| track | is the track into which you want to insert a compressed image | |
| frameIndex | is the frame number in front of which the compressed frame is to be inserted | |
| bufferSize | is the size of the buffer | |
| buffer | is a pointer to buffer that contains the compressed image you want to write to the track |
When you insert a compressed frame into an existing track, the new frame is inserted in front of frame frameIndex. The existing frames immediately following the insertion point, including frame frameIndex, are shifted to the right to make room for the new frame (see Figure 29-3 on page 629). To achieve an effect similar to overwriting the existing data, you must first delete the unwanted frames by calling mvDeleteFrames() before inserting the new frame.
The Movie Library has routines that let you copy frames from one movie track to another movie track without using a buffer. The two movies must have the same image frame rate and frame size—if they do not, an error is generated.
These routines also let you work directly with compressed data, without decompressing and recompressing the data. You can copy compressed frames from one movie into another, even if the two movies use different compression schemes.
Figure 29-4 shows image frames being pasted from one movie into another.
![]() | Note: Because a movie's audio and image tracks are independent, the audio samples associated with the original images do not shift with the image frames (see Figure 29-4). Call mvMapBetweenTracks() to locate the audio associated with the displaced frames, then paste the audio frames in the proper location. |
To copy a range of frames from one movie and paste them into another movie without overwriting existing data, call mvPasteFrames(). Its function prototype is:
DMstatus mvPasteFrames( MVid fromTrack,
MVframe fromFrameIndex,
MVframe frameCount,
MVid toTrack,
MVframe toFrameIndex )
|
where:
| fromTrack | is the source movie track from which frames are copied | |
| fromFrameIndex |
| |
| frameCount | is the number of frames to copy and paste | |
| toTrack | is the destination movie track into which the copied frames are inserted | |
| toFrameIndex | is the frame in the destination movie in front of which the new frames will be pasted |
When frames are pasted into a non-empty movie, the new frames are inserted in front of frame frameIndex. The existing frames immediately following the insertion point, including frame frameIndex, are shifted to the right to make room for the new frames.
To overwrite the existing data, first call mvDeleteFrames() to delete the unwanted frames before calling mvPasteFrames().
During an editing session, you can flush the changes to the file and make sure they are written into the file by calling mvWrite(). Data in tracks is always written immediately; mvWrite() flushes the header information. Its function prototype is:
DMstatus mvWrite( MVid movie ) |
mvWrite() returns DM_SUCCESS if it was able to write the file; otherwise, DM_FAILURE is returned.
When you have finished editing a movie, you write and close the file. You may choose to optimize the movie by calling mvOptimize() before closing it.
To close a movie file, call mvClose(), which flushes all changes that have been made to the movie and makes sure that they are written to the file, and then destroys the movie instance. Its function prototype is:
DMstatus mvClose( MVid movie ) |
mvClose() returns DM_SUCCESS if it was able to write and close the file; otherwise, DM_FAILURE is returned.