cmd: add /status.json
to describe what and how it works
This commit is contained in:
parent
aafd120af3
commit
b2dfba5577
@ -13,6 +13,8 @@ extern unsigned char html_webrtc_html[];
|
||||
extern unsigned int html_webrtc_html_len;
|
||||
extern camera_t *camera;
|
||||
|
||||
void camera_status_json(http_worker_t *worker, FILE *stream);
|
||||
|
||||
static void http_once(FILE *stream, void (*fn)(FILE *stream, const char *data), void *headersp)
|
||||
{
|
||||
bool *headers = headersp;
|
||||
@ -101,6 +103,7 @@ http_method_t http_methods[] = {
|
||||
{ "GET", "/webrtc", http_content, "text/html", html_webrtc_html, 0, &html_webrtc_html_len },
|
||||
{ "POST", "/webrtc", http_webrtc_offer },
|
||||
{ "GET", "/option", camera_http_option },
|
||||
{ "GET", "/status.json", camera_status_json },
|
||||
{ "GET", "/", http_content, "text/html", html_index_html, 0, &html_index_html_len },
|
||||
{ "OPTIONS", "*/", http_cors_options },
|
||||
{ }
|
||||
|
@ -13,6 +13,7 @@ extern camera_options_t camera_options;
|
||||
extern http_server_options_t http_options;
|
||||
extern http_method_t http_methods[];
|
||||
extern rtsp_options_t rtsp_options;
|
||||
extern webrtc_options_t webrtc_options;
|
||||
|
||||
camera_t *camera;
|
||||
|
||||
@ -79,7 +80,9 @@ int main(int argc, char *argv[])
|
||||
goto error;
|
||||
}
|
||||
|
||||
webrtc_server();
|
||||
if (!webrtc_options.disabled && webrtc_server(&webrtc_options) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
camera = camera_open(&camera_options);
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "util/opts/fourcc.h"
|
||||
#include "device/camera/camera.h"
|
||||
#include "output/rtsp/rtsp.h"
|
||||
#include "output/webrtc/webrtc.h"
|
||||
#include "output/output.h"
|
||||
|
||||
camera_options_t camera_options = {
|
||||
@ -53,6 +54,9 @@ rtsp_options_t rtsp_options = {
|
||||
.port = 0,
|
||||
};
|
||||
|
||||
webrtc_options_t webrtc_options = {
|
||||
};
|
||||
|
||||
option_value_t camera_formats[] = {
|
||||
{ "DEFAULT", 0 },
|
||||
{ "YUYV", V4L2_PIX_FMT_YUYV },
|
||||
|
139
cmd/camera-streamer/status.cc
Normal file
139
cmd/camera-streamer/status.cc
Normal file
@ -0,0 +1,139 @@
|
||||
extern "C" {
|
||||
|
||||
#include "util/http/http.h"
|
||||
#include "util/opts/fourcc.h"
|
||||
#include "device/buffer_list.h"
|
||||
#include "device/buffer_lock.h"
|
||||
#include "device/camera/camera.h"
|
||||
#include "output/rtsp/rtsp.h"
|
||||
#include "output/webrtc/webrtc.h"
|
||||
#include "output/output.h"
|
||||
|
||||
extern camera_t *camera;
|
||||
extern http_server_options_t http_options;
|
||||
extern rtsp_options_t rtsp_options;
|
||||
extern webrtc_options_t webrtc_options;
|
||||
|
||||
};
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
static nlohmann::json serialize_buf_list(buffer_list_t *buf_list)
|
||||
{
|
||||
if (!buf_list)
|
||||
return false;
|
||||
|
||||
nlohmann::json output;
|
||||
output["name"] = buf_list->name;
|
||||
output["width"] = buf_list->fmt.width;
|
||||
output["height"] = buf_list->fmt.height;
|
||||
output["format"] = fourcc_to_string(buf_list->fmt.format).buf;
|
||||
output["nbufs"] = buf_list->nbufs;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static nlohmann::json serialize_buf_lock(buffer_lock_t *buf_lock)
|
||||
{
|
||||
if (!buf_lock)
|
||||
return false;
|
||||
|
||||
nlohmann::json output;
|
||||
output["name"] = buf_lock->name;
|
||||
output["enabled"] = (buf_lock->buf_list != NULL);
|
||||
|
||||
if (buf_lock->buf_list) {
|
||||
output["width"] = buf_lock->buf_list->fmt.width;
|
||||
output["height"] = buf_lock->buf_list->fmt.height;
|
||||
output["source"] = buf_lock->buf_list->name;
|
||||
output["frames"] = buf_lock->counter;
|
||||
output["refs"] = buf_lock->refs;
|
||||
output["dropped"] = buf_lock->dropped;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
static nlohmann::json devices_status_json()
|
||||
{
|
||||
nlohmann::json devices;
|
||||
|
||||
for (int i = 0; i < MAX_DEVICES; i++) {
|
||||
if (!camera->devices[i])
|
||||
continue;
|
||||
|
||||
device_t *device = camera->devices[i];
|
||||
nlohmann::json device_json;
|
||||
device_json["name"] = device->name;
|
||||
device_json["path"] = device->path;
|
||||
device_json["allow_dma"] = device->opts.allow_dma;
|
||||
device_json["output"] = serialize_buf_list(device->output_list);
|
||||
for (int j = 0; j < device->n_capture_list; j++) {
|
||||
device_json["captures"][j] = serialize_buf_list(device->capture_lists[j]);
|
||||
}
|
||||
devices += device_json;
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
static nlohmann::json links_status_json()
|
||||
{
|
||||
nlohmann::json links;
|
||||
|
||||
for (int i = 0; i < camera->nlinks; i++) {
|
||||
link_t *link = &camera->links[i];
|
||||
|
||||
nlohmann::json link_json;
|
||||
link_json["source"] = link->source->name;
|
||||
for (int j = 0; link->sinks[j]; j++) {
|
||||
link_json["sinks"][j] = link->sinks[j]->name;
|
||||
}
|
||||
for (int j = 0; j < link->n_callbacks; j++) {
|
||||
link_json["callbacks"][j] = link->callbacks[j].name;
|
||||
}
|
||||
links += link_json;
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
static std::string strip_host_port(const char *host)
|
||||
{
|
||||
const char *sep = strchr(host, ':');
|
||||
|
||||
return sep ? std::string(host, sep) : host;
|
||||
}
|
||||
|
||||
static nlohmann::json get_url(bool running, const char *output, const char *protocol, const char *host, int port, const char *path)
|
||||
{
|
||||
nlohmann::json endpoint;
|
||||
|
||||
endpoint["enabled"] = running;
|
||||
|
||||
if (running) {
|
||||
endpoint["output"] = output;
|
||||
endpoint["uri"] = std::string(protocol) + "://" + strip_host_port(host) + ":" + std::to_string(port) + path;
|
||||
}
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
extern "C" void camera_status_json(http_worker_t *worker, FILE *stream)
|
||||
{
|
||||
nlohmann::json message;
|
||||
|
||||
message["outputs"]["snapshot"] = serialize_buf_lock(&snapshot_lock);
|
||||
message["outputs"]["stream"] = serialize_buf_lock(&stream_lock);
|
||||
message["outputs"]["video"] = serialize_buf_lock(&video_lock);
|
||||
|
||||
message["devices"] = devices_status_json();
|
||||
message["links"] = links_status_json();
|
||||
|
||||
message["endpoints"]["rtsp"] = get_url(video_lock.buf_list != NULL && rtsp_options.running, "video", "rtsp", worker->host, rtsp_options.port, "/stream.h264");
|
||||
message["endpoints"]["webrtc"] = get_url(video_lock.buf_list != NULL && webrtc_options.running, "video", "http", worker->host, http_options.port, "/webrtc");
|
||||
message["endpoints"]["video"] = get_url(video_lock.buf_list != NULL, "video", "http", worker->host, http_options.port, "/video");
|
||||
message["endpoints"]["stream"] = get_url(stream_lock.buf_list != NULL, "stream", "http", worker->host, http_options.port, "/stream");
|
||||
message["endpoints"]["snapshot"] = get_url(snapshot_lock.buf_list != NULL, "snapshot", "http", worker->host, http_options.port, "/stream");
|
||||
|
||||
http_write_response(stream, "200 OK", "application/json", message.dump().c_str(), 0);
|
||||
}
|
@ -244,6 +244,7 @@ extern "C" int rtsp_server(rtsp_options_t *options)
|
||||
buffer_lock_register_notify_buffer(&video_lock, rtsp_h264_capture);
|
||||
|
||||
pthread_create(&rtsp_thread, NULL, rtsp_server_thread, env);
|
||||
options->running = true;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct rtsp_options_s {
|
||||
bool running;
|
||||
uint port;
|
||||
} rtsp_options_t;
|
||||
|
||||
|
@ -5,6 +5,11 @@ extern "C" {
|
||||
#include "device/buffer_lock.h"
|
||||
#include "device/device.h"
|
||||
#include "output/output.h"
|
||||
#include "util/http/http.h"
|
||||
#include "util/opts/log.h"
|
||||
#include "util/opts/fourcc.h"
|
||||
#include "util/opts/control.h"
|
||||
#include "device/buffer.h"
|
||||
};
|
||||
|
||||
#ifdef USE_LIBDATACHANNEL
|
||||
@ -381,10 +386,12 @@ extern "C" void http_webrtc_offer(http_worker_t *worker, FILE *stream)
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void webrtc_server()
|
||||
extern "C" int webrtc_server(webrtc_options_t *options)
|
||||
{
|
||||
buffer_lock_register_check_streaming(&video_lock, webrtc_h264_needs_buffer);
|
||||
buffer_lock_register_notify_buffer(&video_lock, webrtc_h264_capture);
|
||||
options->running = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // USE_LIBDATACHANNEL
|
||||
@ -394,8 +401,9 @@ extern "C" void http_webrtc_offer(http_worker_t *worker, FILE *stream)
|
||||
http_404(stream, NULL);
|
||||
}
|
||||
|
||||
extern "C" void webrtc_server()
|
||||
extern "C" int webrtc_server(webrtc_options_t *options)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // USE_LIBDATACHANNEL
|
||||
|
@ -1,11 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/http/http.h"
|
||||
#include "util/opts/log.h"
|
||||
#include "util/opts/fourcc.h"
|
||||
#include "util/opts/control.h"
|
||||
#include "device/buffer.h"
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct http_worker_s http_worker_t;
|
||||
|
||||
typedef struct webrtc_options_s {
|
||||
bool running;
|
||||
bool disabled;
|
||||
} webrtc_options_t;
|
||||
|
||||
// WebRTC
|
||||
void http_webrtc_offer(http_worker_t *worker, FILE *stream);
|
||||
void webrtc_server();
|
||||
int webrtc_server(webrtc_options_t *options);
|
||||
|
@ -17,6 +17,7 @@
|
||||
#define HEADER_RANGE "Range:"
|
||||
#define HEADER_CONTENT_LENGTH "Content-Length:"
|
||||
#define HEADER_USER_AGENT "User-Agent:"
|
||||
#define HEADER_HOST "Host:"
|
||||
|
||||
static int http_listen(int port, int maxcons)
|
||||
{
|
||||
@ -92,6 +93,7 @@ static void http_process(http_worker_t *worker, FILE *stream)
|
||||
|
||||
worker->range_header[0] = 0;
|
||||
worker->user_agent[0] = 0;
|
||||
worker->host[0] = 0;
|
||||
worker->content_length = -1;
|
||||
|
||||
// request_uri
|
||||
@ -124,11 +126,13 @@ static void http_process(http_worker_t *worker, FILE *stream)
|
||||
break;
|
||||
|
||||
if (strcasestr(line, HEADER_RANGE) == line) {
|
||||
strcpy(worker->range_header, line);
|
||||
strcpy(worker->range_header, trim(line));
|
||||
} else if (strcasestr(line, HEADER_CONTENT_LENGTH) == line) {
|
||||
worker->content_length = atoi(line + strlen(HEADER_CONTENT_LENGTH));
|
||||
worker->content_length = atoi(trim(line + strlen(HEADER_CONTENT_LENGTH)));
|
||||
} else if (strcasestr(line, HEADER_USER_AGENT) == line) {
|
||||
strcpy(worker->user_agent, line + strlen(HEADER_USER_AGENT));
|
||||
strcpy(worker->user_agent, trim(line + strlen(HEADER_USER_AGENT)));
|
||||
} else if (strcasestr(line, HEADER_HOST) == line) {
|
||||
strcpy(worker->host, trim(line + strlen(HEADER_HOST)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@ typedef struct http_worker_s {
|
||||
char client_method[BUFSIZE];
|
||||
char range_header[BUFSIZE];
|
||||
char user_agent[BUFSIZE];
|
||||
char host[BUFSIZE];
|
||||
char *request_method;
|
||||
char *request_uri;
|
||||
char *request_params;
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "util/opts/opts.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
char *
|
||||
strstrn(const char *s, const char *find, size_t len)
|
||||
@ -101,3 +102,17 @@ int ioctl_retried(const char *name, int fd, int request, void *arg)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *trim(char *s)
|
||||
{
|
||||
// skip left side white spaces
|
||||
while (isspace (*s))
|
||||
s++;
|
||||
|
||||
// skip right side white spaces
|
||||
char *e = s + strlen(s) - 1;
|
||||
while (e >= s && isspace(*e))
|
||||
*e-- = 0;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ bool filter_log(const char *filename);
|
||||
uint64_t get_monotonic_time_us(struct timespec *ts, struct timeval *tv);
|
||||
uint64_t get_time_us(clockid_t clock, struct timespec *ts, struct timeval *tv, int64_t delays_us);
|
||||
int shrink_to_block(int size, int block);
|
||||
char *trim(char *s);
|
||||
|
||||
int ioctl_retried(const char *name, int fd, int request, void *arg);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user