wcWebCamClient lib  v0.8.2
theora_test/output_strm/main.cpp
/*===============================================================*/
/* This is an example of how to use the wcWebCamClient library. */
/* Simple class for parsing command line */
/* commandline_tools.h */
/* */
/* Part of wcWebCamClient library project */
/* */
/* Copyright 2022 Ilya Medvedkov */
/*===============================================================*/
#ifndef COMMLINE_TOOLS_H_INCLUDED
#define COMMLINE_TOOLS_H_INCLUDED
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
class InputParser{
public:
InputParser (int &argc, char **argv){
for (int i=1; i < argc; ++i)
this->tokens.push_back(std::string(argv[i]));
}
const std::string& getCmdOption(const std::string &option) const{
std::vector<std::string>::const_iterator itr;
itr = std::find(this->tokens.begin(), this->tokens.end(), option);
if (itr != this->tokens.end() && ++itr != this->tokens.end()){
return *itr;
}
static const std::string empty_string("");
return empty_string;
}
bool cmdOptionExists(const std::string &option) const{
return std::find(this->tokens.begin(), this->tokens.end(), option)
!= this->tokens.end();
}
const std::string& getHost() {
static const std::string CMD_HOST("--host");
static const std::string CMD_HOST_SYN("-u");
const std::string & res = getCmdOption(CMD_HOST);
if (res.empty()) {
return getCmdOption(CMD_HOST_SYN);
} else
return res;
}
const std::string& getProxy() {
static const std::string CMD_PROXY("--proxy");
static const std::string CMD_PROXY_SYN("-p");
const std::string & res = getCmdOption(CMD_PROXY);
if (res.empty()) {
return getCmdOption(CMD_PROXY_SYN);
} else
return res;
}
const std::string& getDevice() {
static const std::string CMD_DEVICE("--device");
static const std::string CMD_DEVICE_SYN("-d");
const std::string & res = getCmdOption(CMD_DEVICE);
if (res.empty()) {
return getCmdOption(CMD_DEVICE_SYN);
} else
return res;
}
const std::string& getUserName() {
static const std::string CMD_USER_NAME("--name");
static const std::string CMD_USER_NAME_SYN("-n");
const std::string & res = getCmdOption(CMD_USER_NAME);
if (res.empty()) {
return getCmdOption(CMD_USER_NAME_SYN);
} else
return res;
}
const std::string& getUserPassword() {
static const std::string CMD_USER_PWRD("--pwrd");
static const std::string CMD_USER_PWRD_SYN("-x");
const std::string & res = getCmdOption(CMD_USER_PWRD);
if (res.empty()) {
return getCmdOption(CMD_USER_PWRD_SYN);
} else
return res;
}
bool ignoreTLSCert() {
static const std::string OPT_TLS_IGNORE("-k");
return cmdOptionExists(OPT_TLS_IGNORE);
}
bool showHelp() {
static const std::string OPT_SHOW_HELP("-h");
return cmdOptionExists(OPT_SHOW_HELP);
}
virtual void printCommonHelp() {
if (tokens.size() > 0)
std::cout << "Usage:" << tokens.at(0) << " [options...]" << std::endl;
std::cout << " -u, --host URL for server host in format https://hostname:port" << std::endl;
std::cout << " -p, --proxy Proxy in format [[username][:password]@][proxyurl:port]" << std::endl;
std::cout << " -d, --device Device name" << std::endl;
std::cout << " -n, --name Login name" << std::endl;
std::cout << " -x, --pwrd Login password" << std::endl;
std::cout << " -k Ignore TLS certificate errors" << std::endl;
std::cout << " -h Show this help" << std::endl;
}
private:
std::vector <std::string> tokens;
};
#endif // COMMLINE_TOOLS_H_INCLUDED

This is an example of how to use the wcWebCamClient library. A simple test program to demonstrate the streaming of output data.

/*===============================================================*/
/* This is an example of how to use the wcWebCamClient library. */
/* In this example, a client is created, authorized on the */
/* server, and starts to streaming. Each outgoing frame is an */
/* PNG-image from the specified directory. */
/* */
/* Part of wcWebCamClient library project */
/* */
/* Copyright 2022 Ilya Medvedkov */
/*===============================================================*
The example uses code from the Theora project (test png2theora).
https://github.com/xiph/theora/blob/master/examples/png2theora.c
*****************************************************************/
#include <iostream>
#include <fstream>
#include <wcwebcamclient.h>
#include <string.h>
#include <chrono>
#include <thread>
#include <libgen.h>
#include <dirent.h>
#include "../../commline_tools.h"
using namespace std;
/* The max delta time between two frames */
static const uint32_t MAX_DELTA = 300;
/* The total program timeout in seconds */
static const uint32_t TIME_OUT = 60;
static uint8_t running = 0;
static chrono::time_point<chrono::high_resolution_clock> lst_timestamp;
static chrono::time_point<chrono::high_resolution_clock> cur_timestamp;
static string input_filter;
static string input_directory;
static char input_png[1024];
static dirent **png_files;
static char * mem_frame_buffer = NULL;
static long frame_size = 0, frame_buffer = 0;
static int16_t loc_frame = 0;
static int16_t total_frames = 0;
bool need_to_load_next_frame = false;
/* Read png-image to memory */
static int png_read(const char *pathname)
{
streampos begin,end;
ifstream fp (pathname, ios::binary);
if (!fp.is_open()) {
cerr << pathname << ": error: " << errno << endl;
return 1;
}
fp.seekg (0, ios::end);
long fsize = fp.tellg();
fp.seekg (0, ios::beg);
if (frame_buffer == 0) {
frame_buffer = (((fsize + WC_STREAM_FRAME_HEADER_SIZE) >> 12) + 1) << 12;
mem_frame_buffer = (char*)malloc(frame_buffer);
} else {
if (frame_buffer < fsize) {
frame_buffer = (((fsize + WC_STREAM_FRAME_HEADER_SIZE) >> 12) + 1) << 12;
mem_frame_buffer = (char*)realloc(mem_frame_buffer, frame_buffer);
}
}
frame_size = fsize + WC_STREAM_FRAME_HEADER_SIZE;
*(uint16_t*)mem_frame_buffer = WC_FRAME_START_SEQ;
*(uint32_t*)(&(mem_frame_buffer[sizeof(uint16_t)])) = (uint32_t) fsize;
fp.read (&(mem_frame_buffer[WC_STREAM_FRAME_HEADER_SIZE]), fsize);
fp.close();
return 0;
}
/* Check whether the file name (de->d_name) matches
the specified filter (input_filter).
src: xiph/theora/blob/master/examples/png2theora.c */
static int include_files (const struct dirent *de)
{
static char name[1024];
int number = -1;
sscanf(de->d_name, input_filter.c_str(), &number);
sprintf(name, input_filter.c_str(), number);
return !strcmp(name, de->d_name);
}
/* Loads new data from the specified file, writes it to a frame,
then inserts a new frame into the output stack.*/
bool load_next_frame(wcHandle client) {
cur_timestamp = chrono::system_clock::now();
auto int_ms = chrono::duration_cast<chrono::milliseconds>(cur_timestamp - lst_timestamp);
if (need_to_load_next_frame && (int_ms.count() >= MAX_DELTA)) {
snprintf(input_png, 1023, "%s/%s", input_directory.c_str(), png_files[loc_frame]->d_name);
if(png_read(input_png)) {
cerr << "could not read " << input_png << endl;
return true;
}
loc_frame++;
if (loc_frame >= total_frames) loc_frame = 0;
if (mem_frame_buffer && (frame_size > 0)) {
if (wcClientFrameLock(client) == WC_OK) {
wcClientFramePushData(client, mem_frame_buffer, frame_size);
}
}
lst_timestamp = cur_timestamp;
need_to_load_next_frame = false;
}
return false;
}
/* Callback. CURL was initialized successfully. */
void onCURLinit(wcHandle self, wcTask task)
{
cout << "CURL initialized" << endl;
}
/* Callback. Authorization was successful. */
void onAuth(wcHandle self, wcTask task)
{
char * res = NULL;
/* Get a new session id and display it on the screen. */
if (wcClientGetStrValue(self, wcstSID, &res) == WC_OK) {
if (res) {
cout << res << endl;
free(res);
}
}
running = 2;
}
/* Callback. The client has been disabled. */
void onDisconnect(wcHandle self, wcTask task)
{
cout << "Client disconnected" << endl;
running = 5;
}
/* Callback. Connection state changed. */
void onConnChanged(wcHandle self, int state)
{
cout << "Client connection changed " << state << endl;
}
/* Callback. Log was changed. */
void onLog(wcHandle self, const char * str)
{
/* Display new log entry. */
cout << str << endl;
}
/* Callback. IO stream closed. */
void onIOTaskFinished(wcHandle client, wcTask tsk)
{
cout << "Output stream closed" << endl;
running = 6;
}
/* Callback. IO stream started. */
void onIOTaskStarted(wcHandle client, wcTask tsk)
{
cout << "Output stream started" << endl;
need_to_load_next_frame = true;
}
/* Callback. Task updated. */
void onTaskSynchronized(wcHandle client, wcTask tsk)
{
/* Get the class for `tsk` */
if (wcTaskGetClass(tsk, &tcid) == WC_OK) {
if (tcid == WC_OUT_STREAM_TASK)
/* The output stream task updated (the previous frame was sent) */
need_to_load_next_frame = true;
}
}
class MyInputParser : public InputParser {
public:
using InputParser::InputParser;
const std::string& getInput() {
static const std::string CMD_INPUT("--input");
static const std::string CMD_INPUT_SYN("-i");
const std::string & res = getCmdOption(CMD_INPUT);
if (res.empty()) {
return getCmdOption(CMD_INPUT_SYN);
} else
return res;
}
virtual void printCommonHelp() override
{
InputParser::printCommonHelp();
cout << " -i, --input The input argument uses C printf format to represent a list of files," << endl;
cout << " i.e. file-%%06d.png to look for files file000001.png to file9999999.png" << endl;
}
};
int main(int argc, char * argv[])
{
/* Parse command line */
MyInputParser input(argc, argv);
const std::string &host = input.getHost();
const std::string &proxy = input.getProxy();
const std::string &user = input.getUserName();
const std::string &pwrd = input.getUserPassword();
std::string device = input.getDevice();
if (device.empty()) device = "device_test001_output";
std::string input_mask = input.getInput();
if (input_mask.empty()) input_mask = "file-%%02d.png";
if (input.showHelp()) {
/* Show help doc */
input.printCommonHelp();
return 0;
}
/* dirname and basename must operate on scratch strings */
char * dirc = strdup(input_mask.c_str());
char * basec = strdup(input_mask.c_str());
input_directory = std::string(dirname(dirc));
input_filter = std::string(basename(basec));
free(dirc);
free(basec);
total_frames = scandir(input_directory.c_str(), &png_files, include_files, alphasort);
if (total_frames <= 0) {
cerr << "no input files found; run with -h for help." << endl;
return 1;
}
lst_timestamp = std::chrono::system_clock::now();
if (!host.empty()){
/* Main part */
/* Configure certificate */
if (input.ignoreTLSCert())
/* Configure proxy */
if (!proxy.empty())
wcClientSetStrValue(client, wcstProxy, proxy.c_str());
/* Configure host */
wcClientSetStrValue(client, wcstHostName, host.c_str());
/* Configure callbacks */
wcSetNotifyCallback(client, wccbkInitCURL, onCURLinit);
wcSetNotifyCallback(client, wccbkDisconnect, onDisconnect);
wcSetConnCallback(client, onConnChanged);
/* Configure streaming callbacks */
wcSetTaskCallback(client, wccbkAfterLaunchOutStream, onIOTaskStarted);
wcSetTaskCallback(client, wccbkSynchroUpdateTask, onTaskSynchronized);
wcSetTaskCallback(client, wccbkSuccessIOStream, onIOTaskFinished);
/* Start client */
wcClientStart(client);
auto start_timestamp = std::chrono::system_clock::now();
while (running < 4)
{
switch (running)
{
case 0:
{
/* Authorize client */
wcClientSetStrValue(client, wcstDeviceName, device.c_str());
wcClientAuth(client, user.c_str(), pwrd.c_str());
running = 1;
break;
}
case 2:
{
/* Start output stream */
wcLaunchOutStream(client, "RAW_PNG", MAX_DELTA, NULL);
running = 3;
break;
}
default:
{
break;
}
}
/* load next frame and push to the output data stack */
if (running == 3) {
if (load_next_frame(client)) return 1;
std::this_thread::sleep_for(std::chrono::milliseconds(20));
} else
std::this_thread::sleep_for(std::chrono::milliseconds(200));
/* Proceed client */
wcClientProceed(client);
auto current_timestamp = std::chrono::system_clock::now();
auto int_s = chrono::duration_cast<chrono::seconds>(current_timestamp - start_timestamp);
if (int_s.count() >= TIME_OUT) {
cout << "timeout" << endl;
break;
}
}
/* Disconnect client */
/* Destroy client */
wcClientDestroy(client);
if (mem_frame_buffer)
free(mem_frame_buffer);
while (total_frames--) free(png_files[total_frames]);
free(png_files);
return 0;
} else {
cout << "should contain at least --host (host-name) option";
}
}
wcRCode DLLEXPORT wcSetTaskCallback(wcHandle client, wcCallback callbackId, TaskNotifyLibFunc func)
Set specified task notify callback for client.
wcRCode DLLEXPORT wcSetNotifyCallback(wcHandle client, wcCallback callbackId, NotifyEventLibFunc func)
Set specified notify callback for client.
wcRCode DLLEXPORT wcSetCStringCallback(wcHandle client, wcCallback callbackId, CStringNotifyLibFunc func)
Set specified notify callback for client to handling the C-style string values.
wcRCode DLLEXPORT wcSetConnCallback(wcHandle client, ConnNotifyEventLibFunc func)
Set specified connection notify callback for client.
@ wccbkAddLog
Added new log entry.
Definition: wcwebcamclient.h:140
@ wccbkSynchroUpdateTask
The IO streaming task signals that a change has occurred.
Definition: wcwebcamclient.h:141
@ wccbkInitCURL
Successful initialization of the multiCURL handle.
Definition: wcwebcamclient.h:135
@ wccbkSuccessAuth
Successful authorization.
Definition: wcwebcamclient.h:136
@ wccbkAfterLaunchOutStream
Outgoing stream started.
Definition: wcwebcamclient.h:145
@ wccbkSuccessIOStream
IO stream terminated for some reason.
Definition: wcwebcamclient.h:146
@ wccbkDisconnect
Client has been disconnected.
Definition: wcwebcamclient.h:138
wcRCode DLLEXPORT wcClientGetStrValue(wcHandle client, wcStateId aStateId, char **aStateVal)
Get a C-style string value for the selected client state.
wcRCode DLLEXPORT wcClientSetStrValue(wcHandle client, wcStateId aStateId, const char *aStateVal)
Set a C-style string value to the selected client state.
wcRCode DLLEXPORT wcClientSetBoolState(wcHandle client, wcStateId aStateId, wcStateVal aStateVal)
Set the boolean value to the selected client state.
@ wcstDeviceName
Definition: wcwebcamclient.h:187
@ wcstSID
Definition: wcwebcamclient.h:188
@ wcstProxy
Definition: wcwebcamclient.h:190
@ wcstHostName
Definition: wcwebcamclient.h:189
@ wcstVerifyTLS
Definition: wcwebcamclient.h:175
wcHandle DLLEXPORT wcClientCreate()
Create client.
wcRCode DLLEXPORT wcClientTasksProceed(wcHandle client)
Call the synchronous client update stage.
wcRCode DLLEXPORT wcClientDisconnect(wcHandle client)
Disconnect client from the server host.
wcRCode DLLEXPORT wcClientProceed(wcHandle client)
Call the asynchronous client update stage.
wcRCode DLLEXPORT wcClientAuth(wcHandle client, const char *aLogin, const char *aPwrd)
Authorize client on the server host.
wcRCode DLLEXPORT wcClientDestroy(wcHandle client)
Destroy client.
wcRCode DLLEXPORT wcClientStart(wcHandle client)
Launch client.
wcRCode DLLEXPORT wcLaunchOutStream(wcHandle client, const char *subProtocol, int delta, void *data)
Launch output stream for authorized client.
wcRCode DLLEXPORT wcClientFrameLock(wcHandle client)
Lock the frame object for threadsafe access to the output data stack.
wcRCode DLLEXPORT wcClientFramePushData(wcHandle client, const void *data, size_t len)
Add a new frame to the outgoing data stack to send.
wcRCode DLLEXPORT wcClientFrameUnLock(wcHandle client)
Unlock the frame object.
const wcRCode WC_OK
Definition: wcwebcamclient.h:74
const wcRCode WC_FALSE
Definition: wcwebcamclient.h:71
wcRCode DLLEXPORT wcTaskGetClass(wcTask task, wcTaskClass *id)
Get the class of the task.
const wcTaskClass WC_OUT_STREAM_TASK
The Output-stream task class (for UPLOAD requests)
Definition: wcwebcamclient.h:114
const wcTaskClass WC_BASE_TASK
The Base task class.
Definition: wcwebcamclient.h:108
int wcHandle
Client handle.
Definition: wcwebcamclient.h:99
wcCallbackTask wcTask
Pointer to task.
Definition: wcwebcamclient.h:102
const uint16_t WC_STREAM_FRAME_HEADER_SIZE
The size of frame header (6 bytes)
Definition: wcwebcamclient.h:119
const uint16_t WC_FRAME_START_SEQ
The frame header sequence.
Definition: wcwebcamclient.h:121
uint32_t wcTaskClass
Task class.
Definition: wcwebcamclient.h:105