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:
- 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.
- 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.
- 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.
- 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().
- 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.
- 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 <chrono>
#include <thread>
struct context_t {
bool roomListReceived = false;
bool roomJoined = false;
uint32_t framesReceived = 0;
};
int main()
{
context_t context;
if (!context.sdk) {
puts("Failed to initialize the ASMIRA SDK");
return 1;
}
void* opaque) {
reinterpret_cast<context_t*>(opaque)->serverState = serverState;
}, &context);
void*) {
puts("Authenticated");
else
printf("Login refused (0x%x)\n", response);
});
context.sdk->SetRoomAnnounceCallback([](const char* roomName, const uint32_t roomID,
printf("Room \"%s\" (id %u)%s%s\n", roomName, roomID,
reinterpret_cast<context_t*>(opaque)->roomListReceived = true;
}, &context);
context.sdk->SetRoomResponseCallback([](
const asdk::Request request,
const void*, const uint32_t, void* opaque) {
reinterpret_cast<context_t*>(opaque)->roomJoined =
}, &context);
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);
if (context.sdk->Connect("127.0.0.1", 30100, 5000, true, "test", "password",
printf("Connect failed: %s\n", context.sdk->errorStr);
context.sdk->Unload();
return 1;
}
bool subscribed = false;
bool joinRequested = false;
puts("Authentication failed");
break;
}
if (!subscribed)
else if (context.roomListReceived && !joinRequested)
}
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);
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
- Core and lifecycle – asdk::Initialize(), asdk::sdk_t, asdk::Status
- Connection and authentication – certificates, login, capabilities
- Rooms – discovering, joining, and the clients in a room
- Video and imaging – decoded frames, encoded passthrough, previews
- Audio – sending captured or encoded audio, receiving room audio
- Chat – room text messages
- Room file transfer – upload, download, resume, cancel, delete
- Position and telemetry – NMEA 0183 and MISB ST 0601 KLV
- Statistics – throughput and packet counters
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.