📄 player.~pas
字号:
// Translate from GMFPlay(Demo of the GMFBridge). Done by Zhang Jianyu 2006-05-05
// http://www.panensoft.com
// email: admin@panensoft.com
//
// Sample DirectShow player that uses
// GDCL Multigraph Framework to play a sequence of
// multiple clips
//
// Copyright (c) GDCL 2004. All Rights Reserved.
// You are free to re-use this as the basis for your own filter development,
// provided you retain this copyright notice in the source.
// http://www.gdcl.co.uk
unit Player;
interface
uses
DirectShow9, Dspack, SysUtils, GMFBridgeLib_TLB, Classes, ActiveX, Windows, Messages,ComObj;
const
MSG_ENDOFSEGMENT = WM_USER+100;
MSG_DSEVENT = WM_USER+101;
type
// TMSG_ENDOFSEGMENT
// TMSG_DSEVENT
TClipEntry = class(TObject)
private
m_pGraph: IGraphBuilder;
m_pSinkFilter: IUnknown;
m_strName: string;
m_tOffset, m_tStart, m_tStop: REFERENCE_TIME;
FGraphEditID : Integer;
public
// rewound ready to re-use
m_bPrimed: boolean;
constructor Create;
destructor Destroy;
function CreateClip(pController: IGMFBridgeController; path: string): HRESULT;
function SetLimits(tStart, tStop: REFERENCE_TIME): HRESULT;
procedure GetLimits(var ptStart, ptStop: REFERENCE_TIME);
function Duration: REFERENCE_TIME;
function CurrentPosition: REFERENCE_TIME;
function Graph: IUnknown;
function SinkFilter: IUnknown;
function Name: string;
procedure SetStartPosition(tOffset: REFERENCE_TIME);
function GetStartPosition: REFERENCE_TIME;
function SetPosition(tStart: REFERENCE_TIME): HRESULT;
function IsPrimed: boolean;
procedure InUse;
// set position and run
procedure Prime(tStart: REFERENCE_TIME = 0);
procedure Stop;
// returns whole file duration even when limits applied
function NativeDuration: REFERENCE_TIME;
// updates the graph stop time to reflect the limits (for active clip)
function SetStopTime: HRESULT;
end;
TClipPlayer = class(TObject)
public
// m_pRenderGraph: IGraphBuilder;
FFilterGraph: TFilterGraph;
constructor Create( hwnd: THandle; msgSegment, msgEvent: word);
Destructor Destroy;
{
ClipPlayer(HWND hwnd, UINT msgSegment, UINT msgEvent);
~ClipPlayer();
}
function AddClip(path: string; Clip: TClipEntry): HRESULT;
function Play: HRESULT;
function Pause: HRESULT;
function Stop: HRESULT;
procedure OnEndOfSegment;
procedure OnEvent;
function TotalDuration: REFERENCE_TIME;
function IsCued: boolean;
function SetClipLimits(pClip: TClipEntry; tStart, tEnd: REFERENCE_TIME): HRESULT;
function GetClipByIndex(idx: integer): TClipEntry;
procedure PlayNext(pClip: TClipEntry);
function CurrentPosition: REFERENCE_TIME;
function SetPosition(tStart: REFERENCE_TIME): HRESULT;
function CurrentClip: TClipEntry;
procedure UpdateDuration;
procedure SetLoop(bLoop: boolean);
function IsLooping: boolean;
private
m_pController: IGMFBridgeController;
m_Clips: TList; //list<ClipEntry> ;
m_itCurrent: TClipEntry;
//? list<ClipEntry>::iterator m_itCurrent;
m_pRenderGraphSourceFilter: IUnknown;
m_tDuration, m_tStartPosition: REFERENCE_TIME;
m_bLoop, m_bActive: boolean;
m_hwndApp: Thandle;
m_msgSegment: Word;
m_msgEvent: Word;
m_pPlayNext: TClipEntry;
function SeekSourceGraphs(): HRESULT;
procedure ResizeWindowToVideo();
function GetState(): FILTER_STATE;
end;
implementation
//----------------------------------------------------------------------------
// Enable Graphedit to connect with your filter graph
//----------------------------------------------------------------------------
function AddGraphToRot(Graph: IFilterGraph; out ID: integer): HRESULT;
var
Moniker: IMoniker;
ROT : IRunningObjectTable;
wsz : WideString;
begin
result := GetRunningObjectTable(0, ROT);
if (result <> S_OK) then exit;
wsz := format('FilterGraph %p pid %x',[pointer(graph),GetCurrentProcessId()]);
result := CreateItemMoniker('!', PWideChar(wsz), Moniker);
if (result <> S_OK) then exit;
result := ROT.Register(0, Graph, Moniker, ID);
Moniker := nil;
end;
//----------------------------------------------------------------------------
// Disable Graphedit to connect with your filter graph
//----------------------------------------------------------------------------
function RemoveGraphFromRot(ID: integer): HRESULT;
var ROT: IRunningObjectTable;
begin
result := GetRunningObjectTable(0, ROT);
if (result <> S_OK) then exit;
result := ROT.Revoke(ID);
ROT := nil;
end;
constructor TClipEntry.Create;
begin
m_tOffset := 0;
m_tStart := 0;
m_tStop := 0;
m_bPrimed := false;
end;
destructor TClipEntry.Destroy;
var
pMC: IMediaControl;
begin
if (assigned(m_pGraph)) then
m_pGraph.QueryInterface(IID_IMediaControl, pmc);
if (assigned(pMC)) then
pMC.Stop;
RemoveGraphFromRot(FGraphEditID);
end;
function TClipEntry.CreateClip(pController: IGMFBridgeController; path: string): HRESULT;
begin
m_bPrimed := false;
result := CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC_SERVER,
IID_IFilterGraph, m_pGraph);
// old version of (GMFBridge)
// pController.CreateSourceGraph(StringToOleStr(path), m_pGraph, m_pSinkFilter);
AddGraphToRot(IFilterGraph2(m_pGraph), FGraphEditID);
try
m_pSinkFilter:= pController.CreateSourceGraph(StringToOleStr(path), m_pGraph);
result:= S_OK;
except
on e: EOleException do
begin
result:= EOleException(e).ErrorCode;
end;
end;
m_strName := path;
end;
function TClipEntry.SetLimits(tStart, tStop: REFERENCE_TIME): HRESULT;
begin
m_tStart := tStart;
m_tStop := tStop;
m_bPrimed := false;
result := S_OK;
end;
procedure TClipEntry.GetLimits(var ptStart, ptStop: REFERENCE_TIME);
begin
ptStart := m_tStart;
ptStop := m_tStop;
end;
function TClipEntry.Duration: REFERENCE_TIME;
var
tDur: REFERENCE_TIME;
pMS: IMediaSeeking;
hr: HRESULT;
begin
if (m_tStop = 0) then
begin
tDur := 0;
m_pGraph.QueryInterface(IID_IMediaSeeking, pMS);
hr := pMS.GetDuration(tDur);
if ((hr <> S_OK)) then
begin
result := 0;
exit;
end;
result := tDur - m_tStart;
exit;
end;
result := m_tStop - m_tStart;
end;
function TClipEntry.CurrentPosition: REFERENCE_TIME;
var
tNow: REFERENCE_TIME;
pMS: IMediaSeeking;
hr: HRESULT;
begin
tNow := 0;
m_pGraph.QueryInterface(IID_IMediaSeeking, pMS);
pMS.GetCurrentPosition(tNow);
result := tNow + m_tStart;
end;
function TClipEntry.Graph: IUnknown;
begin
result := self.m_pGraph;
end;
function TClipEntry.SinkFilter: IUnknown;
begin
result := self.m_pSinkFilter;
end;
function TClipEntry.Name: string;
begin
result := self.m_strName;
end;
procedure TClipEntry.SetStartPosition(tOffset: REFERENCE_TIME);
begin
m_tOffset := tOffset;
end;
function TClipEntry.GetStartPosition: REFERENCE_TIME;
begin
result := m_tOffset;
end;
function TClipEntry.SetPosition(tStart: REFERENCE_TIME): HRESULT;
var
hr: HRESULT;
pSeek: IMediaSeeking;
stop: Int64;
begin
self.m_pGraph.QueryInterface(IID_IMediaSeeking, pSeek);
hr := E_NOINTERFACE;
if (pSeek <> nil) then
begin
// input time is relative to clip start -- add on offset
// from start of media
tStart := tStart + m_tStart;
if (m_tStop = 0) then
begin
stop := 0;
hr := pSeek.SetPositions(
tStart,
AM_SEEKING_AbsolutePositioning,
stop,
AM_SEEKING_NoPositioning);
end
else
begin
hr := pSeek.SetPositions(
tStart,
AM_SEEKING_AbsolutePositioning,
m_tStop,
AM_SEEKING_AbsolutePositioning);
end
end;
result := hr;
end;
function TClipEntry.IsPrimed: boolean;
begin
result := self.m_bPrimed;
end;
procedure TClipEntry.InUse;
begin
m_bPrimed := False;
end;
// set position and run
procedure TClipEntry.Prime(tStart: REFERENCE_TIME = 0);
var
state: TFilterState;
pMC: IMediaControl;
begin
if (not m_bPrimed) then
begin
SetPosition(tStart);
Graph.QueryInterface(IID_IMediaControl, pMC);
pMC.GetState(0, state);
if (state <> State_Running) then
begin
pMC.Run;
end;
m_bPrimed := true;
end;
end;
procedure TClipEntry.Stop;
var
state: TFilterState;
pMC: IMediaControl;
begin
Graph.QueryInterface(IID_IMediaControl, pMC);
pMC.Stop;
m_bPrimed := false;
end;
// returns whole file duration even when limits applied
function TClipEntry.NativeDuration(): REFERENCE_TIME;
var
tDur: REFERENCE_TIME;
pMS: IMediaSeeking;
begin
tDur := 0;
m_pGraph.QueryInterface(IID_IMediaSeeking, pMS);
pMS.GetDuration(tDur);
result := tDur;
end;
// updates the graph stop time to reflect the limits (for active clip)
function TClipEntry.SetStopTime(): HRESULT;
var
pSeek: IMediaSeeking;
hr: HRESULT;
start: int64;
begin
m_pGraph.QueryInterface(IID_IMediaSeeking, pSeek);
hr := E_NOINTERFACE;
if (pSeek <> nil) then
begin
start := 0;
hr := pSeek.SetPositions(
start,
AM_SEEKING_NoPositioning,
m_tStop,
AM_SEEKING_AbsolutePositioning);
end;
result := hr;
end;
function TClipPlayer.AddClip(path: string; Clip: TClipEntry): HRESULT;
var
hr: HResult;
idx: integer;
// Clip: TClipEntry;
pME: IMediaEventEx;
begin
// list<ClipEntry>::iterator it = m_Clips.insert(m_Clips.end(), ClipEntry());
Clip := TClipEntry.Create;
self.m_Clips.Add(Clip);
// ClipEntry* pClip = &(*it);
// *ppClip = pClip;
hr := Clip.CreateClip(m_pController, path);
// HRESULT hr = pClip.Create(m_pController, path);
// if we expect both audio and video, then all clips
// must have both audio and video.
// If the first clip is video only, then switch
// to video-only automatically
if ((hr = VFW_E_UNSUPPORTED_AUDIO) and (m_Clips.Count = 1)) then
begin
// new controller, different options (only one video stream)
m_pController := TGMFBridgeController.Create(nil).DefaultInterface;
// m_pController.CreateInstance(__uuidof(GMFBridgeController));
m_pController.SetNotify((m_hwndApp), (m_msgSegment));
m_pController.AddStream(1, eUncompressed, 0);
m_pController.SetBufferMinimum(200);
// try again
hr := Clip.CreateClip(m_pController, path);
end;
if ((hr = s_OK)) then
begin
clip.SetStartPosition(m_tDuration);
m_tDuration := m_tDuration + Clip.Duration;
// if this is the first clip, create the render graph
if (m_Clips.Count = 1) then
begin
// hr := CoCreateInstance(CLSID_FilterGraph, nil, CLSCTX_INPROC_SERVER,
// IID_IFilterGraph, m_pRenderGraph);
// old version
// m_pController.CreateRenderGraph(Clip.SinkFilter(), m_pRenderGraph,m_pRenderGraphSourceFilter);
m_pRenderGraphSourceFilter:= m_pController.CreateRenderGraph(Clip.SinkFilter(), FFilterGraph as IGraphBuilder);
if ((hr = s_ok) and IsWindow(m_hwndApp)) then
begin
(FFilterGraph as IGraphBuilder).QueryInterface(IID_IMediaEventEx, pME);
if (pME <> nil) then
pME.SetNotifyWindow(OAHWND(m_hwndApp), m_msgEvent, 0);
end;
end;
end
else
begin
idx := m_Clips.IndexOf(Clip);
if (idx > -1) then
begin
m_Clips.Delete(idx);
end;
result := hr;
end;
if(m_Clips.Count>0) then
m_itCurrent:= m_Clips.items[0]
else
m_itCurrent:= nil;
end;
function TClipPlayer.Play: HRESULT;
var
hr: HRESULT;
pMC: IMediaControl;
begin
// make all graphs active
hr := S_OK;
if (not m_bActive) then
begin
UpdateDuration();
hr := Pause();
end;
if (m_itCurrent.Graph() = nil) then
hr := E_FAIL;
if (SUCCEEDED(hr)) then
begin
(FFilterGraph as IGraphBuilder).QueryInterface(IID_IMediaControl, pMC);
hr := pMC.Run;
end;
result := hr;
end;
function TClipPlayer.Pause: HRESULT;
var
hr: HRESULT;
pMC: IMediaControl;
i: integer;
begin
hr:= S_OK;
// make all graphs active
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -