シーク バーの実装

[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayer、IMFMediaEngine、Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayerIMFMediaEngineAudio/Video Capture を使用することを強くお勧めします。 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 インターフェイスのクエリを実行し、インターフェイス ポインターを格納します。

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 フラグはファイルの期間を事前に決定できるかどうかを確認します。 両方の機能がサポートされている場合、アプリケーションによってトラックバーが有効になり、ファイルの期間が取得されます。

グラフがシーク可能な場合、アプリケーションはタイマーを使用して再生中にトラックバーの位置を更新します。 フィルター グラフを実行してファイルを再生する場合は、 SetTimer などの Windows タイマー関数のいずれかを呼び出してタイマー イベントを開始します。 タイマーの詳細については、プラットフォーム 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 メッセージを処理する 1 つの方法を示しています。

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メッセージごとに 1 つずつです。 シーク操作を可能な限りスムーズにするために、アプリケーションはグラフを一時停止します。 グラフを一時停止すると再生は停止しますが、ビデオ ウィンドウが確実に更新されます。 アプリケーションは、TB_ENDTRACK メッセージを受信すると、グラフを元の状態に復元します。