共用方式為


實作搜尋列

[與此頁面相關的功能 DirectShow是舊版功能。 它已被 MediaPlayerIMFMediaEngineMedia Foundation 中的音訊/視訊擷取取代。 這些功能已針對Windows 10和Windows 11進行優化。 Microsoft 強烈建議新程式碼盡可能使用 MediaPlayerIMFMediaEngine音訊/視訊擷取 ,而不是 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 以擷取 Currant 播放位置,然後將位置計算為檔案持續時間的百分比:

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

如果使用者拖曳追蹤列,應用程式會發出一系列的搜尋命令,每個TB_THUMBTRACK訊息都會發出一個。 為了讓搜尋作業盡可能順暢,應用程式會暫停圖形。 暫停圖表會停止播放,但可確保影片視窗已更新。 當應用程式收到TB_ENDTRACK訊息時,它會將圖形還原為其原始狀態。