Поделиться через


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

[Функция, связанная с этой страницей, DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngineи аудио и видеозахват в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Компания Майкрософт настоятельно рекомендует, чтобы в новом коде, по возможности, использовались MediaPlayer, IMFMediaEngine и Audio/Video Capture в 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);

Если метод выполнен успешно, он возвращает новый объект IDvdCmdCmd. Метод 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-навигатор не создает объект синхронизации, а 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-приложения