⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 player.pas

📁 delphi版的无缝播放器
💻 PAS
📖 第 1 页 / 共 2 页
字号:
	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 + -