From b79dfae8b38d1fa7619da7a105cbb94a2db305eb Mon Sep 17 00:00:00 2001 From: Michal Fratczak Date: Fri, 24 Apr 2020 17:35:54 +0200 Subject: [PATCH] ssdv over websocket --- code/Decoder/Decoder.h | 15 +++- code/Decoder/ssdv_wrapper.cpp | 16 +++- code/Decoder/ssdv_wrapper.h | 13 +-- code/webClient/js/NetTransport.js | 19 ++++ code/webClient/js/gui.js | 10 ++- code/webClient/js/habdec_commands.js | 9 ++ code/websocketServer/Base64.h | 124 +++++++++++++++++++++++++++ code/websocketServer/main.cpp | 21 +++++ 8 files changed, 215 insertions(+), 12 deletions(-) create mode 100644 code/websocketServer/Base64.h diff --git a/code/Decoder/Decoder.h b/code/Decoder/Decoder.h index 5522129..f1d9612 100644 --- a/code/Decoder/Decoder.h +++ b/code/Decoder/Decoder.h @@ -127,8 +127,15 @@ public: bool livePrint() const { return live_print_; } void livePrint(bool i_live) { live_print_ = i_live; } - std::function sentence_callback_; // callback on each successfull sentence decode - std::function character_callback_; // callback on each decoded character + + // callback on each successfull sentence decode. callsign, sentence_data, CRC + std::function sentence_callback_; + + // callback on each decoded characters + std::function character_callback_; + + // callback on each decoded ssdv packet. callsign, image_id, jpeg_bytes + std::function)> ssdv_callback_; // SSDV SSDV_wraper_t ssdv_; @@ -560,7 +567,7 @@ void habdec::Decoder::process() vector raw_chars = rtty_.get(); - ssdv_.push(raw_chars); + const bool b_new_ssdv = ssdv_.push(raw_chars); vector printable_chars; copy_if( raw_chars.begin(), raw_chars.end(), back_inserter(printable_chars), @@ -618,6 +625,8 @@ void habdec::Decoder::process() character_callback_time = std::chrono::high_resolution_clock::now(); } + if(b_new_ssdv && ssdv_callback_) + ssdv_callback_( ssdv_.last_img_k_.first, ssdv_.last_img_k_.second, ssdv_.get_jpeg(ssdv_.last_img_k_) ); // overflow protection if(rtty_char_stream_.size() > 1000) diff --git a/code/Decoder/ssdv_wrapper.cpp b/code/Decoder/ssdv_wrapper.cpp index e2c8be9..9254bb2 100644 --- a/code/Decoder/ssdv_wrapper.cpp +++ b/code/Decoder/ssdv_wrapper.cpp @@ -112,8 +112,7 @@ void SSDV_wraper_t::make_jpeg( const packet_set_t& packet_list, const image_key_ using namespace std; auto& last_pkt = *packet_list.rbegin(); - // size_t jpeg_sz = 3 * last_pkt->header_.width * last_pkt->header_.height; - size_t jpeg_sz = 3*1024*1024; + size_t jpeg_sz = 3 * last_pkt->header_.width * last_pkt->header_.height; if( jpegs_.find(image_key) == jpegs_.end() ) jpegs_[image_key] = vector( jpeg_sz ); @@ -129,8 +128,21 @@ void SSDV_wraper_t::make_jpeg( const packet_set_t& packet_list, const image_key_ ssdv_dec_feed( &ssdv, p_pkt->data_.data() ); ssdv_dec_get_jpeg(&ssdv, &p_data, &jpeg_sz); + + last_img_k_ = image_key; } +std::vector SSDV_wraper_t::get_jpeg(const image_key_t& image_key) + +{ + if( jpegs_.find(image_key) == jpegs_.end() ) { + return std::vector(0); + } + + return jpegs_[image_key]; +} + + void SSDV_wraper_t::save_jpeg( const image_key_t& image_key ) { diff --git a/code/Decoder/ssdv_wrapper.h b/code/Decoder/ssdv_wrapper.h index fcd6aaa..4ff86b1 100644 --- a/code/Decoder/ssdv_wrapper.h +++ b/code/Decoder/ssdv_wrapper.h @@ -32,12 +32,6 @@ namespace habdec class SSDV_wraper_t { - -public: - bool push(const std::vector& i_chars); - std::string base_file() const { return base_file_; } - void base_file(const std::string& i_fn) { base_file_ = i_fn; } - private: // incomming data buffer std::vector buff_; @@ -77,6 +71,13 @@ private: void make_jpeg(const packet_set_t&, const image_key_t&); void save_jpeg(const image_key_t&); +public: + bool push(const std::vector& i_chars); + std::string base_file() const { return base_file_; } + void base_file(const std::string& i_fn) { base_file_ = i_fn; } + image_key_t last_img_k_ = {"",0}; + std::vector get_jpeg(const image_key_t&); + }; } \ No newline at end of file diff --git a/code/webClient/js/NetTransport.js b/code/webClient/js/NetTransport.js index a363d56..392a341 100644 --- a/code/webClient/js/NetTransport.js +++ b/code/webClient/js/NetTransport.js @@ -78,3 +78,22 @@ function DecodeDemod(i_buffer, i_offset) return header; } + + +function DecodeJpegBase64(i_buffer, i_offset) +{ + var dv = new DataView(i_buffer, i_offset); + var offset = 0; + var callsign_size = dv.getInt32(offset, true); offset += 4; + var image_id = dv.getInt32(offset, true); offset += 4; + + offset += 4; // why ? + var callsing_data = new Uint8Array( i_buffer, offset, callsign_size); offset += callsign_size; + var callsing_str = new TextDecoder("utf-8").decode(callsing_data); + + // rest is JPEG data + var jpeg_data = new Uint8Array( i_buffer, offset ); + var jpeg_str = new TextDecoder("utf-8").decode(jpeg_data); + + return [callsing_str, image_id, jpeg_str]; +} diff --git a/code/webClient/js/gui.js b/code/webClient/js/gui.js index ed0ccff..48cdff2 100644 --- a/code/webClient/js/gui.js +++ b/code/webClient/js/gui.js @@ -667,6 +667,14 @@ function HABDEC_BUILD_UI(parent_div) var div_server = HABDEC_BUILD_UI_Server(); // + // SSDV + var ssdv_div = document.createElement("div"); + var ssdv_info = document.createElement("text"); + ssdv_info.id = "HabDec_SSDV_Info"; + var ssdv_image = document.createElement("img"); + ssdv_image.id = "HabDec_SSDV_Image"; + ssdv_div.appendChild(ssdv_image); + ssdv_div.appendChild(ssdv_info); // flights list var div_payloads_wrapper = document.createElement("div"); @@ -685,6 +693,7 @@ function HABDEC_BUILD_UI(parent_div) // parent_div.display.height = "1000px"; parent_div.appendChild(div_power); parent_div.appendChild(div_demod_and_ctrls); + parent_div.appendChild(ssdv_div); parent_div.appendChild(div_extra_radio_buttons); parent_div.appendChild(div_server); // parent_div.appendChild(div_payloads_wrapper); @@ -697,7 +706,6 @@ function HABDEC_BUILD_UI(parent_div) window.addEventListener('message', HB_WinMsgHandler); - // HD_ApplyeColorScheme( HD_COLOR_SCHEMES["DEFAULT"] ); } diff --git a/code/webClient/js/habdec_commands.js b/code/webClient/js/habdec_commands.js index f2adee0..90515c0 100644 --- a/code/webClient/js/habdec_commands.js +++ b/code/webClient/js/habdec_commands.js @@ -118,6 +118,15 @@ function ws_onMessage(evt) G_DEMOD_DATA = DecodeDemod(evt.data, 4); RefreshDemod_lastReq = 0; } + else if(what == "SDV_") // SSDV jpeg + { + [callsing_str, image_id, jpeg_en64] = DecodeJpegBase64(evt.data, 4); + console.debug("SSDV", callsing_str, image_id); + var img = document.getElementById("HabDec_SSDV_Image"); + img.setAttribute('src', 'data:image/jpeg;base64,' + jpeg_en64); + var tex= document.getElementById("HabDec_SSDV_Info"); + tex.innerHTML = callsing_str + " / " + image_id; + } } else { diff --git a/code/websocketServer/Base64.h b/code/websocketServer/Base64.h new file mode 100644 index 0000000..cdfdc04 --- /dev/null +++ b/code/websocketServer/Base64.h @@ -0,0 +1,124 @@ +#ifndef _MACARON_BASE64_H_ +#define _MACARON_BASE64_H_ + +/** + * The MIT License (MIT) + * Copyright (c) 2016 tomykaira + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +namespace macaron { + +class Base64 { + public: + + static std::string Encode(const std::string data) { + static constexpr char sEncodingTable[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; + + size_t in_len = data.size(); + size_t out_len = 4 * ((in_len + 2) / 3); + std::string ret(out_len, '\0'); + size_t i; + char *p = const_cast(ret.c_str()); + + for (i = 0; i < in_len - 2; i += 3) { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)]; + *p++ = sEncodingTable[data[i + 2] & 0x3F]; + } + if (i < in_len) { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + if (i == (in_len - 1)) { + *p++ = sEncodingTable[((data[i] & 0x3) << 4)]; + *p++ = '='; + } + else { + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)]; + } + *p++ = '='; + } + + return ret; + } + + static std::string Decode(const std::string& input, std::string& out) { + static constexpr unsigned char kDecodingTable[] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + }; + + size_t in_len = input.size(); + if (in_len % 4 != 0) return "Input data size is not a multiple of 4"; + + size_t out_len = in_len / 4 * 3; + if (input[in_len - 1] == '=') out_len--; + if (input[in_len - 2] == '=') out_len--; + + out.resize(out_len); + + for (size_t i = 0, j = 0; i < in_len;) { + uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + + uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6); + + if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF; + if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF; + if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF; + } + + return ""; + } + +}; + +} + +#endif /* _MACARON_BASE64_H_ */ diff --git a/code/websocketServer/main.cpp b/code/websocketServer/main.cpp index a7c8fc8..f449458 100755 --- a/code/websocketServer/main.cpp +++ b/code/websocketServer/main.cpp @@ -40,6 +40,7 @@ #include "GLOBALS.h" #include "ws_server.h" #include "common/git_repo_sha1.h" +#include "Base64.h" using namespace std; @@ -499,6 +500,26 @@ int main(int argc, char** argv) p_ws_server->sessions_send(p_msg); }; + DECODER.ssdv_callback_ = + [p_ws_server](string callsign, int image_id, std::vector jpeg) + { + shared_ptr p_msg = make_shared(); + p_msg->is_binary_ = true; + // p_msg->to_all_clients_ = true; + + // header + pair ssdv_header{ (int)callsign.size(), (int)image_id }; + p_msg->data_stream_<<"SDV_"; + p_msg->data_stream_.write( reinterpret_cast(&ssdv_header), sizeof(ssdv_header) ); + p_msg->data_stream_<data_stream_<<(char)b64_jpeg_out[i]; + p_ws_server->sessions_send(p_msg); + }; + + // START THREADS //