Microsoft DirectX 9.0

Synchronizing DVD Commands

This section contains the following subtopics.

About Command Synchronization

Command synchronization is an advanced topic for developers who are familiar with the DVD Navigator and DVD navigation issues in general, and whose application requires the sophisticated functionality that command synchronization provides. If you are just starting out with the DVD Navigator, or if your application does not require command synchronization, simply use the various play commands as illustrated in Approach #1. You can always incorporate command synchronization later.

The various playback methods in IDvdControl2—for example, PlayTitle—are asynchronous in nature. When the method is called, it immediately returns a result before it has carried out all the necessary steps. During the period between the successful return of the method call and the ultimate completion of the operation, other commands might be issued that put the DVD Navigator into a new DVD domain or new filter state that prevents the method from successfully completing all the necessary steps. For example, assume your application calls PlayTitle, and immediately afterward, the user clicks the Stop button. If the commands are not synchronized, the DVD Navigator can enter the DVD Stop domain before the disc begins playing at the specified location. This is because PlayTitle returns successfully before the entire operation is carried out. When the disc finally begins to play, the DVD Navigator is no longer in the correct state, and an error will result.

Because many method calls depend on the DVD Navigator being in a particular state, applications need a way to synchronize commands. This involves the following two basic capabilities.

The DVD Navigator gives applications these capabilities, along with several options for synchronizing commands. Each playback method in IDvdControl2 has two parameters used for synchronization: an interface pointer to a synchronization object, and a set of DVD command flags. An application can use these parameters to implement command synchronization in various ways depending on its needs.

Approach 1: No Synchronization

To maintain the asynchronous nature of the method call, use the following syntax, where pDVDControl2 is an IDvdControl2 pointer.

HRESULT hr = pDVDControl2->PlayTitle( uTitle, 
                                      DVD_CMD_FLAG_None, // = 0
                                      NULL);

Approach 2: Simple Blocking Without Synchronization

This approach uses the DVD command flags to implement simple blocking without managing command synchronization objects. This is the simplest way to synchronize commands, because you do not have to manage synchronization objects or handle event notifications, but it does not enable you to determine the return status of any particular command.

HRESULT hr = pDVDControl2->PlayTitle( uTitle,
                                      EC_DVD_CMD_FLAG_Block,         
                                      NULL);

Approach 3: Using Only the Synchronization Object

This approach is functionally equivalent to Approach 2. It provides another way to block the DVD Navigator directly without processing event notifications. When an application passes the address of an IDvdCmd pointer, it receives a command synchronization object that is associated with this specific command. Calling the object's WaitToEnd function will block the DVD Navigator until the method successfully completes. In the meantime, new commands might be added to the queue. Be sure to release the object when you are done with it.

IDvdCmd* pObj;
HRESULT hr = pDVDControl2->PlayTitle(uTitle,  0, &pObj);
if(SUCCEEDED(hr))
{
    pObj->WaitToEnd();
    pObj->Release();
}

Approach 4: Synchronization of One Command at a Time Using Events

By specifying the DVD_CMD_FLAG_SendEvents flag in the dwFlags parameter of the various "play" method calls(PlayChapter, PlayAtTimeInTitle, and so on), the DVD Navigator will not block on the method call. Instead, it sends your application EC_DVD_CMD_START and EC_DVD_CMD_END (see DVD Event Notification Codes) event notifications when the command begins and then when its entire asynchronous operation completes. This enables you to synchronize single events without managing IDvdCmd objects. By not using IDvdCmd objects, you cannot know for sure which command the event is associated with. For many situations, such information is probably not needed anyway, so this approach is sufficient in those cases.

// Call PlayChapterInTitle using "SendEvents".
HRESULT hr = pDVDControl2->PlayChapterInTitle( uTitle, 0x2,
                    DVD_CMD_FLAG_SendEvents, NULL);
...

// In the event notification handler function
switch (lEvent)
{   
   case EC_DVD_CMD_END:
       DoSomething(); 
       break;
}

Approach 5: Synchronization Using Events and IDvdCmd Objects

This approach provides the greatest control over command synchronization, but it is also the most complex to implement. It enables applications to know which command an event is associated with, so that the application can tailor its responses to the EC_DVD_CMD_END events. The first example shows a single-threaded solution, and the second example shows a multithreaded solution.

// Global variable.
IDvdCmd *pGlobalObj = 0;
...

// The navigator assigns pGlobalObj before the event is issued. 
// Otherwise, the event could occur while pGlobalObj is still NULL.
HRESULT hr = pDVDControl2->PlayTitle( uTitle,
DVD_CMD_FLAG_SendEvents, &pGlobalObj );
if(FAILED(hr)) 
{
    pGlobalObj = NULL;
}
... 

// In your event handling code:
switch (lEvent)
{
case EC_DVD_CMD_END:
   {
       IDvdCmd* pObj = GetDvdCmd(lParam1);
       HRESULT hr = (HRESULT)lParam2;
       if (pObj != NULL) 
       {
           if (pGlobalObj == pObj) 
           {
               ...
               pGlobalObj->Release();
               pGlobalObj = NULL;
           }
           pObj->Release();
       }
    }
    break;

} // switch

When your event loop is on a separate thread, use the following approach:

// In global code
IDvdCmd *pGlobalObj=0;
CCritSec globalCritSect;
...

{ // Scope for lock
    CAutoLock(globalCritSect);
    HRESULT hr = pDVDControl2->PlayTitle(uTitle,
DVD_CMD_FLAG_SendEvents, &pGlobalObj);
    if (FAILED(hr)) 
    {
        pGlobalObj = NULL;
    }
}
...

// In your event handling code:
switch (lEvent)
{
case EC_DVD_CMD_COMPLETE:
// Fall through.
case EC_DVD_CMD_CANCEL:
    {
        CAutoLock(globalCritSect);

        IDvdCmd *pObj = GetDvdCmd(lParam1);
        HRESULT hr = (HRESULT)lParam2;
        if (pObj != NULL) 
        {
            if (pGlobalObj == obj) 
            {
                ...
                pGlobalObj->Release();
                pGlobalObj = NULL;
            }
            pObj->Release();
        }
    }
    break;
} // switch

The CCritSec and CAutoLock classes are defined in the DirectShow base-class library.