Creating the Stream

After handler IDs are obtained and the SPCBKEY and DCB information is filled in, the MCD makes the call to SpiCreateStream. At this point the two handlers are initiated to create their stream, buffers are allocated, and the stage is set for streaming.

The following example code illustrates how to create the stream. The caller supplies the source and target stream handlers and the audio device control block should have been filled in previously (see MCIOPEN.C). The caller also supplies the EventProc(edure) where all of the stream events will be reported.

  ulrc = SpiCreateStream ( hidSrc,
                           hidTgt,
                           &pInstance->StreamInfo.SpcbKey,
                           (PDCB) &pInstance->StreamInfo.AudioDCB,
                           (PDCB) &pInstance->StreamInfo.AudioDCB,
                           (PIMPL_EVCB) &pInstance->StreamInfo.Evcb,
                           (PEVFN) EventProc,
                           (ULONG) NULL,
                           hStream,
                           &pInstance->StreamInfo.hEvent);

When the MCD creates a stream, it must register a callback handle with SSM. The callback handle is an entry point or function within the MCD code that processes events coming back from SSM during the streaming process. The callback handle is a powerful mechanism because it frees the driver to do other work during the streaming process. When an event occurs, SSM detects it and reports it to the callback address.

The waveform audio MCD sample includes the following event routines:

RC APIENTRY RecordEventRoutine ( MEVCB  *pevcb){
  MTIME_EVCB        *pMTimeEVCB;    // Modified EVCB
  INSTANCE          *ulpInstance;   // Instance Ptr

  /***********************************************************
  * EventProc receives asynchronous SSM event notifications
  * When the event is received, the event semaphore is posted
  * which will wake up the MCD thread(s) blocked on this
  * semaphore.
  * The semaphore is not posted for time events like
  * cuepoint (TIME) and media position changes since they do
  * not alter the state of the stream.
  ************************************************************/


  switch (pevcb->evcb.ulType)
  {
  case EVENT_IMPLICIT_TYPE:

       /* Retrieve our instance from the EVCB */

       ulpInstance = (INSTANCE *)pevcb->ulpInstance;

       switch (pevcb->evcb.ulSubType)
       {
       case EVENT_ERROR:
            ulpInstance->StreamEvent = EVENT_ERROR;

            /****************************************
            * Check for playlist specific error first
            *****************************************/

            /**************************************
            * End of PlayList event is received
            * as an implicit error event. It
            * is treated as a normal EOS
            ***************************************/
            if (ulpInstance->usPlayLstStrm == TRUE)
                if (pevcb->evcb.ulStatus == ERROR_END_OF_PLAYLIST)
                    ulpInstance->StreamInfo.Evcb.evcb.ulStatus =
                        MMIOERR_CANNOTWRITE;

            DosPostEventSem (ulpInstance->hEventSem);
           break;

       case EVENT_STREAM_STOPPED:
            /****************************************
            * Event Stream Stopped. Release the
            * Blocked thread
            *****************************************/
            ulpInstance->StreamEvent = EVENT_STREAM_STOPPED;
            DosPostEventSem (ulpInstance->hEventSem);
           break;

       case EVENT_SYNC_PREROLLED:
            /******************************************
            * This event is received in response to a
            * preroll start. A Preroll start is done
            * on an MCI_CUE message.
            *******************************************/
            ulpInstance->StreamEvent = EVENT_SYNC_PREROLLED;
            DosPostEventSem (ulpInstance->hEventSem);
           break;
.
.
.

The MEVCB structure in the RecordEventRoutine is a modified IMPL_EVCB which SSM uses to report events (see the OS/2 Multimedia Programming Reference). You can add additional fields to extend the EVCB structure. For example, MEVCB shown in the following example adds an instance pointer containing local instance data. When the Sync/Stream Manager calls the waveform audio MCD, it allows the MCD to have access to the local instance data, which is tied to that event.

The PlayEventRoutine shown in the following example is presumed to receive all types of event notifications from SSM. The types include implicit events and cue point notifications in terms of both time and data. In response to cue point notifications a MCI_CUEPOINT message is returned to MDM by way of mdmDriverNotify.

RC APIENTRY PlayEventRoutine ( MEVCB     *pevcb)
{
  MTIME_EVCB        *pMTimeEVCB;      /* Modified Time EVCB*/
  INSTANCE          * ulpInstance;    /* Current Instance*/
  HWND              hWnd;             /* Callback Handle */
  BOOL              fPlayListDone = FALSE;

  /***********************************************************
  * EventProc receives asynchronous SSM event notifications
  * When the event is received, the event semaphore is posted
  * which will wake up the MCD thread(s) blocked on this
  * semaphore.
  * The semaphore is not posted for time events like
  * cuepoint (TIME) and media position changes since they do
  * not alter the state of the stream.
  ************************************************************/

  switch (pevcb->evcb.ulType)
  {
  case EVENT_IMPLICIT_TYPE:

       /* Retrieve our instance from the EVCB */

       ulpInstance = (INSTANCE *)pevcb->ulpInstance;

       /* Retrieve the callback handle to post messages on */

       hWnd = ulpInstance->hwndCallBack;

       switch (pevcb->evcb.ulSubType)
       {
       case EVENT_EOS:
            ulpInstance->StreamEvent = EVENT_EOS;
            DosPostEventSem (ulpInstance->hEventSem);
           break;

       case EVENT_STREAM_STOPPED:
            /* Self explanatory--someone stopped the stream */

            ulpInstance->StreamEvent = EVENT_STREAM_STOPPED;
            DosPostEventSem (ulpInstance->hEventSem);
           break;

       case EVENT_SYNC_PREROLLED:
            /******************************************
            * This event is received in response to a
            * preroll start. A Preroll start is done
            * on an MCI_CUE message.
            *******************************************/

            ulpInstance->StreamEvent = EVENT_SYNC_PREROLLED;
            DosPostEventSem (ulpInstance->hEventSem);
           break;

       case EVENT_PLAYLISTMESSAGE:

            /******************************************
            * We can receive this event if a playlist
            * parser hits the MESSAGE COMMAND.
            * NOTE: The MCD should return this message
            * with the callback handle specified on the
            * open.  This could be the source of much
            * grief if you return on the wrong handle.
            ******************************************/

            mdmDriverNotify ( ulpInstance->usWaveDeviceID,
                            ulpInstance->hwndOpenCallBack,
                            MM_MCIPLAYLISTMESSAGE,
                            (USHORT) MAKEULONG(pevcb->evcb.ulStatus,
                                ulpInstance->usWaveDeviceID),
                            (ULONG) pevcb->evcb.unused1);
           break;

       case EVENT_PLAYLISTCUEPOINT:

            /************************************************
            * We can receive this event if a playlist
            * parser hits the CUEPOINT COMMAND opcode
            * in the playlist.  This differs from a "normal"
            * cuepoint because it is detected by the source,
            * rather than the target stream handler.
            ************************************************/


            mdmDriverNotify ( ulpInstance->usWaveDeviceID,
                              ulpInstance->hwndOpenCallBack,
                              MM_MCICUEPOINT,
                              (USHORT) MAKEULONG(pevcb->evcb.ulStatus,
                                  ulpInstance->usWaveDeviceID),
                              (ULONG) pevcb->evcb.unused1);
           break;


       } /* SubType case of Implicit Events */
      break;

  case EVENT_CUE_TIME_PAUSE:
       {
       /***************************************************
       * This event will arrive if we played to a certain
       * position in the stream.  Let the play thread know
       * that we have reached the desired point.
       ****************************************************/

       pMTimeEVCB = (MTIME_EVCB *)pevcb;
       ulpInstance = (INSTANCE *)pMTimeEVCB->ulpInstance;
       ulpInstance->StreamEvent = EVENT_CUE_TIME_PAUSE;

       DosPostEventSem (ulpInstance->hEventSem);
       }
       break;

  case EVENT_CUE_TIME:

       break;

  } /* All Events case */

  return (MCIERR_SUCCESS);

} /* PlayEventProc */

Events reported by SSM can be normal occurrences, such as an end-of-stream because playback is complete, or the events can be abnormal occurrences, such as an error returned during the streaming process. The driver needs to know about these events.

SSM reports two types of events: implicit and explicit. Implicit events are always reported. When one occurs, the driver must receive the notification of its occurrence from SSM, however, it does not have to take any action.

An explicit event is reported to the driver only when the driver requests to be notified of the event's occurrence. For example, the driver requests cuepoint notifications.


[Back: Preparing to Stream the Waveform Data]
[Next: Event Processing]