Open playback from a camera in floating window after generate an alarm

I want to open a playback from a camera in a floating window, by now I only get the live view from a camera with the code every time an alarm pre-configured by me happens:

viewAndLayoutItem.InsertBuiltinViewItem(index++,

                              ViewAndLayoutItem.CameraBuiltinId,

                              new Dictionary<string, string>()

                              {

                                { "CameraId", alarm.EventHeader.Source.FQID.ObjectId.ToString() }

                              });

Is there any other method to open a playback from the same camera? The ideia is show side by side the live view and the playback, as we can see in a view when we use the Alarm List and Alarm Preview items:

Att…

João Pedro

The solution is not straight forward, the method to open a view will only start with the view in Live mode.

Afterwards you will then have to turn one of the camera view items into independent playback mode.

Independent playback mode is introduced in the Smart Client Independent Playback plugin sample.

https://doc.developer.milestonesys.com/html/index.html?base=samples/pluginsamples/scindependentplayback/readme.html&tree=tree_1.html

But I think it is more useful how independent playback is used in the Smart Client ImageViewerAddOn plugin sample.

https://doc.developer.milestonesys.com/html/index.html?base=samples/pluginsamples/scimagevieweraddonsample/readme.html&tree=tree_1.html

I am speculating that you can subscribe to the creation of new ImageViewerAddOns, and capture one where you then command playback..

I have not tried it out, if you try I would love to hear the outcome.

Hello Bo, how are you? Thanks for your help!

I’m follow your advise and create a Independent Playback, now I have a ViewItemPlugin, right?

So I trying to insert this ViewItemPlugin in a FloatingWindow, the code for create the FloatingWindow are below:

private Object CameraListReceiver(VideoOS.Platform.Messaging.Message message, FQID destination, FQID source)
 
    {
 
      List<Alarm> alarms = message.Data as List<Alarm>;
 
      if (alarms != null)
 
      {
 
        // Insert cameras
 
        int index = 0;
 
        foreach (Alarm alarm in alarms)
 
        {
 
          DataInfo dataJson;
 
          using (StreamReader r = new StreamReader("C:\\Users\\milestone\\Desktop\\web_pages.json"))
 
          {
 
            string jsonString = r.ReadToEnd();
 
            dataJson = JsonConvert.DeserializeObject<DataInfo>(jsonString);
 
          }
 
 
 
          int viewNumber = _viewCounter + 1;
 
          // Make a new Temporary view group
 
          ConfigItem tempGroupItem = (ConfigItem)ClientControl.Instance.CreateTemporaryGroupItem("Temporary" + viewNumber);
 
          // Make a group 
 
          ConfigItem groupItem = tempGroupItem.AddChild("DynamicViewGroup" + viewNumber, Kind.View, FolderType.UserDefined);
 
          // Build a layout with wide ViewItems at the top and buttom, and 5 small ones in the middle
 
          Rectangle[] rect = new Rectangle[4];
 
          rect[0] = new Rectangle(000, 000, 499, 499);
 
          rect[1] = new Rectangle(499, 000, 499, 499);
 
          rect[2] = new Rectangle(000, 499, 499, 499);
 
          rect[3] = new Rectangle(499, 499, 499, 499);
 
          //rect[4] = new Rectangle(599, 399, 199, 199);
 
          //rect[5] = new Rectangle(799, 399, 199, 199);
 
          //rect[6] = new Rectangle(000, 599, 999, 399);
 
          int capacity = rect.Length;
 
          string viewName = "DynamicView" + viewNumber;
 
          ViewAndLayoutItem viewAndLayoutItem = groupItem.AddChild(viewName, Kind.View, FolderType.No) as ViewAndLayoutItem;
 
          viewAndLayoutItem.Layout = rect;
 
 
 
          if (alarm.EventHeader.Source.FQID.ObjectId.ToString() == dataJson.CameraID) 
 
          {
 
            // Add camera from the triggering event
 
            // Note: EventHeader.Source cannot be null because an alarm must have a source
 
 
 
            if (index < capacity && alarm.EventHeader?.Source.FQID.Kind == Kind.Camera)
 
            {
 
              viewAndLayoutItem.InsertBuiltinViewItem(index++,
 
                                  ViewAndLayoutItem.CameraBuiltinId,
 
                                  new Dictionary<string, string>()
 
                                  {
 
                                    { "CameraId", alarm.EventHeader.Source.FQID.ObjectId.ToString() }
 
                                  });
 
            }
 
 
 
            if (alarm.ReferenceList == null)
 
              continue;
 
 
 
            // Add related cameras from the alarm
 
            foreach (Reference rel in alarm.ReferenceList)
 
            {
 
              if (index < capacity && rel.FQID.Kind == Kind.Camera)
 
              {
 
                //   PLAYBACK - TO DO INSERT PLAYBACK IN THE FLOATING WINDOW
 
                viewAndLayoutItem.InsertViewItemPlugin(index++, ViewItemPlugin.,
 
                                    new Dictionary<string, string>()
 
                                    {
 
                                      
 
                                    });
 
 
 
                viewAndLayoutItem.InsertBuiltinViewItem(index++,
 
                                    ViewAndLayoutItem.HTMLBuiltinId,
 
                                    new Dictionary<string, string>()
 
                                    {
 
                                      { "URL", $"{dataJson.URL}" },
 
                                      { "Scaling", "5"},
 
                                      { "HideNavigationBar", "true"},
 
                                      { "Addscript", "true"}
 
                                    });
 
 
 
                viewAndLayoutItem.InsertBuiltinViewItem(index++,
 
                                    ViewAndLayoutItem.TextBuiltInId,
 
                                    new Dictionary<string, string>()
 
                                    {
 
                                    { "Text", $"Metadata: \n" +
 
                                    $"Name: Joao \n" +
 
                                    $"Gender: Male \n" +
 
                                    $"Emotions: Happy\n" +
 
                                    $"CameraID: {alarm.EventHeader.Source.FQID.ObjectId}"
 
                                    }
 
                                    });
 
              }
 
            }
 
          }
 
          else
 
          {
 
            // Add camera from the triggering event
 
            // Note: EventHeader.Source cannot be null because an alarm must have a source
 
            if (index < capacity && alarm.EventHeader?.Source.FQID.Kind == Kind.Camera)
 
            {
 
              viewAndLayoutItem.InsertBuiltinViewItem(index++,
 
                                  ViewAndLayoutItem.CameraBuiltinId,
 
                                  new Dictionary<string, string>()
 
                                  {
 
                                  { "CameraId", alarm.EventHeader.Source.FQID.ObjectId.ToString() }
 
                                  });
 
            }
 
 
 
            if (alarm.ReferenceList == null)
 
              continue;
 
 
 
            // Add related cameras from the alarm
 
            foreach (Reference rel in alarm.ReferenceList)
 
            {
 
              if (index < capacity && rel.FQID.Kind == Kind.Camera)
 
              {
 
                viewAndLayoutItem.InsertBuiltinViewItem(index++,
 
                                    ViewAndLayoutItem.HTMLBuiltinId,
 
                                    new Dictionary<string, string>()
 
                                    {
 
                                    { "URL", "www.google.com" }
 
                                    });
 
 
 
                viewAndLayoutItem.InsertBuiltinViewItem(index++,
 
                                    ViewAndLayoutItem.HTMLBuiltinId,
 
                                    new Dictionary<string, string>()
 
                                    {
 
                                    { "URL", "www.milestonesys.com" }
 
                                    });
 
 
 
                viewAndLayoutItem.InsertBuiltinViewItem(index++,
 
                                    ViewAndLayoutItem.EmptyBuiltinId,
 
                                    new Dictionary<string, string>()
 
                                    { });
 
              }
 
            }
 
          }
 
 
 
          if (index == 0)
 
          {
 
            // Exit here, since no cameras were found in the alarm list
 
            return null;
 
          }
 
 
 
          viewAndLayoutItem.Save();
 
          tempGroupItem.PropertiesModified();
 
 
 
          Item screen = Configuration.Instance.GetItemsByKind(Kind.Screen).FirstOrDefault();
 
          Item window = Configuration.Instance.GetItemsByKind(Kind.Window).FirstOrDefault();
 
          Item view = groupItem.GetChildren().FirstOrDefault(v => v.Name.Equals(viewName));
 
          if (screen == null || window == null || view == null)
 
          {
 
            return null;
 
          }
 
 
 
          // Create floating window
 
          MultiWindowCommandData data = new MultiWindowCommandData();
 
          data.Screen = screen.FQID;
 
          data.Window = window.FQID;
 
          data.View = view.FQID;
 
          data.X = 200;
 
          data.Y = 200;
 
          data.Height = 500;
 
          data.Width = 500;
 
          data.MultiWindowCommand = "OpenFloatingWindow";
 
          data.PlaybackSupportedInFloatingWindow = true;
 
          EnvironmentManager.Instance.SendMessage(new VideoOS.Platform.Messaging.Message(MessageId.SmartClient.MultiWindowCommand, data), null, null);
 
          _viewCounter++;
 
        }
 
      }
 
      return null;
 
    }

So How can I relate the IndependentPlayback with the CameraId to show this playback in my FloatingWindow?

Att…

João Pedro

As I said it is not straight forward. If you look at the Smart Client ImageViewerAddOn plugin sample it uses a subscription to detect when new Image Viewers are opened (Instance_NewImageViewerControlEvent). What I was suggesting is to capture one, they will happen as you open the new window, where you then command playback..

https://doc.developer.milestonesys.com/html/index.html?base=samples/pluginsamples/scimagevieweraddonsample/readme.html&tree=tree_1.html

Hello Bo, How are you?

Now I can create the SCIndependentPlayback and vinculate this to a DynamicView as you can see.

But my problema now is vinculate the camera to this SCIndependentPlayback.

My code is like that:

viewAndLayoutItem.InsertViewItemPlugin(index++, new SCIndependentPlaybackViewItemPlugin(), 
                                                                                new Dictionary<string, string>()
                                                                                {
                                                                                    { "SelectedFQID", alarm.EventHeader.Source.FQID.ObjectId.ToString() }
                                                                                });

But when the DynamicView pops up I receive this error:

************** Outer Exception **************

Exception type:System.Xml.XmlException

Exception message:Data at the root level is invalid. Line 1, position 1.

Exception source:System.Xml

Exception Target Site: Throw

at System.Xml.XmlTextReaderImpl.Throw(Exception e)

at System.Xml.XmlTextReaderImpl.ParseRootLevelWhitespace()

at System.Xml.XmlTextReaderImpl.ParseDocumentContent()

at System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace)

at System.Xml.XmlDocument.Load(XmlReader reader)

at System.Xml.XmlDocument.LoadXml(String xml)

at VideoOS.Platform.FQID..ctor(String xmlString)

at SCIndependentPlayback.Client.SCIndependentPlaybackViewItemManager.PropertiesLoaded() in [C:\Users\joao.sanchez\Desktop\SCIndependentPlayback\SCIndependentPlayback\Client\SCIndependentPlaybackViewItemManager.cs](file:C:/Users/joao.sanchez/Desktop/SCIndependentPlayback/SCIndependentPlayback/Client/SCIndependentPlaybackViewItemManager.cs):line 32

at VideoOS.RemoteClient.Application.MIP.PlatformViewItem.InitManager()

************** System Info **************

Date and time: 3/6/2024 6:34:19 PM

Machine Name: NSP-USU0138

Processors count: 8

OS version: Microsoft Windows NT 6.2.9200.0

64 bit OS: True

64 bit process: True

IP address: fe80::871c:3302:3249:c55d%19 (NSP-USU0138.corp.invenzi.com)

Current user: NSP-USU0138\milestone

What could be happen?

Att…

João Pedro

The issue here is that you are using the ObjectId where you are expected to use a FQID.

In your code alarm.EventHeader.Source.FQID.ObjectId.ToString() is the ObejctId serialized, instead you should use the FQID.

I would like to mention a tip for investigating issues like this.

I create a view with the SCIndependentPlayback plugin used in the view, configured to use a camera and I can see it working. Then I use the ConfigAPIClient to see what that veiw contains. I can see the View Item definition XML.This is relevant because you can then see the “SelectFQID” option and what it contains.

Now I can guide you what your code should look like to make sure you serialize the FQID to make it useful.

FQID cameraFQID = alarm.EventHeader.Source.FQID;
var test = cameraFQID.ToXmlNode();           
var test2 = test.OuterXml;
string StringToUse = new System.Xml.Linq.XText(test2).ToString();

Hello Bo, how are you?

I don’t understand what I need to do with this XML.

As you indicate to me I add this part of code in my definition for SCIndependentPlaybackViewItemPlugin:

FQID cameraFQID = alarm.EventHeader.Source.FQID;
var test = cameraFQID.ToXmlNode();
var test2 = test.OuterXml;
string StringToUse = new System.Xml.Linq.XText(test2).ToString();
 
viewAndLayoutItem.InsertViewItemPlugin(index++, new SCIndependentPlaybackViewItemPlugin(), 
    new Dictionary<string, string>()
    {
         { "SelectFQID", StringToUse }
    });

But I need to pass all the XML to SelectFQID? I tried to extract the value of SelectFQID from the XML but I don’t have success. How exactly I can use this XML to associate the camera to my SCIndependentPlaybackViewItemPlugin?

Att…

João Pedro

Did you get it to work?

No, only appears the Item view of Playback with the indicators command (Play, Pause, Foward…)

But don’t open the Playback from camera. As you can see in the image below.

Hello Bo, how are you?

Another question, Is there some way to indicate the start time to the Playback start for the SCIndependentPlaybackViewItemPlugin? I need this to show in the floating window the playback some seconds before the alarm happened.

Att…

João Pedro

I think you will have to pick out the right one and send a “SmartClient.PlaybackCommand” message.

This is another Plugin? I’m using DynamicView and SCIndependentPlayback?

I don’t find “SmartClient.PlaybackCommand” in the projects, can you show me the documentation for that?

Sorry, I made a wrong reply.

When you have ImageViewerAddOn in hand, you can get the PlaybackController..

-

Use this property to access the independent playback controller of an ImageViewer.

virtual PlaybackController VideoOS.Platform.Client.ImageViewerAddOn.IndependentPlaybackController

-

https://doc.developer.milestonesys.com/html/index.html?base=miphelp/class_video_o_s_1_1_platform_1_1_client_1_1_image_viewer_add_on.html&tree=tree_search.html?search=addon

When you have the PlaybackController you change mode, change the time and more.

-

https://doc.developer.milestonesys.com/html/index.html?base=miphelp/class_video_o_s_1_1_platform_1_1_client_1_1_playback_controller.html

Ok, I will try to use this samples, but I need to link a camera to the Playback first.

This is not working yet, nevertheless with the code that you indicate to me.

Above I show you the problem, only open the ItemView in blank.

So to clarify the point we are now, I’m having two problems yet to solve:

  1. Link a Camera to a Playback in DynamicView.

This is the code that I try to use to link the camera to the SCIndependentPlayck opened in a DynamicView, but don’t work.

FQID cameraFQID = alarm.EventHeader.Source.FQID;
var test = cameraFQID.ToXmlNode();
var test2 = test.OuterXml;
string StringToUse = new System.Xml.Linq.XText(test2).ToString();
 
viewAndLayoutItem.InsertViewItemPlugin(index++, new SCIndependentPlaybackViewItemPlugin(), 
    new Dictionary<string, string>()
    {
         { "SelectFQID", StringToUse }
    });
  1. When I will open the DynamicView with the Playback I need to open 5 seconds before the alarm.

Here I will use the ImageViewerAddOn plugin to make this control, but first I need the image of camera in the playback.

Please Bo, I’m running out of time to deliver this solution, could you help me with these two points, I make myself available to make a call to clear up doubts.

Att…

João Pedro

In the SCIndependentPlayback sample (SCIndependentPlaybackViewItemManager.cs) it uses

SetProperty("SelectedFQID", _selectedCamera.FQID.ToXmlNode().OuterXml);

It seems you are using the sample code direct, I wonder if you have really changed SelectedFQID to SelectFQID, otherwise this might be a typo.

Also, I might be mistaken and you need to use test2 instead of StringToUse so that you get the OuterXml, the SetProperty above seems to indicate this.

I hope this helps I have not tried what you are attempting to do.

Bo Thanks so much! I used the info of the cameraFQID in XML and appereantely works now!!

The code are like this:

FQID cameraFQID = alarm.EventHeader.Source.FQID;
var properties = cameraFQID.ToXmlNode().OuterXml;
 
viewAndLayoutItem.InsertViewItemPlugin(index++, new SCIndependentPlaybackViewItemPlugin(), new Dictionary<string, string>() { { "SelectedFQID", properties } });

I will try now the commands with ImageViewerAddOn to open this playback some seconds before.

Bo, now to finish this feature I need to play this playback in DynamicView as I mentioned to you.

I’m in trouble to make this work, doesn’t matter what I do the playback always open paused and in the actual datetime.

I’m Using the code below to make the command of playback, but did not work

FQID cameraFQID = alarm.EventHeader.Source.FQID;
var properties = cameraFQID.ToXmlNode().OuterXml;
 
SCIndependentPlaybackViewItemPlugin _viewItemPlugin = new SCIndependentPlaybackViewItemPlugin();
 
timeFromEvent = alarm.EventHeader.Timestamp.ToLocalTime().AddSeconds(-5);
 
viewAndLayoutItem.InsertViewItemPlugin(index++, _viewItemPlugin, new Dictionary<string, string>() { { "SelectedFQID", properties } });
 
Collection<object> resultCollection = null;
ClientControl.Instance.CallOnUiThread(() =>
{
	resultCollection = EnvironmentManager.Instance.SendMessage(new VideoOS.Platform.Messaging.Message(MessageId.SmartClient.PlaybackIndication, _viewItemPlugin), null, null);
});
 
if (resultCollection != null && resultCollection.Count > 0 && resultCollection[0].GetType().Equals(typeof(FQID)))
{
	PlaybackCommandData playbackCommandData = new PlaybackCommandData()
	{
		Command = PlaybackData.Goto,
		DateTime = timeFromEvent,
		Speed = 0
	};
	EnvironmentManager.Instance.PostMessage(new VideoOS.Platform.Messaging.Message(MessageId.SmartClient.PlaybackCommand, playbackCommandData), (FQID)resultCollection[0], null);
}

You mentioned to use the ImageViewerAddOn, but I don’t find the path to attach my playback oppened with the classes and objects that ImageViewerAddOn provide. Can you show me a example, using the commands of playback?

Att…

João Pedro

This sample - https://doc.developer.milestonesys.com/html/index.html?base=samples/pluginsamples/scimagevieweraddonsample/readme.html&tree=tree_1.html