Events are useful tool for monitoring current states of the SDK objects. They are risen every time when something occurs with the monitored SDK object that you operate. There are 2 types of events in the Video SDK:
- OnFrame (OnFrameSafe) - raises on each processed frame by the SDK object. Available only in MPlatform SDK. With MFormats SDK you control each frame separately, so there is no need for such an event.
- OnEvent (OnEventSafe) - raises when each significant event happens with an object.
Difference between -Safe events and usual events
The difference between OnEventSafe and OnFrameSafe events is noticeable only with .Net-based languages, e.g. C# or VB.NET. The -Safe events were implemented to avoid objects handles leakage specific to the way .NET technology works with COM objects.
For Delphi and C++ you should use just OnEvent and OnFrame events through IMCallback interfaces. You can read more about releasing memory in this article - Releasing the SDK objects properly to avoid possible memory overflow.
How to implement events for different languages?
Firstly, it's needed to decide whether you are going to develop a window application or not. We will look at an example of using the events using an instance of MPlaylistClass. Depending on your decision, you must correctly configure the object to be monitored. Otherwise, events will not be called. This is done by setting the value of this prop ("true" for a window app, "false" for the app without window / GUI):
m_objPlaylist.PropsSet("object::events.use_window", "true");
Secondly, if you are planning to work with the object's frames via OnFrameSafe event and modify their data, it's needed to set the following 2 props as "true". More information about the functionality of these props is described in this article - Get a frame and modify its data. Note, that "object::on_frame.sync" and "object::on_frame.data" props should be set as "true" only for code based on MPlatform SDK. With MFormats SDK, you should do all the modifications before you call the .ReceiverFramePut() method.
m_objPlaylist.PropsSet("object::on_frame.sync", "true");
m_objPlaylist.PropsSet("object::on_frame.data", "true");
Thereafter, we can attach events to methods.
C#
Firstly, set OnFrame and OnEvent related props as "true" as it is described above:
m_objPlaylist.PropsSet("object::events.use_window", "true");
m_objPlaylist.PropsSet("object::on_frame.sync", "true");
m_objPlaylist.PropsSet("object::on_frame.data", "true");
C# (Standard approach #1):
// Code within Form_Load method:
m_objPlaylist.OnFrameSafe += new MPLATFORMLib.IMEventsFrame_OnFrameSafeEventHandler(M_objPlaylist_OnFrameSafe);
m_objPlaylist.OnEventSafe += new MPLATFORMLib.IMEventsEvent_OnEventSafeEventHandler(M_objPlaylist_OnEventSafe);
// Code withing program class:
// Invoked every object frame:
private void M_objPlaylist_OnFrameSafe(String bsChannelID, Object pMFrame)
{
Marshal.ReleaseComObject(pMFrame);
}
// Invoked every object event:
private void M_objPlaylist_OnEventSafe(String bsChannelID, String bsEventName, String bsEventParam, Object pEventObject)
{
Marshal.ReleaseComObject(pEventObject);
}
C# (Standard approach #2):
// Code within Form_Load method:
m_objPlaylist.OnFrameSafe += M_objPlaylist_OnFrameSafe;
m_objPlaylist.OnEventSafe += M_objPlaylist_OnEventSafe;
// Code withing program class:
// Invoked every object frame:
private void M_objPlaylist_OnFrameSafe(String bsChannelID, Object pMFrame)
{
Marshal.ReleaseComObject(pMFrame);
}
// Invoked every object event:
private void M_objPlaylist_OnEventSafe(String bsChannelID, String bsEventName, String bsEventParam, Object pEventObject)
{
Marshal.ReleaseComObject(pEventObject);
}
C# (Approach with anonymous methods):
// Code within Form_Load method:
m_objPlaylist.OnFrameSafe += (String bsChannelID, Object pMFrame) =>
{
Marshal.ReleaseComObject(pMFrame);
};
m_objPlaylist.OnEventSafe += (String bsChannelID, String bsEventName, String bsEventParam, Object pEventObject) =>
{
Marshal.ReleaseComObject(pEventObject);
};
C# (Approach with Actions):
// Code within Form_Load method:
Action<String, Object> OnFrameAction = new Action<String, Object>((bsChannelID, pMFrame) =>
{
Marshal.ReleaseComObject(pMFrame);
});
Action<String, String, String, Object> OnEventAction = new Action<String, String, String, Object>((bsChannelID, bsEventName, bsEventParam, pEventObject) =>
{
Marshal.ReleaseComObject(pEventObject);
});
IMEventsFrame_OnFrameSafeEventHandler OnFrameDelegate = new MPLATFORMLib.IMEventsFrame_OnFrameSafeEventHandler(OnFrameAction);
IMEventsEvent_OnEventSafeEventHandler OnEventDelegate = new MPLATFORMLib.IMEventsEvent_OnEventSafeEventHandler(OnEventAction);
m_objPlaylist.OnFrameSafe += OnFrameDelegate;
m_objPlaylist.OnEventSafe += OnEventDelegate;
VB.NET
Firstly, set OnFrame and OnEvent related props as "true" as it is described above:
m_objPlaylist.PropsSet("object::events.use_window", "true")
m_objPlaylist.PropsSet("object::on_frame.sync", "true")
m_objPlaylist.PropsSet("object::on_frame.data", "true")
VB.NET (Standard approach):
// Code within Form_Load method:
AddHandler m_objPlaylist.OnFrame, New IMEvents_OnFrameEventHandler(AddressOf m_objPlaylist_OnFrame)
AddHandler m_objPlaylist.OnEvent, New IMEvents_OnEventEventHandler(AddressOf m_objPlaylist_OnEvent)
// Code withing program class:
// Invoked every object frame:
Private Sub m_objPlaylist_OnFrame(bsChannelID As String, pMFrame As Object)
Marshal.ReleaseComObject(pMFrame)
End Sub
// Invoked every object event:
Private Sub m_objPlaylist_OnEvent(bsChannelID As String, bsEventName As String, bsEventParam As String, pEventObject As Object)
Marshal.ReleaseComObject(pEventObject)
End Sub
VB.NET (Approach with lambda expressions):
AddHandler m_objPlaylist.OnFrame,
Function(bsChannelID As String, pMFrame As Object)
Marshal.ReleaseComObject(pMFrame)
End Function
AddHandler m_objPlaylist.OnEvent,
Function(bsChannelID As String, bsEventName As String, bsEventParam As String, pEventObject As Object)
Marshal.ReleaseComObject(pEventObject)
End Function
C++
Firstly, set OnFrame and OnEvent related props as "true" as it is described above:
CComQIPtr<IMProps> qpProps(cpPlaylist);
if (qpProps)
{
hr = qpProps->PropsSet(BSTR(L"object::on_frame.sync"), BSTR(L"true"));
hr = qpProps->PropsSet(BSTR(L"object::on_frame.data"), BSTR(L"true"));
hr = qpProps->PropsSet(BSTR(L"object::events.use_window"), BSTR(L"true"));
if (FAILED(hr))
{
_tprintf(_T("ERROR: Can't set the prop \n"));
return E_FAIL;
}
}
Secondly, define the signature of the methods that will be invoked by the events:
static HRESULT OnEvent(LONGLONG lCallbackCookie, BSTR bsChannelID, BSTR bsEventName, BSTR bsEventParam, IUnknown* pEventObject); static HRESULT OnFrame(LONGLONG llCallbackUserData, BSTR bsChannelID, IUnknown* pMFrame);
Then, define the methods themselves:
HRESULT OnEvent(LONGLONG lCallbackCookie, BSTR bsChannelID, BSTR bsEventName, BSTR bsEventParam, IUnknown* pEventObject)
{
return S_OK;
}
HRESULT OnFrame(LONGLONG llCallbackUserData, BSTR bsChannelID, IUnknown* pMFrame)
{
return S_OK;
};
And attach the events to the methods:
CComQIPtr<IMObject> qpObject(cpPlaylist);
if (qpObject)
{
qpObject->ObjectCallbackSetFunc((LONG_PTR)OnEvent, (LONG_PTR)OnFrame, 0);
if (FAILED(hr))
{
_tprintf(_T("ERROR: Can't cast to IMObject \n"));
return E_FAIL;
}
}
Delphi
If you design your application in Delphi, firstly, it's needed to define a custom class TEvents for working with the events. Then declare one instance of the defined class (we name it as m_objEvents). After that, create the m_objEvents instance and start listening to the risen events via .ObjectCallbackSet() method. And the only things left - is to define the functions to be invoked by the events:
// 1. Define a custom class TEvents (Right after uses field of your app .pas file):
Type TEvents = class (TInterfacedObject, IMCallback)
function OnEvent(lCallbackCookie: Int64; const bsChannelID: WideString;
const bsEventName: WideString; const bsEventParam: WideString;
const pEventObject: IUnknown): HResult; stdcall;
function OnFrame(lCallbackCookie: Int64; const bsChannelID: WideString;
const pMFrame: IUnknown): HResult; stdcall;
end;
// 2. Declare the objects you will monitor:
private
m_objPlaylist : IMPlaylist;
m_pObject: IMObject;
// 3. Declare a variable in the "var" section of .FormCreate(Sender: TObject); procedure:
m_objEvents: TEvents;
// 4. Create an instance of TEvents class within .FormCreate(Sender: TObject); procedure:
m_objEvents := TEvents.Create();
// 5. Start listening these events with ObjectCallbackSet method:
m_pObject := m_objPlaylist as IMObject;
m_pObject.ObjectCallbackSet(m_objEvents, cookie);
// 6. Create implementation of the IMCallback.OnEvent and IMCallback.OnFrame functions within "implementation" section:
// OnFrame function:
function TEvents.OnFrame(lCallbackCookie: Int64; const bsChannelID: WideString;
const pMFrame: IUnknown): HResult; stdcall;
begin
end;
// On Event function:
function TEvents.OnEvent(lCallbackCookie: Int64; const bsChannelID: WideString;
const bsEventName: WideString; const bsEventParam: WideString;
const pEventObject: IUnknown): HResult; stdcall;
begin
end;
And do not forget to set all the event related properties to work with the frames' data:
(m_objPlaylist as IMProps).PropsSet('object::events.use_window', 'true');
(m_objPlaylist as IMProps).PropsSet('object::on_frame.sync', 'true');
(m_objPlaylist as IMProps).PropsSet('object::on_frame.data', 'true');
In addition, please note that you should use the IMFObject to set callback both in the MPlatform and MFormats for HTML plugin in Delphi.
m_objOverlayHTML := CreateComObject(CLASS_MFOverlayHTML) as IMFBrowser;
(m_objOverlayHTML as IMFObject).MFCallbackSet(m_objEvents, cookie);
Windows Service events
Firstly, set "object::events.use_window" prop as "false" and "object::on_frame.sync" and "object::on_frame.data" props as "true" as it is described above:
m_objFile.PropsSet("object::events.use_window", "false");
m_objFile.PropsSet("object::on_frame.sync", "true");
m_objFile.PropsSet("object::on_frame.data", "true");
In new versions of SDK, the implementation of events for C# is exactly the same as for regular C# applications. Just don't forget to set "object::events.use_window" prop as "false" since windows services have no windows.
And the code itself (for old version of the SDKs):
protected override void OnStart(string[] args)
{
// ...
OnEvent_as_Callback();
OnFrame_as_Callback();
}
delegate void CallbackDelegateFrame(Int64 llCallbackUserData, Int32 bsChannelID, Int32 pFrame);
delegate void CallbackDelegateEvent(Int64 llCallbackUserData, Int32 bsChannelID, Int32 bsEventName, Int32 bsEventParam, Int32 pEventObject);
GCHandle gchCallbackDelegateOnFrame;
GCHandle gchCallbackDelegateOnEvent;
void OnFrame_as_Callback()
{
File.AppendAllText(@"D:\Frame.txt", "OnFrame_as_Callback starting..." + Environment.NewLine);
CallbackDelegateFrame callbackDelegateFrame = TheCallbackFrame;
IntPtr callbackDelegatePtr = Marshal.GetFunctionPointerForDelegate(callbackDelegateFrame);
IntPtr callbackThisPtr = (IntPtr)GCHandle.Alloc(this);
GC.Collect();
gchCallbackDelegateOnFrame = GCHandle.Alloc(callbackDelegateFrame);
m_objFile.ObjectCallbackSetFunc(0, callbackDelegatePtr.ToInt64(), callbackThisPtr.ToInt64());
GC.Collect();
File.AppendAllText(@"D:\Frame.txt", "OnFrame_as_Callback done." + Environment.NewLine);
}
public void TheCallbackFrame(Int64 llCallbackUserData, Int32 bsChannelID, Int32 pFrame32)
{
// Convert this pointer
Object pThis = ((GCHandle)new IntPtr(llCallbackUserData)).Target;
// Convert string
String strChannel = Marshal.PtrToStringBSTR(new IntPtr(bsChannelID));
// Convert pFrame
IMFFrame pMFrame = (IMFFrame)Marshal.GetObjectForIUnknown(new IntPtr(pFrame32));
// Check frame
pMFrame.MFTimeGet(out M_TIME mTime);
File.AppendAllText(@"D:\Frame.txt", "OnFrame!!! " + pThis + " " + strChannel + " " + mTime.tcFrame.nExtraCounter + Environment.NewLine);
}
void OnEvent_as_Callback()
{
File.AppendAllText(@"D:\Event.txt", "OnEvent_as_Callback Started" + Environment.NewLine);
CallbackDelegateEvent callbackDelegate = TheCallbackEvent;
IntPtr callbackDelegatePointer = Marshal.GetFunctionPointerForDelegate(callbackDelegate);
GC.Collect();
gchCallbackDelegateOnEvent = GCHandle.Alloc(callbackDelegate);
m_objFile.ObjectCallbackSetFunc(callbackDelegatePointer.ToInt64(), 0, 0);
GC.Collect();
File.AppendAllText(@"D:\Event.txt", "OnEvent_as_Callback Stopped." + Environment.NewLine);
}
public void TheCallbackEvent(Int64 llCallbackUserData, Int32 bsChannelID, Int32 bsEventName, Int32 bsEventParam, Int32 pEventObject)
{
// Convert this pointer
Object pThis = ((GCHandle)new IntPtr(llCallbackUserData)).Target;
// Convert string
String strChannel = Marshal.PtrToStringBSTR(new IntPtr(bsChannelID));
// Convert string
String strEventName = Marshal.PtrToStringBSTR(new IntPtr(bsEventName));
// Convert string
String strEventParam = Marshal.PtrToStringBSTR(new IntPtr(bsEventParam));
File.AppendAllText(@"D:\Event.txt", "OnEvent!!! " + llCallbackUserData + " " + strChannel + " " + strEventName + " " + strEventParam + Environment.NewLine);
}
List of the available events for the SDK objects
MFile
The bsChannelID shows a path to the currently initialized file.
- "start" - raises the moment playback is started.
bsEventParam:
"" - simple start of playback.
"loop" - restart of the file if it is in a "loop" state. - "pause" - raises the time you set the object in pause state by the FilePlayPause method.
bsEventParam is a duration of the pause. - "EOF" - raises when the file reaches its end.
bsEventParam:
"stop" - the file stops at the end.
"loop" - the file is in "loop" state. - "stop" - raises the time you set the object in stop state by the FilePlayStop method.
bsEventParam is a duration of the stop. - "connect" - raises at the moment a network stream is connected or disconnected.
bsEventParam:
"false" - the stream is disconnected.
"true" - the stream is re-connected. - "watch-dog" - raises the moment a watch-dog triggers some event.
bsEventParam:
"object-locked"
"error-access"
"file-freeze"
"file-failed"
"update"
"update-failed" - "SCTE35" - raises the moment an SCTE-35 trigger is received. As bsEventParam it uses a parsed SCTE-35 message string.
- "SCTE104" - raises the moment an SCTE-104 trigger is received. As bsEventParam it uses a parsed SCTE-104 message string.
- "connect" - raises when the stream connected/diconnected.
bsEventParam:
"true" - stream connected to the object.
"false" - stream disconnected.
MLive & MFLive
- "no_signal" - raises when a source signal is lost ("No Video Signal" message appears).
Possible bsEventParam values: true (the signal is lost) and false (the signal is restored). - "format_changed" - raises the moment a source video format is changed. bsEventParam shows the name of a new video format.
- "SCTE35" - raises the moment an SCTE-35 trigger is received. As bsEventParam it uses a parsed SCTE-35 message string.
- "SCTE104" - raises the moment an SCTE-104 trigger is received. As bsEventParam it uses a parsed SCTE-104 message string.
MPlaylist
- "switch" - raises the moment playlist switches from one item to another.
bsEventParam:
"from" - indicates a previous item (a path to a file or an object name) as the bsChannels parameter.
"to" - indicates the item (a path to a file or an object name) as the bsChannels parameter. - "playlist-command" - raises the moment an MPlaylist executes a command. Indicates a command as the bsChannels parameter.
- "playlist_add" - raises the moment you add a new item with the PlaylistAdd method. Shows a name of the MPlaylist object as the bsChannels parameter.
bsEventParam is a target index of a new item. - "playlist_remove" - raises the moment you remove an item with the PlaylistRemove or the PlaylistRemoveByIndex methods. Shows a name of the MPlaylist object as the bsChannels parameter.
bsEventParam is a target index of a removed item. - "EOL" - raises the moment the playlist is over. Shows a name of the MPlaylist object as the bsChannels parameter.
- "pause" - raises the time you set the object in pause state by the FilePlayPause method.
bsEventParam is a duration of the pause. - "start" - raises the moment the object is started with the ObjectStart method.
- "stop" - raises the time you set the object in stop state by the FilePlayStop method.
bsEventParam is:
a duration of the stop
"stop-done" - raises when stop time is over and playback is resumed.
"error-OutOfMemory" - raises when the playlist is stopped because of an OutOfMemory exception. - "EOF" - raises the moment an item is over.
- "break-command" - raises the moment a break command is executed.
bsEventParam is:
a position of a break
"error-execute" - indicates that command can't be executed. - "break-start" - raises the moment a break is started.
bsEventParam is:
a position of a break
"error-start" - indicates that a break can't be started. - "break-done" - raises the moment a break is over.
- "SCTE35" - raises the moment an SCTE-35 trigger is received. As bsEventParam it uses a parsed SCTE-35 message string.
- "connect" - raises when the stream connected/diconnected.
bsEventParam:
"true" - stream connected to the object.
"false" - stream disconnected.
MWriter
bsChannelID is the name of an object.
- "file-close" - raises the moment when the encoded file is available for usage by the system. Available for both MWriter and MFWriter object.
bsEventParam is a path to the encoded file. - "error-abort" - raises on any error during recorder. Available for MFWriter only.
bsEventParam can be "disk-full" (raises when the target disk is full) or an error code. - "switch" - raises the moment you switch from one destination to another.
bsEventParam is a path to the encoded file. - "start" - raises the moment the encoding is started.
bsEventParam is a path to the encoded file. - "stop" - raises the moment you stop the encoding.
bsEventParam is a path to the encoded file. - "file-close-info" - raises the moment when the encoded file is available for usage by the system.
bsEventParam is information about a file. - "disk-full" - raises when the target disk is full.
- "end-of-media" - raises when the end of a source is reached.
- "max-switch" - raises when the maximal amount of switches is reached.
MFWriter
- "file-close" - raises the moment when the encoded file is available for usage by the system. Available for both MWriter and MFWriter object.
bsEventParam is a path to the encoded file. - "error-abort" - raises on any error during recorder. Available for MFWriter only.
bsEventParam can be "disk-full" (raises when the target disk is full) or an error code.
bsChannelID is the name of an object.
MMixer
- "scene_active_set" - raises the moment you set an active scene.
bsEventParam indicates whether the active scene is set ("true", "false"). - "pause" - raises the time you set the object in pause state by the FilePlayPause method.
bsEventParam is:
duration of the pause.
"pause-done" - indicates that the pause is over
"pause-start" - indicates that the pause is started - "start" - raises the moment you start the object with the ObjectStart method.
- "stop" - raises the moment you set the object in stop state by the FilePlayStop method.
bsEventParam is a duration of the stop. - "source_disconnected" - raises when a stream is disconnected.
- "source_connected" - raises when a stream is connected back.
- "disconnect" - raises when you remove a stream with the StreamsRemove method.
- "connect" - raises when you add a stream with the StreamsAdd method.
MPreview
- "wpf_nextframe" - raises on each received frame in wpf_preview=true mode. See the article about WPF Preview for more details.