Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 92 additions & 1 deletion res/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -368,10 +368,12 @@
function onLoad() {
apiRead('audio');
apiRead('jpeg');
apiRead('mp4');
apiRead('mjpeg');
apiRead('mp4');
apiRead('night');
apiRead('onvif');
apiRead('record');
apiRead('rtsp');
toggleRecord(document.getElementById('record_state'));
apiRead('time');

Expand Down Expand Up @@ -723,6 +725,94 @@ <h2 class="unl">OSD</h2>
</div>
</div>
</div>
<input type="checkbox" id="network-modal">
<div class="c modal vh-100 w-100 scrolly">
<div class="c bg-black menu pv2 row" tabindex="0">
<b><label for="network-modal" class="lbl-modal ph2 gray">Divinus</label></b>
<div class="col">
<a href="javascript:saveConfig()" class="ph2">Save</a>
<a href="javascript:restartApp()" class="ph2">Restart</a>
<label for="network-modal" class="btn primary close">Close</label>
</div>
</div>
<div class="c cont">
<h2 class="unl">Network</h2>
<div class="row">
<div class="6 col box">
<fieldset>
<legend>RTSP</legend>
<table class="w-100">
<tr>
<td><label for="rtsp_enable">Enabled</label></td>
<td><select id="rtsp_enable">
<option value="false">OFF</option>
<option value="true">ON</option>
</select></td>
</tr>
<tr>
<td><label for="rtsp_port">Port</label></td>
<td><input type="number" id="rtsp_port" min="1" max="65535"></td>
</tr>
<tr>
<td><label for="rtsp_enable_auth">Authentication</label></td>
<td><select id="rtsp_enable_auth">
<option value="false">OFF</option>
<option value="true">ON</option>
</select></td>
</tr>
<tr>
<td><label for="rtsp_auth_user">Username</label></td>
<td><input type="text" id="rtsp_auth_user"></td>
</tr>
<tr>
<td><label for="rtsp_auth_pass">Password</label></td>
<td><input type="password" id="rtsp_auth_pass"></td>
</tr>
<tr>
<td><label for="rtsp_audio_codec">Audio&nbsp;codec</label></td>
<td><select id="rtsp_audio_codec">
<option value="pcma">G.711A</option>
<option value="pcmu">G.711U</option>
<option value="mp3">MP3</option>
</select></td>
</tr>
</table>
<a href="javascript:apiApply('rtsp')" class="btn right">Apply</a>
</fieldset>
</div>
<div class="6 col box">
<fieldset>
<legend>ONVIF</legend>
<table class="w-100">
<tr>
<td><label for="onvif_enable">Enabled</label></td>
<td><select id="onvif_enable">
<option value="false">OFF</option>
<option value="true">ON</option>
</select></td>
</tr>
<tr>
<td><label for="onvif_enable_auth">Authentication</label></td>
<td><select id="onvif_enable_auth">
<option value="false">OFF</option>
<option value="true">ON</option>
</select></td>
</tr>
<tr>
<td><label for="onvif_auth_user">Username</label></td>
<td><input type="text" id="onvif_auth_user"></td>
</tr>
<tr>
<td><label for="onvif_auth_pass">Password</label></td>
<td><input type="password" id="onvif_auth_pass"></td>
</tr>
</table>
<a href="javascript:apiApply('onvif')" class="btn right">Apply</a>
</fieldset>
</div>
</div>
</div>
</div>
<input type="checkbox" id="system-modal">
<div class="c modal vh-100 w-100 scrolly">
<div class="c bg-black menu pv2 row" tabindex="0">
Expand Down Expand Up @@ -827,6 +917,7 @@ <h2 class="unl">System</h2>
<div class="col">
<label for="live-modal" class="lbl-modal ph2 white">Live</label>
<label for="media-modal" class="lbl-modal ph2 white">Media</label>
<label for="network-modal" class="lbl-modal ph2 white">Network</label>
<label for="osd-modal" class="lbl-modal ph2 white">OSD</label>
<label for="system-modal" class="lbl-modal ph2 white">System</label>
<a href="image.jpg" class="ph2 white" target="_blank">Snapshot</a>
Expand Down
14 changes: 14 additions & 0 deletions src/app_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ int app_config_save(void) {
fprintf(file, "rtsp:\n");
fprintf(file, " enable: %s\n", app_config.rtsp_enable ? "true" : "false");
fprintf(file, " port: %d\n", app_config.rtsp_port);
fprintf(file, " audio_codec: %s\n", app_config.rtsp_audio_codec);
fprintf(file, " enable_auth: %s\n", app_config.rtsp_enable_auth ? "true" : "false");
fprintf(file, " auth_user: %s\n", app_config.rtsp_auth_user);
fprintf(file, " auth_pass: %s\n", app_config.rtsp_auth_pass);
Expand Down Expand Up @@ -236,6 +237,7 @@ enum ConfigError app_config_parse(void) {

app_config.rtsp_enable = false;
app_config.rtsp_port = 554;
strcpy(app_config.rtsp_audio_codec, "mp3"); /* upstream default; the Fullhan image ships pcma in its yaml */
app_config.rtsp_enable_auth = false;
app_config.rtsp_auth_user[0] = '\0';
app_config.rtsp_auth_pass[0] = '\0';
Expand Down Expand Up @@ -432,6 +434,18 @@ enum ConfigError app_config_parse(void) {

parse_bool(&ini, "rtsp", "enable", &app_config.rtsp_enable);
parse_int(&ini, "rtsp", "port", 0, USHRT_MAX, &app_config.rtsp_port);
{
/* Bounded, and only the codecs the RTP path implements: anything
* else would advertise a payload type nothing produces. */
char codec[16] = {0};
if (parse_param_value_n(&ini, "rtsp", "audio_codec", codec, sizeof(codec)) == CONFIG_OK) {
if (EQUALS(codec, "pcma") || EQUALS(codec, "pcmu") || EQUALS(codec, "mp3")) {
strncpy(app_config.rtsp_audio_codec, codec,
sizeof(app_config.rtsp_audio_codec) - 1);
app_config.rtsp_audio_codec[sizeof(app_config.rtsp_audio_codec) - 1] = 0;
}
}
}
if (app_config.rtsp_enable) {
parse_bool(&ini, "rtsp", "enable_auth", &app_config.rtsp_enable_auth);
parse_param_value(
Expand Down
1 change: 1 addition & 0 deletions src/app_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ struct AppConfig {
char rtsp_auth_user[32];
char rtsp_auth_pass[32];
int rtsp_port;
char rtsp_audio_codec[8]; /* pcma (G.711A), pcmu (G.711U) or mp3 */

// [record]
bool record_enable;
Expand Down
4 changes: 2 additions & 2 deletions src/fmt/bitbuf.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "bitbuf.h"

Expand Down Expand Up @@ -57,8 +58,7 @@ enum BufError put_to_offset(
const uint32_t size) {
chk_ptr uint32_t pos = offset + size;
if (pos >= ptr->size)
chk_realloc for (uint32_t i = 0; i < size; i++) ptr->buf[offset + i] =
data[i];
chk_realloc memcpy(ptr->buf + offset, data, size);
return BUF_OK;
}
enum BufError put(struct BitBuf *ptr, const char *data, const uint32_t size) {
Expand Down
3 changes: 2 additions & 1 deletion src/fmt/moov.c
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,8 @@ enum BufError write_DecoderConfig(struct BitBuf *ptr, const struct MoovInfo *moo
err = put_u32_be(ptr, 0);
chk_err;

err = put_u8(ptr, moov_info->audio_codec);
err = put_u8(ptr, (moov_info->audio_codec == ACODEC_ID_MP3 && moov_info->audio_samplerate >= 32000) ?
0x6B /* MPEG-1 audio */ : moov_info->audio_codec);
chk_err; // objectTypeIndication, https://mp4ra.org/#/object_types
err = put_u8(ptr, 0x15);
chk_err; // streamType
Expand Down
4 changes: 4 additions & 0 deletions src/fmt/mp4.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ enum BufError create_header(char is_h265) {

void mp4_set_config(short width, short height, char framerate, char acodec,
unsigned short bitrate, char channels, unsigned int srate) {
/* A new stream configuration (codec, size) invalidates the cached header
* and parameter sets; without this a codec switch keeps sending the old one */
buf_header.offset = 0;
buf_sps_len = buf_pps_len = buf_vps_len = 0;
vid_width = width;
vid_height = height;
vid_framerate = framerate;
Expand Down
21 changes: 19 additions & 2 deletions src/hal/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ enum ConfigError section_pos(
enum ConfigError parse_param_value(
struct IniConfig *ini, const char *section, const char *param_name,
char *param_value) {
return parse_param_value_n(ini, section, param_name, param_value, (size_t)-1);
}

enum ConfigError parse_param_value_n(
struct IniConfig *ini, const char *section, const char *param_name,
char *param_value, size_t param_value_size) {
int start_pos = 0;
int end_pos = 0;
if (strlen(section) > 0) {
Expand All @@ -82,8 +88,19 @@ enum ConfigError parse_param_value(
if (match > 0 || (end_pos >= 0 && end_pos - start_pos < m[1].rm_so))
return CONFIG_PARAM_NOT_FOUND;

int res = sprintf(param_value, "%.*s", (int)(m[1].rm_eo - m[1].rm_so),
ini->str + start_pos + m[1].rm_so);
int len = (int)(m[1].rm_eo - m[1].rm_so);
int res;
if (param_value_size != (size_t)-1) {
if ((size_t)len >= param_value_size)
len = (int)param_value_size - 1;
res = snprintf(param_value, param_value_size, "%.*s", len,
ini->str + start_pos + m[1].rm_so);
if (res >= (int)param_value_size)
res = (int)param_value_size - 1;
} else {
res = sprintf(param_value, "%.*s", len,
ini->str + start_pos + m[1].rm_so);
}
param_value[res] = 0;

if (res >= 2 && param_value[0] == '"' && param_value[res - 1] == '"') {
Expand Down
6 changes: 6 additions & 0 deletions src/hal/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ bool open_config(struct IniConfig *ini, FILE **file);
enum ConfigError find_sections(struct IniConfig *ini);
enum ConfigError section_pos(
struct IniConfig *ini, const char *section, int *start_pos, int *end_pos);
/* Bounded form of parse_param_value(): the unbounded one sprintf()s the matched
* value straight into the caller's buffer, which overflows small fields. */
enum ConfigError parse_param_value_n(
struct IniConfig *ini, const char *section, const char *param_name,
char *param_value, size_t param_value_size);

enum ConfigError parse_param_value(
struct IniConfig *ini, const char *section, const char *param_name,
char *param_value);
Expand Down
28 changes: 27 additions & 1 deletion src/hal/tools.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,28 @@ int compile_regex(regex_t *r, const char *regex_text) {
}


int escape_json(char *dst, const char *src, size_t maxlen)
{
size_t o = 0;

for (; src && *src && o + 7 < maxlen; src++) {
unsigned char c = (unsigned char)*src;

if (c == '"' || c == '\\') {
dst[o++] = '\\';
dst[o++] = c;
} else if (c < 0x20) {
o += snprintf(dst + o, maxlen - o, "\\u%04x", c);
} else {
dst[o++] = c;
}
}

dst[o] = 0;
return o;
}


int escape_url(char *dst, const char *src, size_t maxlen) {
static const char hex[] = "0123456789ABCDEF";
int len = 0;
Expand Down Expand Up @@ -518,14 +540,18 @@ void uuid_generate(char *uuid) {
uuid[36] = '\0';
}

int parse_ranged(const char *value, long min, long max, long *out) {
int parse_ranged(const char *value, long min, long max, long *out)
{
char *remain;
long result;

errno = 0;
result = strtol(value, &remain, 10);

if (remain == value || (remain && *remain) || errno == ERANGE ||
result < min || result > max)
return 0;

*out = result;
return 1;
}
2 changes: 2 additions & 0 deletions src/hal/tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ int color_parse(const char *str);

int compile_regex(regex_t *r, const char *regex_text);

int escape_json(char *dst, const char *src, size_t maxlen);

int escape_url(char *dst, const char *src, size_t maxlen);

void generate_nonce(char *nonce, size_t len);
Expand Down
1 change: 1 addition & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ int main(int argc, char *argv[]) {

if (app_config.rtsp_enable) {
rtspHandle = rtsp_create(RTSP_MAXIMUM_CONNECTIONS, app_config.rtsp_port, 1);
rtsp_latch_audio_codec();
HAL_INFO("rtsp", "Started listening for clients...\n");
if (app_config.rtsp_enable_auth) {
if (EMPTY(app_config.rtsp_auth_user) || EMPTY(app_config.rtsp_auth_pass))
Expand Down
Loading