实现查找条

[与此页面关联的功能 DirectShow 是一项旧功能。 它已被 MediaPlayerIMFMediaEngine媒体基金会中的音频/视频捕获取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能使用 MediaPlayerIMFMediaEngineMedia 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消息时,它会将图形还原到其原始状态。