Синхронизация команд DVD

[Функция, связанная с этой страницей DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngine, и аудио/ видео захвата в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует, чтобы новый код использовал MediaPlayer, IMFMediaEngine и аудио- и видеозахват в Media Foundation вместо DirectShow, когда это возможно. Корпорация Майкрософт предлагает переписать существующий код, использующий устаревшие API, чтобы по возможности использовать новые API.]

Команды DVD не всегда выполняются мгновенно. По этой причине некоторые методы в IDvdControl2 являются асинхронными. К ним относятся методы воспроизведения, такие как PlayTitle, и методы навигации по меню, такие как ShowMenu и ReturnFromSubmenu. Асинхронный метод возвращается немедленно, не дожидаясь завершения команды. После возврата метода другие события могут помешать выполнению команды, даже если метод выполнен успешно. DirectShow предоставляет несколько вариантов синхронизации команд, начиная от отсутствия синхронизации до полной синхронизации с помощью событий графа фильтра.

Все асинхронные методы имеют параметр dwFlags и параметр ppCmd . Параметр dwFlags определяет поведение синхронизации, а параметр ppCmd возвращает указатель на необязательный объект синхронизации. Различные результаты поведения зависят от значений, которые вы задаете для этих параметров.

Синхронизация отсутствует

Для простого приложения воспроизведения DVD лучше всего просто игнорировать проблемы синхронизации. Иногда команда может завершиться сбоем или пользовательский интерфейс может немного запаздывать при обновлении, но эти ошибки будут находиться в порядке долей секунд.

Чтобы выполнить команду без синхронизации, установите флаг DVD_CMD_FLAG_None в параметре dwFlags и задайте для параметра ppCmdзначение NULL:

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

Блокировка

Если задать флаг EC_DVD_CMD_FLAG_Block в параметре dwFlags , метод блокируется до завершения команды:

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

Фактически этот флаг превращает асинхронный метод в синхронный. Недостаток заключается в том, что пользовательский интерфейс блокируется при вызове метода из потока приложения.

Объект синхронизации

Все асинхронные методы могут возвращать объект синхронизации, который можно использовать для ожидания запуска или завершения команды. Чтобы получить этот объект, передайте адрес указателя IDvdCmd в параметре ppCmd :

IDvdCmd *pCmdObj = NULL;
hr = pDVDControl2->PlayTitle(uTitle, DVD_CMD_FLAG_None, &pCmdObj);

Если метод завершается успешно, он возвращает новый объект IDvdCmd . Метод IDvdCmd::WaitForStart блокируется до начала команды, а метод IDvdCmd::WaitForEnd — до завершения команды. Возвращаемое значение указывает состояние команды.

Следующий код функционально эквивалентен установке флага EC_DVD_CMD_FLAG_Block, показанного ранее.

IDvdCmd *pCmdObj = NULL;
hr = pDVDControl2->PlayTitle(uTitle, DVD_CMD_FLAG_None, &pCmdObj);
if (SUCCEEDED(hr))
{
    // Use pCmdObj to wait for the command to complete.
    hr = pCmdObj->WaitToEnd();
    pCmdObj->Release();
}

В этом случае метод PlayTitle не блокируется, но приложение блокируется путем вызова WaitForEnd.

События состояния команды

Если вы задали флаг DVD_CMD_FLAG_SendEvents в параметре dwFlags , DVD-навигатор отправляет событие EC_DVD_CMD_START при запуске команды и событие EC_DVD_CMD_END при завершении команды.

Параметр lParam2 события — это возвращаемое значение HRESULT для команды. Параметр lParam1 события предоставляет способ получения объекта синхронизации для команды. Если передать lParam1 в метод IDvdInfo2::GetCmdFromEvent , метод возвращает указатель на интерфейс IDvdCmd объекта синхронизации. Этот интерфейс можно использовать для ожидания завершения команды, как описано выше. Однако если вы передали значение NULL для параметра ppCmd в исходном методе IDvdControl2 , DVD Navigator не создает объект синхронизации, а GetCmdFromEvent возвращает E_FAIL.

В следующем коде показано, как использовать события состояния команд без объекта синхронизации.

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

// In your event handling code:
switch (lEvent)
{
   case EC_DVD_CMD_END:
       HRESULT hr2 = (HRESULT)lParam2;
       /* ... */ 
       break;
}

Обратите внимание, что без объекта синхронизации невозможно определить, какая команда связана с событием. В следующем коде показано, как использовать события с объектом синхронизации. Идея заключается в том, чтобы сохранить объекты синхронизации в списке, а затем сравнить указатели объектов при получении события EC_DVD_CMD_START или EC_DVD_CMD_END .

IDvdCmd *pCmdObj = NULL;
hr = pDVDControl2->PlayTitle(uTitle, DVD_CMD_FLAG_SendEvents, &pCmdObj);
if (SUCCEEDED(hr)) 
{
    // Store pCmdObj in a list of pending commands.
}

// In your event handling code:
switch (lEvent)
{
case EC_DVD_CMD_END:
   {
       IDvdCmd *pObj = NULL;
       hr = pDvdInfo2->GetCmdFromEvent(lParam, &pObj);
       if (SUCCEEDED(hr)) 
       {
           // Find this object in your list by comparing IUnknown
           // pointers. Assume the following function is defined in 
           // your application:
           IDvdCmd *pPendingObj = GetPendingCommandFromList(pObj); 
           if (pPendingObj)
           {
               // Update UI accordingly (not shown). 
               pPendingObj->Release();
           }
           pObj->Release();
       }
    }
    break;
} 

Очистка буферов DVD-навигатора

Во время воспроизведения DVD-навигатор буферизует видеоданные. Объем буферизированных данных зависит. Когда DVD-навигатор переключается на новый фрагмент видео, данные, уже существующие в конвейере, не теряются, поэтому переход выполняется легко. По умолчанию, когда DVD-навигатор выдает команду, данные в конвейере не очищают. В результате может возникнуть некоторая задержка, прежде чем вы сможете увидеть эффект выполнения команды в зависимости от объема буферизации данных. Чтобы повысить скорость отклика, можно принудить DVD-навигатор к очистке, установив флаг DVD_CMD_FLAG_Flush.

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

Этот флаг можно объединить с любым из флагов, описанных ранее, с помощью побитового ИЛИ. Побочным эффектом очистки является то, что некоторые видео могут быть потеряны, поэтому не используйте этот флаг, если вам нужно гарантировать отсутствие пробелов в видео.

Приложения DVD