同步 DVD 命令
[与此页面关联的功能 DirectShow 是一项旧功能。 它已被 MediaPlayer、 IMFMediaEngine 和 媒体基金会中的音频/视频捕获取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能使用 MediaPlayer、 IMFMediaEngine 和 Media Foundation 中的音频/视频捕获 ,而不是 DirectShow。 如果可能,Microsoft 建议重写使用旧 API 的现有代码以使用新 API。]
DVD 命令并不总是立即完成。 因此, IDvdControl2 中的某些方法是异步的。 其中包括播放方法(如 PlayTitle)和菜单导航方法(如 ShowMenu 和 ReturnFromSubmenu)。 异步方法会立即返回,而无需等待命令完成。 方法返回后,其他事件可能会阻止命令完成,即使该方法成功也是如此。 DirectShow 提供了多个用于同步命令的选项,从不同步到使用筛选器图事件完全同步。
所有异步方法都有一个 dwFlags 参数和一个 ppCmd 参数。 dwFlags 参数指定同步行为,ppCmd 参数返回指向可选同步对象的指针。 根据为这些参数提供的值,会导致不同的行为。
无同步
对于基本 DVD 播放应用程序,最佳选择可能只是忽略同步问题。 有时,命令可能会失败,或者 UI 在更新时可能会稍有滞后,但这些错误将以秒为单位。
若要发出不同步的命令,请在 dwFlags 参数中设置 DVD_CMD_FLAG_None 标志,并将 ppCmd 参数设置为 NULL:
hr = pDVDControl2->PlayTitle(uTitle, DVD_CMD_FLAG_None, NULL);
阻塞
如果在 dwFlags 参数中设置 EC_DVD_CMD_FLAG_Block 标志, 方法将阻止,直到命令完成:
hr = pDVDControl2->PlayTitle(uTitle, EC_DVD_CMD_FLAG_Block, NULL);
实际上,此标志会将异步方法转换为同步方法。 缺点是,如果你从应用程序线程调用 方法,则 UI 会阻塞。
同步对象
所有异步方法都可以返回同步对象,可以使用该对象等待命令开始或结束。 若要获取此对象,请在 ppCmd 参数中传递 IDvdCmd 指针的地址:
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 阻止。
命令状态事件
如果在 dwFlags 参数中设置DVD_CMD_FLAG_SendEvents标志,DVD 导航器将在命令开始时发送 EC_DVD_CMD_START 事件,并在命令结束时发送 EC_DVD_CMD_END 事件。
事件的 lParam2 参数是命令的 HRESULT 返回值。 事件的 lParam1 参数提供了一种方法来获取命令的同步对象。 如果将 lParam1 传递给 IDvdInfo2::GetCmdFromEvent 方法,该方法将返回指向同步对象的 IDvdCmd 接口的指针。 可以使用此接口等待命令完成,如前所述。 但是,如果在原始 IDvdControl2 方法中为 ppCmd 参数传递了 NULL,则 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_CMD_FLAG_Flush标志来强制 DVD 导航器刷新。
hr = pDVDControl2->PlayTitle(uTitle, DVD_CMD_FLAG_Flush, NULL);
可以使用按位 OR 将此标志与上述任何标志组合使用。 刷新的副作用是某些视频可能会丢失,因此,如果需要保证视频中没有间隙,请不要使用此标志。
相关主题