Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2ef8a32
Makefile: conform to gnu99 standards
peterbarker Jun 13, 2017
3d68e89
Add a .gitignore
peterbarker Jun 14, 2017
0fb539d
files: index.html: adjust welcome message
peterbarker Aug 18, 2017
514d219
Use console_printf in place of fprintf
peterbarker Jun 21, 2017
010f86e
main: avoid calling mavlink_broadcast with bad fd
peterbarker Jun 13, 2017
9efce71
web_server: Add UDP address:port output
peterbarker Jun 20, 2017
61758fb
web_server: allow specification of IP address for TCP listen
peterbarker Jun 21, 2017
a408636
web_server: Make serial port read errors non-fatal
peterbarker Jun 21, 2017
30c8d45
web_server: Increase debug on accept failure
peterbarker Jun 21, 2017
abb5e29
web_server: Move http accept into own function, add error checking
peterbarker Jun 21, 2017
67f1df0
files: filesystem.html: option to start in a specific directory
peterbarker Aug 31, 2017
1631374
linux: use clock_gettime to reliably get boot time
peterbarker Sep 7, 2017
34a7fb3
linux: invoke shutdown(1) for __reboot
peterbarker Jun 14, 2017
2e1dfb2
posix: add disk_info function
peterbarker Jun 14, 2017
de8842e
posix: add file_listdir
peterbarker Jun 14, 2017
dcfc332
posix: download_filesystem support
peterbarker Sep 1, 2017
2cadb19
linux: Get system information statistics working
peterbarker Jun 22, 2017
d6bca4c
web_server: catch SIGPIPE
peterbarker Sep 28, 2017
f4b8aab
filesystem.html: sort by filename
peterbarker Oct 5, 2017
be7dfdc
Added copy URL button
shortstheory Aug 2, 2018
4bdf991
Merge branch 'apsync' into video_streaming
shortstheory Aug 2, 2018
feaa880
Fixed layout bug
shortstheory Aug 7, 2018
b0d8471
Changed SIG for killing the stream server
shortstheory Aug 8, 2018
cd4289c
Fixed segfault bug
shortstheory Aug 8, 2018
bad39ae
Changed location of stream server binary
shortstheory Sep 18, 2018
2374b8f
Changed stream_server executable path
shortstheory Sep 18, 2018
9ce1ff1
mavlink: update to ardupilot master
peterbarker Mar 14, 2019
80d1085
Makefile: use mavgen shipped with mavlink's pymavlink
peterbarker Mar 14, 2019
ab9568b
Merged with current version of AP master
shortstheory Mar 27, 2019
e801235
Fixed CORS requests from Chrome/Safari
shortstheory Mar 31, 2019
11a48e5
Disabled the file recorder
shortstheory Apr 1, 2019
e4b0b54
Re-enabled quality for UVC cameras
shortstheory Apr 1, 2019
2b4dcf1
Removed file recording checkbox
shortstheory May 21, 2019
b820315
Changed path of stream_server executable for APStreamline
shortstheory Jul 31, 2019
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ all: files/embedded.c mavlink web_server
mavlink: generated/mavlink/ardupilotmega/mavlink.h

generated/mavlink/ardupilotmega/mavlink.h:
mavgen.py --lang C modules/mavlink/message_definitions/v1.0/ardupilotmega.xml -o generated/mavlink --wire-protocol=2.0
modules/mavlink/pymavlink/tools/mavgen.py --lang C modules/mavlink/message_definitions/v1.0/ardupilotmega.xml -o generated/mavlink --wire-protocol=2.0

web_server: $(OBJ) files/embedded.c
$(CC) -o web_server $(OBJ) $(LIBS)
Expand Down
205 changes: 205 additions & 0 deletions files/apsync/video.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
<!DOCTYPE HTML>
<html manifest="manifest.appcache">
<head>
<title>ArduPilot</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="/css/styles.css">
<script type="text/javascript" src="/js/config.js"></script>
<script type="text/javascript" src="/js/cors.js"></script>
<script type="text/javascript" src="/js/mavlink.js"></script>
</head>
<body>

<p><a href="/index.html"><img src="/images/main_logo.png" alt="ArduPilot"></a></p>
<h1>Video Streaming</h1>
<div id="serverStoppedDiv">
<p>Network Interface: <select id="if_select_box"></select><button type="submit" style="padding:1px" value="set_if" onclick="start_server();">Start RTSP Server</button></p>
</div>
<div id="serverStartedDiv">
<table id="rtsp_table" class="parameters">
<tr><th>Device</th><th>Camera Name</th><th class="alnright">RTSP URL</th><th>Copy URL</th><th title='Auto quality adjusts the resolution and framerate according to the available bandwidth and packet loss estimates. The stream has to be restarted for UVC and Jetson CSI cameras for the changes to take effect.'>Quality ⓘ</th><th>Apply</th></tr>
</table>
<p><input type="submit" name="action" value="Stop RTSP Server" onclick="stop_server();"></p>
</div>
<script>
var VideoPresetsEnum = {
VIDEO_320x240x15: 0, VIDEO_640x480x15: 1, VIDEO_1280x720x15: 2,
VIDEO_320x240x30: 3, VIDEO_640x480x30: 4, VIDEO_1280x720x30: 5,
VIDEO_320x240x60: 6, VIDEO_640x480x60: 7, VIDEO_1280x720x60: 8
};
var VideoPresetsName = [
"320x240 15fps", "640x480 15fps", "1280x720 15fps",
"320x240 30fps", "640x480 30fps", "1280x720 30fps",
"320x240 60fps", "640x480 60fps", "1280x720 60fps"
];

var AUTO_PRESET = 1024;

window.onload = refresh;

function removeOptions(selectbox) {
var i;
for(i = selectbox.options.length - 1 ; i >= 0 ; i--) {
selectbox.remove(i);
}
}

function start_server() {
var select = document.getElementById("if_select_box");
console.log(select.value);
var interface = select.value;
command_send("start_rtsp_server(" + interface + ")", {"onload" : late_refresh});
}

function stop_server() {
command_send("stop_rtsp_server()", {"onload" : refresh});
}

function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}

function late_refresh() {
// The stream server needs some time to wake up
timed_refresh(1000);
}

function timed_refresh(millis) {
sleep(millis);
refresh();
}

function fill_interface_list(interface_list) {
console.log(interface_list);
var select = document.getElementById("if_select_box");
removeOptions(select);
var iflist = JSON.parse(interface_list);
var interfaces = iflist["interfaces"];
for (var i = 0; i < interfaces.length; i++) {
select.options[select.options.length] = new Option(interfaces[i], interfaces[i]);
}
}

// Hacky code to copy text from a clipboard
function CopyToClipboard(containerid) {
console.log("Copying " + containerid.innerHTML);
var textarea = document.createElement('textarea');
textarea.id = 'temp_element';
textarea.style.height = 0
document.body.appendChild(textarea)
textarea.value = containerid.innerHTML
var selector = document.querySelector('#temp_element')
selector.select()
document.execCommand('copy')
document.body.removeChild(textarea)
}

function fill_rtsp_table(cam_data) {
console.log(cam_data);
var table = document.getElementById("rtsp_table");

// delete any existing rows
var nrows = table.rows.length;
for (var i=nrows-1; i>0; i--) {
table.deleteRow(i);
}

if (cam_data == "ERROR\n") {
console.log("error str");
document.getElementById("serverStartedDiv").style.display = 'none';
document.getElementById("serverStoppedDiv").style.display = 'block';
command_send("get_interfaces()", { "onload" : fill_interface_list });
} else {
document.getElementById("serverStartedDiv").style.display = 'block';
document.getElementById("serverStoppedDiv").style.display = 'none';
var plist;
try {
plist = JSON.parse(cam_data);
plist.sort(function(a,b) {
if (a.mount < b.mount) {
return -1;
}
if (a.mount > b.mount) {
return 1;
}
return 0;
});
} catch(e) {
console.log(e);
return;
}

var n = plist.length;
for (var i=0; i<n; i++) {
var row = table.insertRow(table.rows.length);
var rowdata = plist[i];
var device_cell = row.insertCell(0);
var ip = rowdata.ip;
var port = rowdata.port;
device_cell.innerHTML = rowdata.dev_mount;
device_cell.id = "device" + i;
row.insertCell(1).innerHTML = rowdata.name;
var mount_url = "rtsp://" + ip + ":" + port + rowdata.mount;
var mount_url_box = "mountbox" + i;
row.insertCell(2).innerHTML = '<div id="' + mount_url_box+ '">' + mount_url + "</div>";
console.log("MOUNT URL " + mount_url);
row.insertCell(3).innerHTML = '<button type="submit" style="width: 100%; height: 100%;" onclick="CopyToClipboard(' + mount_url_box + ')">Copy</button>';

var form_quality = "form_quality" + i;
var quality_select_box = "quality_select_box"+i;
var set_quality_button = "set_quality_button"+i;
var record_video_checkbox = "record"+i;

var current_quality = rowdata.current_quality;
var curr_recording = rowdata.recording;

row.insertCell(4).innerHTML = '<select id="' + quality_select_box + '"></select>';
row.insertCell(5).innerHTML = '<button type="submit" value="' + i.toString() + '" onclick="send_settings(this)">Set</button>';

var select = document.getElementById(quality_select_box);
select.options[select.options.length] = new Option("Auto", AUTO_PRESET);
for (var j = 0; j < Object.keys(VideoPresetsEnum).length; j++) {
var test_value = 0;
test_value = 1 << j;
if (test_value & rowdata.frame_property_bitmask) {
console.log("Supports " + VideoPresetsName[j]);
select.options[select.options.length] = new Option(VideoPresetsName[j], j);
}
}
document.getElementById(quality_select_box).value = current_quality;
}
}
}

function send_settings(button) {
console.log("Reached this " + button.value);
var table = document.getElementById("rtsp_table");
var quality_select_box = document.getElementById("quality_select_box" + button.value);
var record_video_checkbox = document.getElementById("record"+button.value);

var device_name = document.getElementById("device" + button.value).innerHTML;
var qual_setting = quality_select_box.value;

var record_val = 0;

var sdp_string = "SDP$" + device_name + " " + qual_setting + " " + record_val;
command_send("set_device_quality(" + sdp_string + ")", {"onload" : after_sdp});
console.log("QualValue - " + sdp_string);
timed_refresh(100);
}

function after_sdp() {
console.log("SDP string sent");
}

function refresh() {
command_send("get_camera_details()", { "onload" : fill_rtsp_table });
}

</script>
</html>
1 change: 1 addition & 0 deletions files/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ <h2>ArduPilot Web Server</h2>
<li><a href="apsync/status.html">System Status</a></li>
<li><a href="apsync/calibration.html">Calibration</a></li>
<li><a href="parameters.html">Flight Parameters</a></li>
<li><a href="apsync/video.html">Video Streaming</a></li>
</ul>


Expand Down
2 changes: 1 addition & 1 deletion files/js/config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
var drone_url = "http://192.168.99.1";
var drone_url = "http://127.0.0.1";

/* URL for Ublox MGA data */
var mga_data_url = "http://gps.tridgell.net/data/mga-offline.ubx";
Expand Down
82 changes: 82 additions & 0 deletions functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "mavlink_json.h"
#include "mavlink_core.h"
#include "cgi.h"
#include "rtsp_ipc.h"

#ifdef _POSIX_VERSION
#include "posix/functions.h"
Expand All @@ -16,6 +17,10 @@
#include "linux/functions.h"
#endif

#ifdef _POSIX_VERSION
#include "posix/functions.h"
#endif

#ifdef SYSTEM_FREERTOS
#include "../mavlink_wifi.h"
#include "video_main.h"
Expand Down Expand Up @@ -755,6 +760,78 @@ static void get_param(struct template_state *tmpl, const char *name, const char
}
}

/*
gets the list of avaialble cameras from the RTSP stream server over a local socket
*/
static void get_camera_details(struct template_state *tmpl, const char *name, const char *value, int argc, char **argv)
{
char msg[IPC_BUFFER_SIZE];
char result[IPC_BUFFER_SIZE];
msg[0] = '\0';
get_server_response(GET_DEVICE_PROPS, msg, NULL);
if (strlen(msg)) {
process_server_response(msg, result);
sock_printf(tmpl->sock, "%s", result);
} else {
sock_printf(tmpl->sock, "%s", "ERROR");
}
}

/*
find the list of available network interfaces for starting the RTSP stream server
*/
static void get_interfaces(struct template_state *tmpl, const char *name, const char *value, int argc, char **argv)
{
char interfaces_list[IPC_BUFFER_SIZE];
get_interfaces_list(interfaces_list);
sock_printf(tmpl->sock, "%s", interfaces_list);
}

/*
sets the properties of a camera through IPC with the RTSP stream server
*/
static void set_device_quality(struct template_state *tmpl, const char *name, const char *value, int argc, char **argv)
{
if (argc) {
if (send_server_message(argv[0])) {
printf("Error in setting camera properties\n");
}
}
}

static pid_t stream_server_pid = -1;

/*
forks to create a new process for the RTSP stream
*/
static void start_rtsp_server(struct template_state *tmpl, const char *name, const char *value, int argc, char **argv)
{
if (stream_server_pid == -1) {
stream_server_pid = fork();
if (stream_server_pid < 0) {
printf("Fork failed\n");
} else if (stream_server_pid == 0) {
if (execlp("stream_server", "stream_server", argv[0], NULL)==-1) {
printf("Error in launching the stream server\n");
}
}
} else {
printf("Stream server running with PID: %d\n", stream_server_pid);
}
}

static void stop_rtsp_server(struct template_state *tmpl, const char *name, const char *value, int argc, char **argv)
{
if (stream_server_pid != -1) {
if(!kill(stream_server_pid, SIGINT)) {
stream_server_pid = -1;
printf("Stream server successfully killed\n");
} else {
printf("Could not kill the stream server\n");
}
}
}

/*
get parameter list
*/
Expand Down Expand Up @@ -1061,4 +1138,9 @@ void functions_init(struct template_state *tmpl)
tmpl->put(tmpl, "process_content", "", process_content);
tmpl->put(tmpl, "get_param", "", get_param);
tmpl->put(tmpl, "get_param_list", "", get_param_list);
tmpl->put(tmpl, "get_camera_details", "", get_camera_details);
tmpl->put(tmpl, "set_device_quality", "", set_device_quality);
tmpl->put(tmpl, "start_rtsp_server", "", start_rtsp_server);
tmpl->put(tmpl, "stop_rtsp_server", "", stop_rtsp_server);
tmpl->put(tmpl, "get_interfaces", "", get_interfaces);
}
Loading