ToxMod Server SDK 1.5.2
ToxMod Server-Side SDK
 
Loading...
Searching...
No Matches
basic_example.c
/* @file basic_example.c
* @copyright This file is Confidential and Proprietary to Modulate, Inc.
* This file may not be shared or distributed without permission from Modulate.
* Email mailto:support@modulate.ai with requests, bug reports, or questions!
* Last updated September 12, 2021.
*/
/* @brief Defined to allow fopen on Windows */
#define _CRT_SECURE_NO_WARNINGS
#include "tox_server.h"
#include <stdio.h>
#include <string.h>
#include <ogg/ogg.h>
/*
* Test program to read test_clip.opus and upload the internal Opus packets
* to ToxMod's servers
*/
/*
* Game-specific data inputs - help keep track of what game or application
* is involved, who is speaking, and who they are speaking to, etc.
*/
static const char* account_uuid = TOXMOD_ACCOUNT_ID;
static const char* api_key = TOXMOD_API_KEY;
static const char* single_tenant_prefix = "dev";
/*
* Properties of the player and voice chat session to track their behavior
* alongside other participants in the same voice chat session
*/
static const char* player_name = "test_player";
static const char* session_name = "test_session";
/* Input Opus file specific parameters */
const char* input_filename = "test_clip.opus";
#define packet_length_ms 20
#define maximum_packets_in_circular_buffer (1000 / packet_length_ms)
/*
* Packet size is in bytes
* > Note: the included test clip has 80 bytes per packet, but other Opus files
* may have different packet sizes. The maximum Opus packet size if no properties
* of the input file are known is 1275.
* https://datatracker.ietf.org/doc/html/rfc6716.html#section-3 3.2.1
*/
static const unsigned int maximum_opus_packet_size = 80;
/* A simple error handler for debugging */
#define error_handler(err) \
if(err) { \
fprintf(stderr, "Tox server error %s\n", tox_server_error_name(err)); \
return 1; \
}
/* Helper functions to read Opus packets from the test_clip.opus Ogg Opus file */
int get_page(ogg_sync_state* sync_state, ogg_page* page, FILE* clip_file) {
const unsigned int chunk_size = 1024;
while((ogg_sync_pageout(sync_state, page) != 1)) {
char* buffer = ogg_sync_buffer(sync_state, chunk_size);
const unsigned long bytes_read = (unsigned long)fread(buffer, 1, chunk_size, clip_file);
if(bytes_read == 0)
return 0;
ogg_sync_wrote(sync_state, bytes_read);
}
return 1;
}
int check_is_opus_header(const unsigned char* packet, unsigned int packet_size) {
/* https://datatracker.ietf.org/doc/html/rfc7845.html#section-5 */
if(packet_size < 8)
return 0;
return (packet[0] == 0x4f) && (packet[1] == 0x70);
}
int main(void) {
/* Check that account_uuid and api_key are set */
if(!strcmp(account_uuid, "your account uuid here")) {
fprintf(stderr, "Account UUID has not been set. Please set your account_uuid, api_key, "
"and (if applicable) single_tenant_prefix in this example program's "
"source code file before using it.\n");
return 1;
}
if(!strcmp(api_key, "your api key here")) {
fprintf(stderr, "API Key has not been set. Please set your account_uuid, api_key, "
"and (if applicable) single_tenant_prefix in this example program's "
"source code file before using it.\n");
return 1;
}
printf("Running Opus packets from %s through tox_server\n", input_filename);
/* Run global intialization with the account-wide parameters */
err = tox_server_global_init(account_uuid, api_key, single_tenant_prefix);
error_handler(err);
/* Create a single tox_server_instance_t for the one player */
tox_server_instance_t tox_server_instance;
tox_server_config_t tox_server_config;
tox_server_config.player_name_unique = player_name;
tox_server_config.session_name_unique = session_name;
tox_server_config.circular_buffer_max_num_packets = maximum_packets_in_circular_buffer;
tox_server_config.max_packet_size = maximum_opus_packet_size;
err = tox_server_create_instance(&tox_server_instance, &tox_server_config);
error_handler(err);
/* Block for reading the input Opus file and runnin it through tox_server */
int current_uploads = 0;
{
/*
* Prepare separate Ogg state for reading in the test clip
* This is likely unneeded on a voice chat server with direct
* access to Opus packets
*/
ogg_sync_state sync_state;
ogg_stream_state stream_state;
ogg_page page;
ogg_packet packet;
ogg_sync_init(&sync_state);
/* Open the test clip file */
FILE* clip_file = fopen(input_filename, "rb");
if(!clip_file) {
fprintf(stderr, "Failed to open clip file %s\n", input_filename);
return 2;
}
/*
* Loop through all of the Ogg packets in the test clip file, adding
* them libtox_server. This simple example also calls the buffer copy,
* begin upload, and run uploads functions in the same loop; in a voice chat
* server, those functions should be run in separate loops on a timer, as they
* are not realtime-safe
*/
while(1) {
/* Read in Ogg packets from the file */
const int read_successful = get_page(&sync_state, &page, clip_file);
if(!read_successful)
break;
if(ogg_page_bos(&page)) {
const int serial_number = ogg_page_serialno(&page);
ogg_stream_init(&stream_state, serial_number);
}
ogg_stream_pagein(&stream_state, &page);
while(ogg_stream_packetout(&stream_state, &packet) != 0) {
if(!check_is_opus_header(packet.packet, packet.bytes)) {
/* Realtime-safe, add the Opus packets to libtox_server */
err = tox_server_add_packet(&tox_server_instance, packet.packet, packet.bytes);
error_handler(err);
/* Not realtime-safe, run in separate timed loops during production */
err = tox_server_run_buffer_copy(&tox_server_instance);
error_handler(err);
err = tox_server_begin_upload(&tox_server_instance);
error_handler(err);
/*
* 0 timeout due to this simple program running in the same packet add loop.
* When running in a separate timed loop, 100 is a good choice for the timeout.
*/
err = tox_server_run_all_uploads_with_timeout(&current_uploads, 0);
error_handler(err);
}
}
}
/* Cleanup the file and its ogg stream */
fclose(clip_file);
ogg_sync_clear(&sync_state);
ogg_stream_clear(&stream_state);
}
/* Finish any in-progress uploads before closing */
while(current_uploads > 0) {
err = tox_server_run_all_uploads_with_timeout(&current_uploads, 100);
error_handler(err);
}
/* Destroy the per-player tox_server_instance_t, and cleanup global state */
err = tox_server_destroy_instance(&tox_server_instance);
error_handler(err);
error_handler(err);
return 0;
}
Definition tox_server.h:147
const char * session_name_unique
Definition tox_server.h:157
unsigned long circular_buffer_max_num_packets
Definition tox_server.h:163
const char * player_name_unique
Definition tox_server.h:152
unsigned long max_packet_size
Definition tox_server.h:168
Definition tox_server.h:136
TOX_SERVER_ERROR tox_server_add_packet(tox_server_instance_t *tox_instance_ptr, const unsigned char *packet, unsigned int packet_size)
TOX_SERVER_ERROR tox_server_destroy_instance(tox_server_instance_t *tox_instance_ptr)
TOX_SERVER_ERROR tox_server_begin_upload(tox_server_instance_t *tox_instance_ptr)
TOX_SERVER_ERROR tox_server_create_instance(tox_server_instance_t *tox_instance_ptr, const tox_server_config_t *tox_server_config_ptr)
TOX_SERVER_ERROR
Definition tox_server.h:55
TOX_SERVER_ERROR tox_server_global_init(const char *account_uuid, const char *api_key, const char *single_tenant_prefix)
TOX_SERVER_ERROR tox_server_global_cleanup(void)
TOX_SERVER_ERROR tox_server_run_all_uploads_with_timeout(int *num_running_uploads, int wait_timeout_milliseconds)
TOX_SERVER_ERROR tox_server_run_buffer_copy(tox_server_instance_t *tox_instance_ptr)