Partilhar via


Sincronizando comandos de DVD

[O recurso associado a esta página, DirectShow, é um recurso herdado. Foi substituído por MediaPlayer, IMFMediaEnginee Audio/Video Capture in Media Foundation. Esses recursos foram otimizados para Windows 10 e Windows 11. A Microsoft recomenda vivamente que o novo código utilize MediaPlayer, IMFMediaEngine e Captura de Áudio/Vídeo no Media Foundation em vez de DirectShow, quando possível. A Microsoft sugere que o código existente que usa as APIs herdadas seja reescrito para usar as novas APIs, se possível.]

Os comandos do DVD nem sempre são concluídos instantaneamente. Por esse motivo, alguns dos métodos em IDvdControl2 são assíncronos. Isso inclui métodos de reprodução, como PlayTitle, e métodos de navegação de menu, como ShowMenu e ReturnFromSubmenu. Um método assíncrono retorna imediatamente, sem esperar que o comando seja concluído. Depois que o método retorna, outros eventos podem impedir que o comando seja concluído, mesmo se o método tiver êxito. O DirectShow fornece várias opções para sincronizar comandos, desde a ausência de sincronização até a sincronização completa usando eventos de gráfico de filtro.

Todos os métodos assíncronos têm um parâmetro dwFlags e um parâmetro ppCmd. O parâmetro dwFlags especifica o comportamento de sincronização e o parâmetro ppCmd retorna um ponteiro para um objeto de sincronização opcional. Comportamentos diferentes resultam dependendo dos valores que você dá para esses parâmetros.

Sem sincronização

Para um aplicativo básico de reprodução de DVD, a melhor opção pode ser simplesmente ignorar problemas de sincronização. Ocasionalmente, um comando pode falhar ou a interface do usuário pode atrasar ligeiramente quando é atualizada, mas esses erros serão da ordem de frações de segundos.

Para emitir um comando sem sincronização, defina o sinalizador DVD_CMD_FLAG_None no parâmetro dwFlags e defina o parâmetro ppCmd como NULL:

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

Bloqueio

Se definir o sinalizador EC_DVD_CMD_FLAG_Block no parâmetro dwFlags, o método será bloqueado até que o comando seja concluído:

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

Na verdade, esse sinalizador transforma um método assíncrono em um método síncrono. A desvantagem é que sua interface do usuário bloqueia se você chamar o método a partir do thread do aplicativo.

Objeto de Sincronização

Todos os métodos assíncronos podem retornar um objeto de sincronização, que você pode usar para aguardar o início ou o término do comando. Para obter este objeto, passe o endereço de um ponteiro IDvdCmd no parâmetro ppCmd:

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

Se o método for bem-sucedido, ele retornará um novo IDvdCmd objeto. O método IDvdCmd::WaitForStart bloqueia até que o comando comece e o IDvdCmd::WaitForEnd bloqueia até que o comando termine. O valor de retorno indica o status do comando.

O código a seguir é funcionalmente equivalente à configuração do sinalizador EC_DVD_CMD_FLAG_Block, conforme mostrado anteriormente.

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();
}

Nesse caso, o método PlayTitle não bloqueia, mas o aplicativo bloqueia chamando WaitForEnd.

Eventos de Estado do Comando

Se você definir o sinalizador DVD_CMD_FLAG_SendEvents no parâmetro dwFlags, o Navegador de DVD enviará um evento EC_DVD_CMD_START quando o comando começar e um evento EC_DVD_CMD_END quando o comando terminar.

O parâmetro lParam2 do evento é o valor de retorno HRESULT para o comando. O parâmetro lParam1 do evento fornece uma maneira de obter o objeto de sincronização para o comando. Se passar lParam1 para o método IDvdInfo2::GetCmdFromEvent, o método retornará um ponteiro para a interface IDvdCmd do objeto de sincronização. Você pode usar essa interface para aguardar a conclusão do comando, conforme descrito anteriormente. No entanto, se você passou NULL para o parâmetro ppCmd no método IDvdControl2 original, o Navegador de DVD não cria um objeto de sincronização e GetCmdFromEvent retorna E_FAIL.

O código a seguir mostra como usar eventos de status de comando sem objeto de sincronização.

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;
}

Observe que, sem um objeto de sincronização, você não pode saber qual comando está associado ao evento. O código a seguir mostra como usar eventos com o objeto de sincronização. A ideia é armazenar os objetos de sincronização em uma lista e, em seguida, comparar ponteiros de objeto quando você obtém o evento EC_DVD_CMD_START ou 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;
} 

Limpar os buffers do Navegador de DVD

Durante a reprodução, o Navegador de DVD armazena dados de vídeo em buffer. A quantidade de dados armazenados em buffer varia. Quando o Navegador de DVD muda para uma nova parte do vídeo, os dados que já estão no pipeline não são perdidos, portanto, a transição é perfeita. Por padrão, quando o Navegador de DVD emite um comando, ele não limpa os dados já no fluxo de dados. Como resultado, pode haver alguma latência antes que você possa ver o efeito do comando, dependendo da quantidade de dados armazenados em buffer. Para aumentar a capacidade de resposta, você pode forçar o Navegador de DVD a descarregar, definindo o sinalizador DVD_CMD_FLAG_Flush.

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

Este sinalizador pode ser combinado com qualquer um dos sinalizadores descritos anteriormente, usando um bit a bit OR. Um efeito colateral do flushing é que algum vídeo pode ser perdido, então não use esta bandeira se você precisa garantir que não há lacunas no vídeo.

Aplicações de DVD