OpenMPEG for OS/2 Offers New Support Software Audio, Buffered Play and Video CD

From EDM2
Jump to: navigation, search

By Lauren Post

Volume 12 of the IBM Developer Connection delivers an updated release of the OpenMPEG for OS/2 Beta package that provides complete software playback of Motion Picture Expert Group (MPEG) multimedia files through an OS/2 subsystem for the first time. If you are running OS/2 Warp Version 4, you have everything you need to run the OpenMPEG for OS/2 Beta. If you are running OS/2 Warp Version 3, you need to install FixPak 26 before using OpenMPEG for OS/2.

This third release of the package includes the following new functions:

  • Software MPEG Audio support
  • Asynchronous buffer management
  • MPEG-2 Program streams support for MPEG hardware
  • MPEG-2 Transport streams support for MPEG hardware
  • Video CD support for playing multiple tracks
  • Video CD-i support using filename _VIDCDI.MPEG
  • Karaoke CD support
  • OpenMPEG Multiplatform specification changes
  • Software video enhancements
  • Multiple Instance support for simultaneous hardware and software playback
  • Single-stream support for single-stream hardware devices

This article addresses asynchronous buffer management and Video CD and CD-i support. Look for descriptions of other new OpenMPEG functions in future volumes of the IBM Developer Connection.

Software Audio Support

The latest OpenMPEG Beta on Volume 12 contains complete software support for MPEG-1 movies which means anyone can now play MPEG as long as you have a sound card. In the past, special sound cards like MWAVE which could decompress MPEG audio in hardware were required to have audio support, but our latest version supports a software decoder to do this function. A limitation for software MPEG audio is that it will require a Pentium 90 MHz for reasonable frame rates. Anything lower (down to a 486 66MHz machine) will probably still play, but will have low frame rates and possible audio breakup. An interface is provided for changing the quality of the audio to three settings: low, medium and high, through environment variables. The current decoder defaults to a lower quality.

Buffered Play

Asynchronous buffer management makes OpenMPEG unique because, instead of just processing a file, the OpenMPEG engine can accept buffers and process them asynchronously. This capability is especially important for networking because, instead of downloading a movie, the movie can be streamed over a network and passed directly into OpenMPEG and to the hardware. Buffered play also is a helpful feature for anyone using interactive programming, such as game developers. Instead of passing seek commands throughout a game in progress (which can create much overhead), they can perform the I/O operations on an MPEG file internally and pass the buffers directly to OpenMPEG.

Buffered play was partially implemented in the OpenMPEG Beta version delivered in Volume 11 of the IBM Developer Connection, but on a synchronous level in which OpenMPEG accepted buffers but immediately returned them as consumed. In Volume 12, the engine has been updated to accept several buffers and use the buffer memory throughout until the buffer data has been entirely consumed.

The examples in this article show how to set up for buffered play, pass buffers to OpenMPEG, and process consumed buffers. These examples can be examined further in the SHOWOM1 sample program provided with OpenMPEG in the "Multimedia Category" of the DevCon for OS/2 (disc 2).

Setup for Buffered Play

Buffered play requires the OM1_BUFFERS flag in OM1OpenStream. When the OM1OpenStream is complete, the OpenMPEG engine is ready to start calling for buffers. However, no buffers are available until the OM1_BUF_READY state is set or until an OM1Play is issued. This means that no calls to query the size or information about the content can occur until a callback OM1_BUF_STREAM_OPEN is received. Below is sample code showing how to open a buffered stream and set the OM1_BUF_READY state.

#define BUFFER_SIZE 16384
#define NUM_BUFFERS 21

USHORT usBufIndex;

/* create a zero size window and set handle = hwndMPEG */
memset (om1BufData, 0, sizeof (OM1_BUF_DATA) * NUM_BUFFERS);
memset (FileBuffer, 0, BUFFER_SIZE * NUM_BUFFERS);
OpenInput.Flags= OM1F_BUFFERS; /* Buffered play */
OpenInput.Filename = 0L;
OpenInput.WindowHandle = hwndMPEG;
OpenInput.pfnCallback = CallbackProc;
OpenInput.DeviceName = 0L;
OpenOutput.hStream = 0L;
rc = OM1OpenStream (&OpenInput, &OpenOutput);
if (rc == OM1E_SUCCESS) {
/* The stream is not really open at this point since no data has been */
/* passed to OpenMPEG, but the initial setup is successful */
 hStream = OpenOutput.hStream;

/* To start the OpenMPEG, to request buffers, pass an OM1_BUF_READY */

SetInput.Index = OM1I_BUF_READY;
SetInput.Flags = 0L;
rc = OM1Set (hStream, &SetInput);
if (rc !== OM1E_SUCCESS) {
 /* Error setting up for buffered play */
/* Callbacks should have already occurred in the */
/* callback procedure for getting the buffers */

Callback Procedure

The callback procedure is the most important feature of buffered play. The application uses this mechanism to provide the buffers to the OpenMPEG engine. The flow starts with the application telling OpenMPEG it is ready to send buffers with the OM1I_BUF_STATE index on OM1Set. On this thread, the OpenMPEG engine will call the callback procedure and request buffers until it has filled up its queue. For the application, it is important that the buffers are a minimum size or else OpenMPEG will not be able to process the MPEG content correctly.

Here is a list of the buffered play messages:


The important messages to process in the callback are the OM1M_BUF_READY message, where the data is returned in a pointer to the OM1_BUF_DATA structure, and the OM1M_BUF_STREAM_OPEN message, where OpenMPEG notifies the application that it has received enough buffers and has enough data to start playback. At this time, the application can query OpenMPEG to get the height and width of the movie to set its own window size. When the stream has been entirely processed after playback, OpenMPEG will send an OM1M_BUF_STREAM_CLOSE message to notify the application that the stream is closed. When the stream is empty, OpenMPEG will send an OM1M_BUF_EMPTY message to notify the application that playback quality is jeopardized because the stream does not have enough data to process. This state happens primarily when the application does not return data on the OM1M_BUF_READY message.

The following callback procedure demonstrates buffered play notifications and completion notifications:

ULONG APIENTRY Callback( BYTE msg, BYTE hStream, ULONG MessageValue )
 switch (msg) {
   if (MessageValue == OM1_PLAY) {
   /* Play is done so close the stream instance */
   /* and destroy the window */
   OM1Close (hStream);

   /* The OpenMPEG engine is requesting a buffer to
   /* If an empty buffer exists, then read from the file into
   if (!(om1BufData[usBufIndex].pBuffer)) {
     ulBytesRead = fread(FileBuffer[usBufIndex],
    /* No data can be read so return NULL */
    if (ulBytesRead == 0)
      return ((ULONG)NULL);
    /* Prep the om1BufData structure that will be returned
    om1BufData[usBufIndex].BufferSize = ulBytesRead;
    om1BufData[usBufIndex].pBuffer = (PBYTE) FileBuffer[usBufIndex];
    om1BufData[usBufIndex].BytesToCallback = ulBytesRead;
    om1BufData[usBufIndex].BufferID = usBufIndex;
    om1BufData[usBufIndex].Flags = 0;
    pom1_buf = &om1BufData[usBufIndex];

    /* The application uses the usBufIndex to manage its
    /* a circular fashion. It must check when it has reached
    /* end of the array and loop back to the beginning */
    if (usBufIndex >= NUM_BUFFERS) usBufIndex = 0;
    /* The buffer is passed to OpenMPEG in the return field*/
    return((ULONG) pom1_buf);
   } else {
    /* No data is available to pass so a NULL is returned */
    }/* endif */

   /* A buffer has been consumed so it can now be
   /* Later this buffer can be reused on the
   pom1_buf = (POM1_BUF_DATA) MessageValue;
   om1BufData[pom1_buf.BufferID].pBuffer = NULL;

  if (!MessageValue) {
   /* The stream was opened successfully and the now
   /* can query OpenMPEG for the size  */
   GetInput.Index = OM1I_VID_SIZE;
   ulReturn = OM1Get ( hStream, &GetInput, &GetOutput);
   ulWidth = GetOutput.Output & 0xffff;
   ulHeight = GetOutput.Output  16;
   WinPostMsg ( hwndFrame, WM_COMMAND, (PVOID)ID_SNAP, 0L );
   fOpen = TRUE;
  } else {
   /* An error occurred and the stream could not be
   return ( FALSE );
  } /* endif */

   /* The stream has closed, so the application should
 return 0;

Video CD and CD-i Support

Another new function available in the this release of OpenMPEG is the Video CD and CD-i support. CD-i type CDs are actually unreadable since they are similar to music CDs. However, we have provided the support to play a Video CD by passing the dummy file name _VIDCDI.MPG. OpenMPEG uses the dummy file name as a key to query the CD drive for a CD-i CD.

The documentation contains a new mode, OM1F_VIDEOCD. When this mode is specified in OM1OpenStream, it notifies OpenMPEG that a Video CD is to be played. As a result, OpenMPEG will play all the tracks on a Video CD without requiring the user to change to the CD drive, go to the MPEGAV directory, and select the last file. If you use the time format OM1F_TRACKS, you can seek to different tracks. This also applies to music video CDs like karaoke.

What We Are Working on Next

Our objective in the next release of OpenMPEG is to have better performance so that higher frame rates can be obtained with a complete software solution. Our goal is to give everyone the opportunity to use OpenMPEG in our final delivery. The buffered play support in OpenMPEG provides important functionality for networking and interactivity.

We value any feedback you can give us about this beta release of the OpenMPEG subsystem. If you have any comments about the beta, please let us know through the IBM forums or in the OS2DF1 Multimedia section on CompuServe.

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation