Skip to content

Commit

Permalink
Added support for Adaptive Video streaming
Browse files Browse the repository at this point in the history
  • Loading branch information
shortstheory committed Jul 30, 2018
1 parent cf31727 commit df93b31
Show file tree
Hide file tree
Showing 7 changed files with 465 additions and 4 deletions.
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 Mount Point</th><th onclick="alert('\'Auto\' quality adjusts the resolution and framerate according to the available bandwidth and packet loss estimates')">Quality ⓘ</th><th onclick="alert('Enabling \'Record\' starts the recording of the live-streamed video in the Companion Computer\'s home directory. The stream must be opened on the client for the recording to start. This does not work currently work for the \'Auto\' quality setting and for the Pi camera.')">Record ⓘ</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]);
}
}

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;
row.insertCell(2).innerHTML = mount_url;

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;

// H.264 cams don't do file recording properly, so let's disable it for now
if (rowdata.camtype != 1) {
row.insertCell(3).innerHTML = '<select id="' + quality_select_box + '"></select>';
} else {
row.insertCell(3).innerHTML = '<select disabled id="' + quality_select_box + '"></select>';
}
if (rowdata.camtype != 2) {
row.insertCell(4).innerHTML = '<input type="checkbox" id=' + record_video_checkbox + ' margin: auto; text-align:center; >';
} else {
row.insertCell(4).innerHTML = '<input disabled type="checkbox" id=' + record_video_checkbox + ' margin: auto; text-align:center; >';
}
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;
document.getElementById(record_video_checkbox).checked = curr_recording;
}
}
}

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;

if (record_video_checkbox.checked == true) {
record_val = 1;
} else {
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
77 changes: 77 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 Down Expand Up @@ -755,6 +756,77 @@ 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];
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, SIGTERM)) {
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 +1133,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

0 comments on commit df93b31

Please sign in to comment.