实现查找条
[与此页面关联的功能 DirectShow 是一项旧功能。 它已被 MediaPlayer、 IMFMediaEngine 和 媒体基金会中的音频/视频捕获取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能使用 MediaPlayer、 IMFMediaEngine 和 Media Foundation 中的音频/视频捕获 ,而不是 DirectShow。 如果可能,Microsoft 建议重写使用旧 API 的现有代码以使用新 API。]
本部分介绍如何为媒体播放器应用程序实现查找栏。 查找条作为跟踪条控件实现。 有关在 DirectShow 中查找的概述,请参阅 查找筛选器图。
应用程序启动时,初始化跟踪条:
void InitSlider(HWND hwnd)
{
// Initialize the trackbar range, but disable the
// control until the user opens a file.
hScroll = GetDlgItem(hwnd, IDC_SLIDER1);
EnableWindow(hScroll, FALSE);
SendMessage(hScroll, TBM_SETRANGE, TRUE, MAKELONG(0, 100));
}
在用户打开媒体文件之前,将禁用跟踪条。 跟踪条范围设置为 0 到 100。 在文件播放期间,应用程序将按文件持续时间的百分比计算播放位置,并相应地更新跟踪条。 例如,跟踪条位置“50”始终对应于文件的中间。
当用户打开文件时,使用 RenderFile 生成文件播放图。 此操作的代码显示在 如何播放文件中。 然后查询 IMediaSeeking 接口的 Filter Graph 管理器并存储接口指针:
IMediaSeeking *g_pSeek = 0;
hr = pGraph->QueryInterface(IID_IMediaSeeking, (void**)&g_pSeek);
若要确定文件是否可查找,请调用 IMediaSeeking::CheckCapabilities 方法或 IMediaSeeking::GetCapabilities 方法。 这些方法几乎执行相同的操作,但它们的语义略有不同。 以下示例使用 CheckCapabilites:
// Determine if the source is seekable.
BOOL bCanSeek = FALSE;
DWORD caps = AM_SEEKING_CanSeekAbsolute | AM_SEEKING_CanGetDuration;
bCanSeek = (S_OK == pSeek->CheckCapabilities(&caps));
if (bCanSeek)
{
// Enable the trackbar.
EnableWindow(hScroll, TRUE);
// Find the file duration.
pSeek->GetDuration(&g_rtTotalTime);
}
AM_SEEKING_CanSeekAbsolute标志检查源文件是否可查找,AM_SEEKING_CanGetDuration标志检查是否可以提前确定文件的持续时间。 如果这两项功能都受支持,应用程序将启用跟踪栏并检索文件持续时间。
如果图形是可查找的,应用程序将使用计时器在播放期间更新跟踪条位置。 运行筛选器图播放文件时,通过调用 Windows 计时器函数之一(如 SetTimer)启动计时器事件。 有关计时器的详细信息,请参阅平台 SDK 中的主题“计时器”。
void StartPlayback(HWND hwnd)
{
pControl->Run();
if (bCanSeek)
{
StopTimer(); // Make sure an old timer is not still active.
nTimerID = SetTimer(hwnd, IDT_TIMER1, TICK_FREQ, (TIMERPROC)NULL);
if (nTimerID == 0)
{
/* Handle Error */
}
}
}
void StopTimer()
{
if (wTimerID != 0)
{
KillTimer(g_hwnd, wTimerID);
wTimerID = 0;
}
}
使用计时器事件更新跟踪条的位置。 调用 IMediaSeeking::GetCurrentPosition 检索库兰特播放位置,然后将位置计算为文件持续时间的百分比:
case WM_TIMER:
if (wParam == IDT_TIMER1)
{
// Timer should not be running unless we really can seek.
ASSERT(bCanSeek == TRUE);
REFERENCE_TIME timeNow;
if (SUCCEEDED(pSeek->GetCurrentPosition(&timeNow)))
{
long sliderTick = (long)((timeNow * 100) / g_rtTotalTime);
SendMessage( hScroll, TBM_SETPOS, TRUE, sliderTick );
}
}
break;
用户还可以移动跟踪条来查找文件。 当用户拖动或单击跟踪条控件时,应用程序会收到WM_HSCROLL事件。 wParam 参数的低字是跟踪条通知消息。 例如,在跟踪条操作结束时发送TB_ENDTRACK,当用户拖动跟踪条时,将连续发送TB_THUMBTRACK。 以下代码演示了处理WM_HSCROLL消息的一种方法:
static OAFilterState state;
static BOOL bStartOfScroll = TRUE;
case WM_HSCROLL:
short int userReq = LOWORD(wParam);
if (userReq == TB_ENDTRACK || userReq == TB_THUMBTRACK)
{
DWORD dwPosition = SendMessage(hTrackbar, TBM_GETPOS, 0, 0);
// Pause when the scroll action begins.
if (bStartOfScroll)
{
pControl->GetState(10, &state);
bStartOfScroll = FALSE;
pControl->Pause();
}
// Update the position continuously.
REFERENCE_TIME newTime = (g_rtTotalTime/100) * dwPosition;
pSeek->SetPositions(&newTime, AM_SEEKING_AbsolutePositioning,
NULL, AM_SEEKING_NoPositioning);
// Restore the state at the end.
if (userReq == TB_ENDTRACK)
{
if (state == State_Stopped)
pControl->Stop();
else if (state == State_Running)
pControl->Run();
bStartOfScroll = TRUE;
}
}
}
如果用户拖动跟踪条,应用程序会发出一系列 seek 命令,每个命令针对它收到的TB_THUMBTRACK消息。 为了使搜寻操作尽可能顺利,应用程序暂停图形。 暂停图形会停止播放,但可确保更新视频窗口。 当应用程序收到TB_ENDTRACK消息时,它会将图形还原到其原始状态。