SDK Version 2.0.1
AnsuR Technologies ASMIRA SDK
Loading...
Searching...
No Matches
ASMIRA Viewer SDK

Introduction

The ASMIRA SDK lets an application join an ASMIRA room and take part in it: watch the video a sender streams, listen to the room audio, follow the platform and sensor position, exchange chat messages, and share files. It talks to an ASMIRA server the same way the ASMIRA applications do, so anything a viewer or controller can do from the UI is available here.

The API surface is one header, asmirasdk.h, and one structure, asdk::sdk_t. The header is self contained: it loads the shared library at runtime and resolves the entry points itself, so there is no import library to link against.

Everything the SDK receives arrives through callbacks, and everything it sends is a method on asdk::sdk_t. The modules break both down by topic.

Requirements

Build time:

  • asmirasdk.h, added to the include path. Nothing else, and no library to link.
  • A C++14 or newer compiler; the SDK itself is built as C++17. MSVC and GCC are supported. The header picks the platform from _WINDOWS, __WIN32__, __WIN64__ or __GNUC__ and stops with #error Compiler unsupported if none is defined, so pass /D_WINDOWS when building outside a Visual Studio project.

Run time, staged next to the executable:

  • asmirasdk.dll on Windows, libasmirasdk.so elsewhere. asdk::Initialize() opens this by name from the working directory or the platform search path.
  • videocore.dll / libvideocore.so for video and image decoding, together with the ffmpeg libraries it depends on. Without it asdk::Initialize() fails and returns nullptr.
  • audio.dll / libaudio.so for Opus and Speex. This one is optional: the SDK stays usable without it, but audio can be neither sent nor decoded and asdk::audio_options_t::codec reports asdk::AudioCodecUndef.

The server has to be ASMIRA 4.3.2 or newer.

Versioning

The header declares the version it was built against in asdk::VERSION_MAJOR, asdk::VERSION_MINOR and asdk::VERSION_PATCH, and asdk::Initialize() compares them against the version the library reports:

  • A different major version is refused, asdk::Initialize() returns nullptr.
  • A different minor version is accepted, with a warning printed to stdout.
  • The patch version is not checked.

The server checks its own version at login and answers with asdk::AuthenticationResponseVersionError, asdk::AuthenticationResponseVersionRecUpgrade or asdk::AuthenticationResponseVersionMayUpgrade.

The lifecycle

A client always goes through the same six steps:

  1. Initialize. asdk::Initialize() loads the library and returns an asdk::sdk_t. A nullptr means the library or one of its dependencies is missing, or the major version does not match.
  2. Install callbacks. Register everything of interest before connecting, so nothing that arrives during login is missed. Some callbacks also act as a switch: received audio is only decoded once asdk::sdk_t::SetDecodedAudioFrameCallback() has been set.
  3. Connect. asdk::sdk_t::ConnectSecured() for TLS, using a certificate from asdk::sdk_t::RequestCertificate(), or asdk::sdk_t::Connect() without. Announce a role with asdk::UserCapability and the features the client handles with asdk::DeviceCapability. Neither call blocks: they queue the attempt and return, so asdk::StatusOK here means only that the SDK accepted the request. Watch asdk::sdk_t::SetServerStateCallback() for asdk::ServerStateConnectedAndAuthenticated and asdk::sdk_t::SetAuthenticateResponseCallback() for the login result.
  4. Join a room. SendRequest(RequestRoomListSubscribe) makes the server announce its rooms through asdk::sdk_t::SetRoomAnnounceCallback(). Join one with SendRequestStr(RequestRoomJoin, roomName) and wait for asdk::RoomResponseSuccess on asdk::sdk_t::SetRoomResponseCallback().
  5. Take part. Frames, audio, position, chat and file announcements now flow in on their callbacks. Chat, audio and file transfer are room scoped and only work once a room has been joined.
  6. Shut down. asdk::sdk_t::Disconnect(), then asdk::sdk_t::Unload(), which releases the handle and frees the library. The handle must not be used afterwards.

Steps 3 and 4 are asynchronous throughout. Every request is a question, and the answer comes back on a callback, so a client is a small state machine rather than a sequence of blocking calls. Both examples are written that way.

Getting started

The smallest client that connects, joins a room and receives video:

#include <asmirasdk.h>
#include <chrono>
#include <thread>
struct context_t {
asdk::sdk_t* sdk = nullptr;
bool roomListReceived = false;
bool roomJoined = false;
uint32_t framesReceived = 0;
};
int main()
{
context_t context;
// Loads asmirasdk.dll / libasmirasdk.so and checks the version.
context.sdk = asdk::Initialize();
if (!context.sdk) {
puts("Failed to initialize the ASMIRA SDK");
return 1;
}
// Install the callbacks before connecting. Each one runs on an SDK thread,
// so keep the work short and never block.
context.sdk->SetServerStateCallback([](const asdk::ServerState serverState,
void* opaque) {
reinterpret_cast<context_t*>(opaque)->serverState = serverState;
}, &context);
// The response is a bit set, so test the bit rather than comparing.
context.sdk->SetAuthenticateResponseCallback([](const asdk::AuthenticationResponse response,
void*) {
puts("Authenticated");
else
printf("Login refused (0x%x)\n", response);
});
// Announced once per room after RequestRoomListSubscribe.
context.sdk->SetRoomAnnounceCallback([](const char* roomName, const uint32_t roomID,
const asdk::RoomStateValue state,
const asdk::RoomFlagValue flags, void* opaque) {
printf("Room \"%s\" (id %u)%s%s\n", roomName, roomID,
(state & asdk::RoomStatePassword) ? " [password]" : "",
(flags & asdk::RoomFlagChatViewers) ? " [chat]" : "");
reinterpret_cast<context_t*>(opaque)->roomListReceived = true;
}, &context);
// Answers SendRequest and SendRequestStr. optionalData carries any payload
// the request produced and is valid for the duration of this call only.
context.sdk->SetRoomResponseCallback([](const asdk::Request request,
const asdk::RoomResponse response,
const void*, const uint32_t, void* opaque) {
if (request == asdk::RequestRoomJoin)
reinterpret_cast<context_t*>(opaque)->roomJoined =
}, &context);
// Decoded RGB video. frame and frame->data are only valid for the duration
// of this call, so copy anything that has to outlive it.
context.sdk->SetFrameCallback([](const asdk::frame_t* frame, void* opaque) {
context_t* context = reinterpret_cast<context_t*>(opaque);
if (++context->framesReceived == 1)
printf("First frame: %dx%d\n", frame->width, frame->height);
}, &context);
// Use ConnectSecured with a certificate from RequestCertificate for TLS.
if (context.sdk->Connect("127.0.0.1", 30100, 5000, true, "test", "password",
"Basic Example") != asdk::StatusOK) {
printf("Connect failed: %s\n", context.sdk->errorStr);
context.sdk->Unload();
return 1;
}
// Connecting and joining are asynchronous: ask, then wait for the callback
// to report the result.
bool subscribed = false;
bool joinRequested = false;
while (asdk::TimeSpentSteady(start) < 30000) {
if (context.serverState == asdk::ServerStateAuthenticateError) {
puts("Authentication failed");
break;
}
if (context.serverState == asdk::ServerStateConnectedAndAuthenticated) {
if (!subscribed)
subscribed = context.sdk->SendRequest(asdk::RequestRoomListSubscribe) ==
else if (context.roomListReceived && !joinRequested)
joinRequested = context.sdk->SendRequestStr(asdk::RequestRoomJoin, "test") ==
}
if (context.roomJoined && context.framesReceived > 0)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
printf("Received %u frames\n", context.framesReceived);
context.sdk->Disconnect();
context.sdk->Unload();
return 0;
}

Callbacks and threading

Callbacks are invoked from SDK internal threads, never from the thread that called into the SDK. Two consequences matter:

  • Do not block. A callback holds up the thread that delivers it. Copy what is needed, hand it to the application, and return.
  • Guard shared state. Anything a callback touches is touched concurrently with the application's own thread.

Every callback takes the opaque pointer given when it was installed and passes it back unchanged, which is how a callback reaches its context without a global.

Buffers handed to a callback – asdk::frame_t::data, the decoded audio in asdk::audio_frame_t, the bytes in RoomFileDownloadedFunc, the position structures behind asdk::position_values_t – belong to the SDK and are only valid for the duration of the call. Copy anything that has to outlive it.

Capabilities

Two independent sets are announced on connect.

asdk::UserCapability is the role asked of the server: one of asdk::UserCapabilitySender, asdk::UserCapabilityController, asdk::UserCapabilityViewer or asdk::UserCapabilityGuest. Use a single bit. The server checks it against the account and answers with asdk::AuthenticationResponseWrongLoginType if it does not match. Sender, Controller and Super are the privileged roles, so a viewer cannot for instance delete a shared room file.

asdk::DeviceCapability is a bit set describing what the client can actually do: which video codecs it decodes, whether it has a microphone, whether it handles chat or file transfer. The server and the other room members use it to decide what to send, so declare only what is really supported. Start from asdk::DeviceCapabilityBasic and add what applies, for example DeviceCapabilityBasic | DeviceCapabilityFileTransfer.

Error handling

Every call returns an asdk::Status. asdk::StatusOK means the request was accepted, not that it finished: an upload queued, samples handed to the encoder, a join request sent. The result of the operation itself comes back on a callback.

When a call fails, asdk::sdk_t::errorStr holds a readable description of the last error and asdk::sdk_t::status the last status, both useful for logging:

if (sdk->RoomFileSendFromPath(roomID, "report.pdf") != asdk::StatusOK)
printf("upload rejected: %s\n", sdk->errorStr);
@ StatusOK
Definition asmirasdk.h:161

The statuses worth handling apart from the general ones are asdk::StatusErrorHandle (the handle is not initialized), asdk::StatusErrorNotPermitted (the connected user's role does not allow it), asdk::StatusErrorFile (a local file could not be read, is empty or too large) and asdk::StatusErrorAudioModule (no audio module, so no codec).

Where to go next

The examples are the fastest way in: example_basic.cpp above is the minimum, and example_complete.cpp is a full state machine that also does chat, audio, room previews and file transfer.