📄 player.~pas
字号:
if (not m_bActive) then
begin
// render graph first, or data may be discarded
(FFilterGraph as IGraphBuilder).QueryInterface(IID_IMediaControl, pMC);
pMC.Pause();
// seek all source graphs to correct start positions
SeekSourceGraphs();
// activate each graph
for i:= 0 to m_Clips.Count-1 do
// list<ClipEntry>::iterator it = m_Clips.begin();
// for(; it != m_Clips.end(); it++)
begin
TCLipEntry(m_Clips.Items[i]).Graph.QueryInterface(IID_IMediaControl, pMC);
pMC.Run();
end;
m_bActive := true;
end;
// IMediaControlPtr pMC = m_pRenderGraph;
(FFilterGraph as IGraphBuilder).QueryInterface(iid_iMediaControl, pMC);
if (pMC <> Nil) then
pMC.Pause();
result:= S_OK;
end;
function TClipPlayer.Stop: HRESULT;
var
hr: HRESULT;
pMC: IMediaControl;
i: integer;
begin
if (m_bActive) then
begin
// deactivate all graphs
(FFilterGraph as IGraphBuilder).QueryInterface(iid_iMediaControl, pMC);
if (pMC <> Nil) then
pMC.Stop();
for i:= 0 to m_Clips.Count-1 do
begin
TCLipEntry(m_Clips.Items[i]).Graph.QueryInterface(IID_IMediaControl, pMC);
pMC.Stop;
end;
m_bActive := false;
end;
// rewind on stop
//? m_itCurrent = m_Clips.end();
m_tStartPosition := 0;
m_pPlayNext := nil;
result:= S_OK;
end;
procedure TClipPlayer.OnEndOfSegment();
var
idx: integer;
ClipOld: TClipEntry;
begin
// ? already at the end
{
if (m_itCurrent = m_Clips.Items[m_Clips.Count - 1]) then
exit;
}
ClipOld := m_itCurrent;
if (m_itCurrent = m_Clips.Items[m_Clips.Count - 1]) then
begin
if (not m_bLoop) then
begin
// no more clips, so allow EOS to propagate through
// render graph. We will receive EC_COMPLETE eventually.
m_pController.NoMoreSegments;
// disconnect graphs -- but we cannot do that until NoMoreSegments has
// sent the EndOfStream through the bridge
m_pController.BridgeGraphs(nil, nil);
exit;
end;
// disconnect graphs before seeking the source graph
m_pController.BridgeGraphs(nil, nil);
m_itCurrent := m_Clips.Items[0];
m_tStartPosition := 0;
m_itCurrent.Prime;
// looping to same graph? rewind now
if (m_itCurrent = ClipOld) then
m_itCurrent.Prime(0);
// now we are about to connect it, so as soon as data is delivered out of the graph it
// is no longer primed for re-use (it will need a new seek before using again)
m_itCurrent.InUse();
m_pController.BridgeGraphs(m_itCurrent.SinkFilter, m_pRenderGraphSourceFilter);
exit;
end;
// locate next graph
idx := m_Clips.IndexOf(self.m_itCurrent);
idx := idx + 1;
m_itCurrent := m_Clips.Items[idx];
if (m_pPlayNext <> nil) then
begin
// jump to specified clip
m_itCurrent := m_pPlayNext;
m_pPlayNext := nil;
end;
// has this graph been rewound?
m_itCurrent.Prime;
// now we are about to connect it, so as soon as data is delivered out of the graph it
// is no longer primed for re-use (it will need a new seek before using again)
m_itCurrent.InUse();
// reconnect
m_pController.BridgeGraphs(m_itCurrent.SinkFilter, m_pRenderGraphSourceFilter);
if (m_bLoop and (m_itCurrent <> ClipOld)) then
begin
// need to rewind clip for next time round
// unless we are reusing it immediately (ie looping single clip)
// it's better to do this when not in use
ClipOld.Prime(0);
end;
end;
procedure TClipPlayer.OnEvent();
var
lEvent, l1, l2: longint;
pME: IMediaEventEx;
begin
(FFilterGraph as IGraphBuilder).QueryInterface(IID_IMediaEventEx, pME);
if (pME <> nil) then
begin
while (pME.GetEvent(lEvent, l1, l2, 0) = S_OK) do
begin
case (lEvent) of
EC_COMPLETE: Stop;
EC_VIDEO_SIZE_CHANGED: ResizeWindowToVideo;
end;
pME.FreeEventParams(lEvent, l1, l2);
end;
end;
end;
function TClipPlayer.TotalDuration(): REFERENCE_TIME;
begin
result := m_tDuration;
end;
function TClipPlayer.IsCued(): boolean;
var
fs: TFilterState;
hr: HRESULT;
pmc: IMEdiaControl;
begin
(FFilterGraph as IGraphBuilder).QueryInterface(IID_IMediaControl, pMC);
if (pMC <> nil) then
begin
hr := pMC.GetState(0, fs);
if (hr = VFW_S_STATE_INTERMEDIATE) then
begin
result := false;
exit;
end;
end;
result := true;
end;
function TClipPlayer.SetClipLimits(pClip: TClipEntry; tStart, tEnd: REFERENCE_TIME): HRESULT;
var
tDur: REFERENCE_TIME;
hr: HRESULT;
bFound: boolean;
pThis: TClipEntry;
i: integer;
begin
tDur := pClip.Duration();
hr := pClip.SetLimits(tStart, tEnd);
if (FAILED(hr) or (tDur = pClip.Duration())) then
begin
result := hr;
exit;
end;
// this is called from the same message loop as the end-of-segment processing, so it's safe to
// access the current segment
if (pClip = CurrentClip()) then
begin
// send just the stop time to the graph
// in the hope that it is not too late
hr := pClip.SetStopTime();
end;
// clip duration has changed: update start position of
// all subsequent clips (for current position slider UI)
m_tDuration := 0;
bFound := false;
for i := 0 to m_Clips.Count - 1 do
begin
pThis := m_Clips.items[i];
if (pThis = pClip) then
begin
bFound := true;
m_tDuration := pClip.GetStartPosition() + pClip.Duration();
end else if (bFound) then
begin
// following clip: adjust
pClip.SetStartPosition(m_tDuration);
m_tDuration := m_tDuration + pClip.Duration();
end;
end;
result := S_OK;
end;
function TClipPlayer.GetClipByIndex(idx: integer): TClipEntry;
begin
if (idx <= m_Clips.Count - 1) then
result := m_Clips.Items[idx]
else
result := nil;
end;
procedure TClipPlayer.PlayNext(pClip: TClipEntry);
begin
m_pPlayNext := pClip;
end;
function TClipPlayer.CurrentPosition(): REFERENCE_TIME;
var
dTime: double;
tNow: REFERENCE_TIME;
ClipTmp: TClipEntry;
begin
if (m_itCurrent = nil) or (not m_bActive) then
begin
result := m_tStartPosition;
exit;
end;
dTime := 0;
dTime := m_pController.GetSegmentTime();
tNow := round(dTime * 10000000);
// this is relative to the start position within this particular clip.
// Did we start at the beginning of this clip?
if (m_tStartPosition > m_itCurrent.GetStartPosition()) then
// no, we started some distance into the clip
tNow := tNow + (m_tStartPosition - m_itCurrent.GetStartPosition());
// offset from start of this clip to start of entire sequence
tNow := tNow + m_itCurrent.GetStartPosition();
if ((tNow < 0) and m_bLoop) then
begin
// current time is near end of previous loop
ClipTmp := m_Clips.Items[m_Clips.count - 1];
// list<ClipEntry>::iterator it = m_Clips.end();
// it--;
tNow := tNow + ClipTmp.GetStartPosition() + ClipTmp.Duration();
end;
result := tNow;
end;
function TClipPlayer.SetPosition(tStart: REFERENCE_TIME): HRESULT;
var
fs: FILTER_STATE;
pMC: IMediaControl;
bRunning: boolean;
begin
m_tStartPosition := tStart;
(FFilterGraph as IGraphBuilder).QueryInterface(IID_IMediaControl, pMC);
if (pMC = nil) then
begin
result := E_FAIL;
exit;
end;
pMC.GetState(0, fs);
bRunning := false;
if (fs = State_Running) then
begin
pMC.Pause;
bRunning := true;
end
else if (fs = State_Stopped) then
begin
// on going active, we'll seek all graphs
result := S_OK;
exit;
end;
result := SeekSourceGraphs();
if (bRunning) then
pMC.Run;
end;
function TClipPlayer.CurrentClip(): TClipEntry;
begin
{
if (m_itCurrent == m_Clips.end())
return NULL;
}
result := m_itCurrent;
end;
procedure TClipPlayer.UpdateDuration();
var
i: integer;
clipTmp: TClipEntry;
begin
// loop through all clips setting position and calculating duration
m_tDuration := 0;
for i := 0 to m_Clips.Count - 1 do
begin
clipTmp := m_Clips.items[i];
clipTmp.SetStartPosition(m_tDuration);
m_tDuration := m_tDuration + clipTmp.Duration;
end;
end;
procedure TClipPlayer.SetLoop(bLoop: boolean);
begin
m_bLoop := bLoop;
end;
function TClipPlayer.IsLooping(): boolean;
begin
result := m_bLoop;
end;
function TClipPlayer.SeekSourceGraphs(): HRESULT;
var
tStartThis, tOffset: REFERENCE_TIME;
hr: HRESULT;
i: integer;
ClipTmp: TClipEntry;
begin
// disconnect first
m_pController.BridgeGraphs(nil, nil);
// now seek all graphs and activate them
tStartThis := 0;
hr := E_INVALIDARG;
m_itCurrent := m_Clips.Items[0];
// list<ClipEntry>::iterator it;
for i := 0 to m_Clips.count - 1 do
// for (it = m_Clips.begin(); it != m_Clips.end(); it++)
begin
ClipTmp := m_Clips.items[i];
// all clips at or after start: seek to correct position
if ((tStartThis + ClipTmp.Duration()) <= m_tStartPosition) then
begin
// whole clip is before beginning point - not required
// but must rewind if we are in loop mode
if (m_bLoop) then
begin
ClipTmp.Prime(0);
end
end else
begin
// included in clip -- set start position
tOffset := 0;
if (m_tStartPosition >= tStartThis) then
begin
// starts someway in (this is first clip in sequence)
m_itCurrent := ClipTmp;
tOffset := m_tStartPosition - tStartThis;
end;
ClipTmp.Prime(tOffset);
end;
tStartThis := tStartThis + ClipTmp.Duration();
end;
// bridge the correct graph
// note: once connected, this graph is no longer primed (it has output some data
// and will need rewinding before re-use)
m_itCurrent.InUse();
try
m_pController.BridgeGraphs(m_itCurrent.SinkFilter(), m_pRenderGraphSourceFilter);
except
// to do get hr;
end;
result := hr;
end;
procedure TClipPlayer.ResizeWindowToVideo();
var
pVW: IVideoWindow;
pBV: IBasicVideo;
cx, cy, style, cxWindow, cyWindow: longint;
rcVideo, rc: TRECT;
begin
(FFilterGraph as IGraphBuilder).QueryInterface(IID_IVideoWindow, pVW);
(FFilterGraph as IGraphBuilder).QueryInterface(IID_IBasicVideo, pBV);
if ((pVW <> nil) and (pBV <> nil)) then
begin
// size of new video
pBV.GetVideoSize(cx, cy);
rcVideo := Rect(0, 0, cx, cy);
// adjust from client size to window size
pVW.get_WindowStyle(style);
AdjustWindowRect(rcVideo, style, false);
// get current window top/left
pVW.GetWindowPosition(rc.left, rc.top, cxWindow, cyWindow);
// reposition video window with old top/left position and new size
pVW.SetWindowPosition(rc.left, rc.top, rcVideo.right - rcVideo.left, rcVideo.bottom - rcVideo.top);
end;
end;
function TClipPlayer.GetState(): FILTER_STATE;
var
pMC: IMediaControl;
state: FILTER_STATE;
begin
state := State_Stopped;
(FFilterGraph as IGraphBuilder).QueryInterface(IID_IMediaControl, pMC);
if (pMC <> nil) then
pMC.GetState(0, state);
result := state;
end;
constructor TClipPlayer.Create( hwnd: THandle; msgSegment, msgEvent: word);
begin
m_bLoop:= False;
m_tDuration:= 0;
m_tStartPosition:= 0;
m_bActive:= false;
m_hwndApp:= hwnd;
m_msgEvent:= msgEvent;
m_msgSegment:= msgSegment;
m_pPlayNext:= NIL;
self.m_Clips:= TList.Create;
FFilterGraph := TFilterGraph.Create(nil);
FFIlterGraph.Active := true;
FFilterGraph.ClearGraph;
m_pController:= TGMFBridgeController.Create(nil).DefaultInterface;
m_pController.SetNotify(m_HwndApp, msgSegment);
// we use a video and an audio stream,
// options:
//don't allow compressed in source graphs,
//don't discard when not connected
m_pController.AddStream(1, eUncompressed, 0);
m_pController.AddStream(0, eUncompressed, 0);
// increase buffering at the join, so that audio does not run out
m_pController.SetBufferMinimum(200);
end;
Destructor TClipPlayer.Destroy;
var
i: integer;
begin
FFilterGraph.Free;
for i:= m_Clips.Count-1 downto 0 do
TClipEntry(m_Clips.Items[i]).Destroy;
m_Clips.Clear;
m_Clips.Free;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -