Introduction

Jabra PanaCast 50 features three 13-megapixel cameras with 180° panoramic-4K, real-time video stitching that captures participants without visual lag or distortion. The device’s eight beamforming microphones allow for precise voice detection and clear communication, and the array of four powerful stereo speakers support full-duplex audio, free of sound clipping.

Jabra PanaCast 50 also supports real-time whiteboard content streaming - its dual video streams allow you to display the whiteboard and meeting participants for enhanced collaboration simultaneously.

In addition, Jabra PanaCast 50’s advanced Artificial Intelligence (AI) features include Intelligent Zoom, which automatically detects and focuses on participants in a meeting, Virtual Director, which automatically focuses on the speaking participants in engaging meetings, and People Count, which manages meeting room usage and capacity and notifies the user if the room capacity has exceeded the limit.

This article introduces you to some examples of the Jabra SDK library APIs and callbacks, allowing any application developer to integrate with the PanaCast 50 features.

Understanding PanaCast 50 enumeration on Windows 10

When connected to a computer via the USB port, the PanaCast 50 enumerates to the host as a USB Hub with two USB composite devices: a USB video device and a USB audio/Human Interface Device (HID).

The standard Windows USB Audio Class, USB Video Class (UVC), and HID Class Drivers are associated with the device and handle all video, audio, and HID communication between the host and the PanaCast 50. No additional drivers are needed, thereby allowing Plug and Play support.

When the device is in normal mode, it enumerates as a single video device and expose a single video stream, an audio endpoint, and HID pages - Telephony (0xB), Consumer (0xC), as well as the two Jabra-specific vendor pages.

Jabra PanaCast enumeration 1

When whiteboard sharing is enabled on a separate content camera, the device re-enumerates to the host with the following two video endpoints:

Jabra PanaCast enumeration 1

The following diagram is the high-level software stack on Windows, showing Microsoft Teams Client’s use of the standard HID communication protocol. This is done via the Telephony and Consumer pages to send and receive HID commands and events, as well as to support call-control with a USB audio device.

For audio and video capture and rendering, applications such as Microsoft Teams and other UC (Unified Communications) clients use a Windows SDK such as the Core Audio, Media Foundation, or DirectShow frameworks.

Jabra Direct, a software solution provided by Jabra to manage video and audio endpoint devices, uses the Jabra SDK library to communicate with Jabra headsets and video products.

Moreover, the Jabra SDK library uses the vendor-specific HID pages to send and receive Jabra-specific HID commands. The audio and video capture and rendering are mainly managed by the UC client application and the operating system.

Jabra PanaCast HID pages

Understanding Jabra SDK for Windows, macOS, and Linux

The Jabra SDK for Windows, macOS, and Linux enables developers to integrate and deploy custom software solutions with Jabra audio and video devices. With the library included in the Jabra SDK, developers can access USB endpoint audio and video devices, filter and pick the device of interest, and register for callbacks on device button presses, state changes, and analytics.

The Jabra SDK library also supports high-level APIs to read and write device settings and send commands to the device.

Moreover, the native Jabra SDK library exposes the following core interfaces:

  • Common Interface - Common.h
  • Device Settings - JabraDeviceConfig.h
  • Remote Call control Interface - JabraNativeHID.h
  • Ambience Modes - Interface_AmbienceModes.h
  • Bluetooth Interface - Interface_Bluetooth.h
  • Video Device – Interface_Video.h, Interface_Whiteboard.h
  • Firmware Update Interface - Interface_Firmware.h
  • Network Interface – Interface_Network.h

The Jabra SDK library is currently available for Windows (C, C++, C#), Mac (C, Objective C) , and Linux (C) platforms.

On Windows, you can build a native application in C, C++ using the Jabra Native SDK library (libjabra.dll) or a managed .NET application in C# using the Jabra .NET wrapper library (JabraSDK.dll).

The Jabra .NET wrapper library exposes a subset of the Native library. The wrapper library JabraSDK.dll is a .NET assembly that uses platform invoke services (PInvoke) to access the APIs in the Jabra Native library.

For more information on the Jabra SDK for Windows, macOS, and Linux, refer to the following URLs:

In addition to the Native library for desktop applications, Jabra also provides a JavaScript library for browser-based web applications and apps built with Node.js:

Integrating with Jabra PanaCast 50

The following section outlines the basic steps to expose Jabra PanaCast 50 features using the C# wrapper from the Jabra SDK for Windows. You will learn how to:

  • Initialize the SDK using ServiceFactory
  • Get access to a DeviceService instance
  • Handle device arrival and removal notifications
  • Filter out a video device
  • Obtain an IDevice instance

Using the IDevice interface, you can call various APIs to get people count, set room capacity limits, control camera pan, tilt and zoom settings, set camera presets, and dynamically change video modes.

The code snippets in the following section demonstrate how you can build a simple WinForms application and use a TreeView control to show the Jabra devices connected to USB.

Initializing the library

The C# wrapper exposes a few core interfaces such as IServiceFactory, IDeviceService, IDevice, and ISettings to use with Jabra audio and video devices.

To use the Jabra library in your managed .NET application, you must add a reference to JabraSDK.dll providing access to all core interfaces and data types.

The following code snippet shows how you can instantiate ServiceFactory, register your client, and get an instance of DeviceService. Using DeviceService, you can register for a few callbacks to handle device arrival and removal events, as well as device logging events.

// Class members to keep track of Jabra core interfaces
using JabraSDK;

IServiceFactory m_serviceFactory = null;
IDeviceService m_deviceService = null;
IDevice m_currentDevice = null;
// Jabra SDK init
m_serviceFactory = new ServiceFactory();

//Replace with your API Key from  https://developer.jabra.com/
m_serviceFactory.SetClientId("xxxxx");
m_deviceService = m_serviceFactory.CreateDeviceService();

// Device arrival and removal event handlers for when a Jabra device is attached or
// removed from USB port. This event is also triggered for all the Jabra USB 
// device that are already attached to the USB port
m_deviceService.DeviceAdded += M_deviceService_DeviceAdded;
m_deviceService.DeviceRemoved += M_deviceService_DeviceRemoved;

// Device Logging event handler
m_deviceService.DeviceLoggingInput += M_deviceService_DeviceLoggingInput;

Handling Device Arrival and Removal Notifications

When you have registered for the DeviceAdded, DeviceRemoved, and the DeviceLoggingInput events by specifying callback methods, the callback methods will be asynchronous and called from different threads.

One of the typical actions you can take on these callback methods is to update Windows UI elements (TreeView) already created on the main thread. Because you cannot update Windows UI elements from a secondary thread directly, you must marshal and post them to the main thread via Control.Invoke as shown in the UpdateMainThreadUI function.

On a DeviceRemoved event, you can update the WinForm TreeView control and remove the device from the TreeView as shown in the RemoveDeviceFromTreeView function code sample that follows.

When a DeviceAdded event is received, you can add the device information to the TreeView, and do some initialization for Jabra PanaCast 50, as shown in the AddDeviceToTreeView and InitVideoDevice methods.

//Event handlers for device removal, arrival and device logging events
private void M_deviceService_DeviceRemoved(object sender, DeviceRemovedEventArgs e)
{
    UpdateMainThreadUI(UIAction.eDeviceRemoved, e.Device);

}

private void M_deviceService_DeviceAdded(object sender, DeviceAddedEventArgs e)
{
     UpdateMainThreadUI(UIAction.eDeviceAdded, e.Device);

     //Initialize Video device and set required properties
     InitVideoDevice(e.Device);
}

private void M_deviceService_DeviceLoggingInput(object sender, DeviceLoggingEventArgs e)
{               
    UpdateMainThreadUI(UIAction.eDeviceLog, e);
}



//Helper methods to marshal device arrival, removal and device logging events 
//to main thread to update Windows UI control created in the main thread
internal enum UIAction
{
    eDeviceAdded,
    eDeviceRemoved,
    eDeviceLog
}

private delegate void MarshalToMainForm(UIAction action, object obj);

private void UpdateMainThreadUI(UIAction action, object e)
{
     object[] args = new object[] { action, e };

     MarshalToMainForm MarshalToFormDelegate = new MarshalToMainForm(AccessMainForm);

     base.Invoke(MarshalToFormDelegate, args);

}


private void AccessMainForm(UIAction action, object obj)
{
    switch (action)
    {
         case UIAction.eDeviceAdded:
              AddDeviceToTreeView((IDevice)obj);
              break;

         case UIAction.eDeviceRemoved:
              RemoveDeviceFromTreeView((IDevice)obj);
               break;

         case UIAction.eDeviceLog:
              HandleDeviceLogEvents((DeviceLoggingEventArgs)obj);
              break;

      }
}

//Add device to the TreeView root node: 
/Create a new TreeNode
//Use IDevice.DeviceId to uniquely identify the TreeNode in the TreeView.
//Use IDevice.Name and IDevice.ProductId to display device info 
//Add the TreeNode to the TreeView
private void AddDeviceToTreeView(IDevice dev)
{
    if (m_rootNode != null)
    {
       TreeNode tn = new TreeNode();
       tn.Tag = dev.DeviceId;
       tn.Text = String.Format(dev.DeviceId + ". " + dev.Name + "( PID = 0x" + dev.ProductId.ToString("x") + " )");

       m_rootNode.Nodes.Add(tn);
       m_rootNode.Expand();
   }
}

//Remove device from the TreeView:
//Iterate through all the Nodes in the TreeView to find the TreeNode to remove
//Use IDevice.DeviceId for identifying the TreeNode for the device that is removed
//Remove the TreeNode from the TreeView
private void RemoveDeviceFromTreeView(IDevice dev)
{
    if (m_rootNode != null)
    {
        TreeNode nodeToRemove = null;
        foreach (TreeNode tn in m_rootNode.Nodes)
        {
            if ((int)tn.Tag == dev.DeviceId)
            {
                 nodeToRemove = tn;
                 break;
              }
         }

  if (nodeToRemove != null)
         {
              m_rootNode.Nodes.Remove(nodeToRemove);
              m_rootNode.Expand();
         }
     }
 }

Setting Room Capacity and People Count

The device arrival and removal methods are called for all Jabra USB video and audio devices supported by the Jabra library.

In addition to updating the TreeView UI control, you must also check for the device type. If it is a video device, you must also set video device-specific properties.

To set room capacity limits and receive notifications when the capacity exceeds the limit, you can use the IDevice methods SetRoomCapacity and SetRoomCapacityNotificationEnabled. Moreover, the SetNotificationStyle and SetNotificationUsage methods specifies the method (LED, Tone, Voice) and frequency (Always or Only When Video Is Enabled) of notifications.

To get the current People Count in the room, you can use the GetPeopleCount method. Changes in People Count are reported as part of the device logging events, so you must enable device logging by calling DeviceLoggingConfiguration(true) on the IDevice instance.

In addition, you can set how frequently the People Count change event is reported by calling the SetPeopleCountReportInterval method. As the People Count changes, you must specify an argument with an interval in seconds, as shown in the following code snippet:

//Check for Video device and set properties for room capacity, device logging
//and people count reporting interval
private void InitVideoDevice(IDevice dev)
{ 
    //Check for Video device
    if (dev.IsFeatureSupported(DeviceFeature.Video))
    {
       try
       {
           //Set the current video device instance so you can access the IDevice
           //instance from other modules
           m_currentDevice = dev;

           //Set the Room Capacity to 3 people
           dev.SetRoomCapacity(3);
           dev.SetRoomCapacityNotificationEnabled(true);

           //Set Room capacity exceeded notification alerts
           dev.SetNotificationStyle(NotificationStyle.LedTonesAndVoice);
           dev.SetNotificationUsage(NotificationUsage.WhenVideoIsEnabled);

           //Get the current people count in the room
    short peopleCount = dev.GetPeopleCount();

           //Enable Device Logging as People count changes are reported as part of the
        //device logging events.
           dev.DeviceLoggingConfiguration(true);

           //Set interval (in seconds) for people count event reporting
           dev.SetPeopleCountReportInterval(10);

        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Jabra SDK Exception");
        }
    }
}

Handling Device Logging Events: Parsing People Count

When the People Count changes, the DeviceLoggingInput event handler is called, and the DeviceLoggingEventArgs Data property presents the JSON representation of the device event payload.
For example, see "People_count": "1" in the following Device Log Event:

DeviceID = 1,
Data = {
  "AppID": "0NcMNlux1F4w4lU+6AG7kAM8KpzBgFag2CmoDbSn5sQ=",
  "Device Name": "Jabra PanaCast 50",
  "ESN": "70BF92FEBD86",
  "FW": "1.15.0",
  "LocalTimeStamp": "05-01-2022 21:19:59",
  "People_count": "1",
  "Pid": "3012",
  "Raw data": "0x03 0x00 0x01 0x00",
  "Seq.No": 5,
  "Variant": "08-0A"
}

You can use a JSON parsing library such as Newtonsoft.Json to parse the payload and extract the "People_count" property as shown in the following code snippet:

private void HandleDeviceLogEvents(DeviceLoggingEventArgs e)
{
   //Parse People Count from JSON payload
   var parsed = JObject.Parse(e.Data);
   JToken pc = parsed["People_count"];
   if (pc != null)
   {
      String peopleCount = String.Format("People Count = {0}", pc);
      tsPeopleCount.Text = peopleCount;
   }
}

When People Count in the room exceeds capacity, a device logging event is reported.
For example, see "Room_exceeded": "TRUE" in the following Device Log Event:

DeviceID = 1,
Data = {
  "AppID": "0NcMNlux1F4w4lU+6AG7kAM8KpzBgFag2CmoDbSn5sQ=",
  "Device Name": "Jabra PanaCast 50",
  "ESN": "70BF92FEBD86",
  "FW": "1.15.0",
  "LocalTimeStamp": "05-01-2022 21:24:32",
  "Pid": "3012",
  "Raw data": "0x01 0x0a 0x01",
  "Room_exceeded": "TRUE",
  "Seq.No": 5,
  "Variant": "08-0A"
}

Based on the chosen notification method and frequency, you will receive LED, tone, or voice alerts on the Panacast 50.

Camera Control, Video Signal Settings, and Intelligent Zoom in PanaCast 50

Jabra PanaCast 50 supports camera control settings such as Zoom, Pan, Tilt, etc. and Video Signal settings such as Contrast, Brightness, Hue, etc. via standard UVC through Video Control Input and Output descriptors.

Therefore, any Windows video camera tool that supports UVC can access the supported settings and allow end-users to change them.

The following example shows video signal and camera control settings supported by PanaCast 50.

Jabra PanaCast controls 1

Jabra PanaCast controls 2

To change these video device settings, a Windows application developer can use tools like DirectShow or Media Foundation SDK. Moreover, the Jabra SDK library enables you to read, write, and get the range of each video device setting by providing simple APIs.

For example, using the IDevice instance, you can call the GetZoomLimits, GetPanTiltLimits methods to get metadata about the Zoom, Pan, and Tilt ranges or call the SetZoom, SetPanTilt methods to set them on the Jabra device.

PanaCast 50 also lets you store three camera presets for Pan, Tilt, and Zoom. You can apply them via the StorePTZPreset and ApplyPTZPreset methods as shown in the following code snippet.

//Get PTZ Limits from the device
ZoomLimits zl = dev.GetZoomLimits();
PanAndTiltLimits pl = dev.GetPanTiltLimits();

//Set PTZ Max and store in preset 1
dev.SetZoom(zl.Max);
dev.SetPanTilt(pl.PanLimits.Max, pl.TiltLimits.Max);
dev.StorePTZPreset(PTZPresetSlot.PTZPreset1);


//Set PTX min and store in preset 2
dev.SetZoom(zl.Min);
dev.SetPanTilt(pl.PanLimits.Min, pl.TiltLimits.Min);
dev.StorePTZPreset(PTZPresetSlot.PTZPreset2);

//Set PTX and store in preset 3
dev.SetZoom((ushort)(zl.Max/2));
dev.SetPanTilt(pl.PanLimits.Max /2, pl.TiltLimits.Max/2);
dev.StorePTZPreset(PTZPresetSlot.PTZPreset3);
//Apply Preset 1 to the device
dev.ApplyPTZPreset(PTZPresetSlot.PTZPreset1);

Using the Jabra SDK library, you can change Intelligent Zoom and Virtual Director AI features on the Jabra PanaCast 50. You can get the current video mode by calling the GetVideoMode method and switch between full-screen, Intelligent Zoom, and Virtual Director (Active speaker) modes by calling the SetVideoMode method, as shown in the following code sample.

//Get current Video Mode 
VideoMode vmode = dev.GetVideoMode();

//Set the Video mode to Virtual Director to track active speaker
dev.SetVideoMode(VideoMode.ActiveSpeaker);

For a complete list of APIs supported by the Jabra Native SDK libraries for Video devices, compare the SDK documentation.