Implementing a Seek Bar
[The feature associated with this page, DirectShow, is a legacy feature. It has been superseded by MediaPlayer, IMFMediaEngine, and Audio/Video Capture in Media Foundation. Those features have been optimized for Windows 10 and Windows 11. Microsoft strongly recommends that new code use MediaPlayer, IMFMediaEngine and Audio/Video Capture in Media Foundation instead of DirectShow, when possible. Microsoft suggests that existing code that uses the legacy APIs be rewritten to use the new APIs if possible.]
This section describes how to implement a seek bar for a media-player application. The seek bar is implemented as a trackbar control. For an overview of seeking in DirectShow, see Seeking the Filter Graph.
When the application starts, initialize the trackbar:
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));
}
The trackbar is disabled until the user opens a media file. The trackbar range is set from 0 to 100. During file playback, the application will calculate the playback position as a percentage of the file duration, and update the trackbar accordingly. For example, trackbar position "50" always corresponds to the middle of the file.
When the user opens a file, build a file-playback graph using RenderFile. The code for this is shown in How To Play a File. Then query the Filter Graph Manager for the IMediaSeeking interface and store the interface pointer:
IMediaSeeking *g_pSeek = 0;
hr = pGraph->QueryInterface(IID_IMediaSeeking, (void**)&g_pSeek);
To determine whether the file is seekable, call either the IMediaSeeking::CheckCapabilities method or the IMediaSeeking::GetCapabilities method. These methods do almost the same thing, but their semantics are slightly different. The following example uses 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);
}
The AM_SEEKING_CanSeekAbsolute flag checks whether the source file is seekable, and the AM_SEEKING_CanGetDuration flag checks whether the duration of the file can be determined in advance. If both of the capabilities are supported, the application enables the trackbar and retrieves the file duration.
If the graph is seekable, the application will use a timer to update the trackbar position during playback. When you run the filter graph to play the file, start the timer event by calling one of the Windows timer functions, such as SetTimer. For more information about timers, see the topic "Timers" in the Platform 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;
}
}
Use the timer event to update the position of the trackbar. Call IMediaSeeking::GetCurrentPosition to retrieve the currant playback position, and then calculate the position as a percentage of the file duration:
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;
The user can also move the trackbar to seek the file. When the user drags or clicks the trackbar control, the application receives a WM_HSCROLL event. The low word of the wParam parameter is the trackbar notification message. For example, TB_ENDTRACK is sent at the end of the trackbar action, and TB_THUMBTRACK is sent continuously while the user drags the trackbar. The following code shows one way to handle the WM_HSCROLL message:
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;
}
}
}
If the user drags the trackbar, the application issues a series of seek commands, one for each TB_THUMBTRACK message that it receives. To make the seek operations as smooth as possible, the application pauses the graph. Pausing the graph halts playback but ensures that the video window is updated. When the application receives the TB_ENDTRACK message, it restores the graph to its original state.