From 2832efc2613aec289b38114e03e3a962f3d033e3 Mon Sep 17 00:00:00 2001 From: scottxu Date: Sat, 27 Feb 2021 00:40:14 +0800 Subject: [PATCH 1/9] no message --- rtsp-server/xop/H264Source.cpp | 12 +++++++++++- rtsp_output.cpp | 21 +++++++++++---------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/rtsp-server/xop/H264Source.cpp b/rtsp-server/xop/H264Source.cpp index 02c2d4bd..c6d9a900 100644 --- a/rtsp-server/xop/H264Source.cpp +++ b/rtsp-server/xop/H264Source.cpp @@ -47,7 +47,17 @@ string H264Source::GetMediaDescription(uint16_t port) string H264Source::GetAttribute() { - return string("a=rtpmap:96 H264/90000"); + char buf[500] = { 0 }; + sprintf(buf, "a=rtpmap:96 H264/90000"); + + sprintf(buf+strlen(buf), + "a=fmtp:96 profile-level-id=1;" + "mode=AAC-hbr;" + "sizelength=13;indexlength=3;indexdeltalength=3;" + "config=%04u"); + + return string(buf); + //return string("a=rtpmap:96 H264/90000"); } bool H264Source::HandleFrame(MediaChannelId channel_id, AVFrame frame) diff --git a/rtsp_output.cpp b/rtsp_output.cpp index 6e345f1e..1082aaf3 100644 --- a/rtsp_output.cpp +++ b/rtsp_output.cpp @@ -331,8 +331,15 @@ static void rtsp_output_video(void *param, struct encoder_packet *packet) rtsp_out_data *out_data = (rtsp_out_data *)param; xop::AVFrame videoFrame = {0}; videoFrame.timestamp = get_timestamp(90000, packet); + + if (packet->pts == packet->dts) + videoFrame.type = xop::VIDEO_FRAME_I; + else if (packet->pts > packet->dts) + videoFrame.type = xop::VIDEO_FRAME_P; + else + videoFrame.type = xop::VIDEO_FRAME_B; + if (packet->keyframe) { - videoFrame.type = xop::VIDEO_FRAME_I; uint8_t *header; size_t header_size = get_video_header( obs_output_get_video_encoder(out_data->output), @@ -343,15 +350,9 @@ static void rtsp_output_video(void *param, struct encoder_packet *packet) memcpy(videoFrame.buffer.get() + header_size, packet->data, packet->size); } else { - if (packet->pts == packet->dts) - videoFrame.type = xop::VIDEO_FRAME_I; - else if (packet->pts > packet->dts) - videoFrame.type = xop::VIDEO_FRAME_P; - else - videoFrame.type = xop::VIDEO_FRAME_B; - videoFrame.size = packet->size; - videoFrame.buffer.reset(new uint8_t[videoFrame.size]); - memcpy(videoFrame.buffer.get(), packet->data, packet->size); + videoFrame.size = packet->size; + videoFrame.buffer.reset(new uint8_t[videoFrame.size]); + memcpy(videoFrame.buffer.get(), packet->data, packet->size); } struct queue_frame queue_frame; From c77a771635977abb026727341c67fae608ca7165 Mon Sep 17 00:00:00 2001 From: scottxu Date: Tue, 2 Mar 2021 21:58:58 +0800 Subject: [PATCH 2/9] Add format parameters for video in SDP. --- helper.h | 31 ++++++++++++++++ rtsp-server/xop/H264Parser.cpp | 21 ++++++++++- rtsp-server/xop/H264Parser.h | 5 ++- rtsp-server/xop/H264Source.cpp | 68 +++++++++++++++++++++++++++------- rtsp-server/xop/H264Source.h | 18 ++++++++- rtsp-server/xop/H265Source.cpp | 2 +- rtsp_output.cpp | 57 ++++++++++++++++++---------- 7 files changed, 163 insertions(+), 39 deletions(-) diff --git a/helper.h b/helper.h index fe714799..1a7814a0 100644 --- a/helper.h +++ b/helper.h @@ -2,6 +2,7 @@ #include #include #include +#include #ifndef RTSP_HELPER_H #define RTSP_HELPER_H @@ -53,4 +54,34 @@ static config_t *rtsp_properties_open_config() return config; } +static void rtsp_output_avc_get_sps_pps(const uint8_t *data, size_t size, + const uint8_t **sps, size_t *sps_size, + const uint8_t **pps, size_t *pps_size) +{ + const uint8_t *nal_start, *nal_end; + const uint8_t *end = data + size; + int type; + + nal_start = obs_avc_find_startcode(data, end); + while (true) { + while (nal_start < end && !*(nal_start++)) + ; + + if (nal_start == end) + break; + + nal_end = obs_avc_find_startcode(nal_start, end); + + type = nal_start[0] & 0x1F; + if (type == OBS_NAL_SPS) { + *sps = nal_start; + *sps_size = nal_end - nal_start; + } else if (type == OBS_NAL_PPS) { + *pps = nal_start; + *pps_size = nal_end - nal_start; + } + + nal_start = nal_end; + } +} #endif // RTSP_HELPER_H diff --git a/rtsp-server/xop/H264Parser.cpp b/rtsp-server/xop/H264Parser.cpp index d3f26dd2..493c695a 100644 --- a/rtsp-server/xop/H264Parser.cpp +++ b/rtsp-server/xop/H264Parser.cpp @@ -1,7 +1,8 @@ -#include "H264Parser.h" +#include "H264Parser.h" #include using namespace xop; +using namespace std; Nal H264Parser::findNal(const uint8_t *data, uint32_t size) { @@ -69,4 +70,22 @@ Nal H264Parser::findNal(const uint8_t *data, uint32_t size) return nal; } +vector H264Parser::RemoveEmulationBytes(vector const from) +{ + size_t i = 0; + vector to(from.size()); + auto toiter = to.begin(); + while (i < from.size() && toiter + 1 < to.end()) { + if (i + 2 < from.size() && from[i] == 0 && from[i + 1] == 0 && + from[i + 2] == 3) { + *toiter++ = *toiter++ = 0; + i += 3; + } else { + *toiter++ = from[i]; + i += 1; + } + } + to.resize(toiter - to.begin()); + return to; +} diff --git a/rtsp-server/xop/H264Parser.h b/rtsp-server/xop/H264Parser.h index 454c6122..9a8dde05 100644 --- a/rtsp-server/xop/H264Parser.h +++ b/rtsp-server/xop/H264Parser.h @@ -1,8 +1,9 @@ -#ifndef XOP_H264_PARSER_H +#ifndef XOP_H264_PARSER_H #define XOP_H264_PARSER_H #include #include +#include namespace xop { @@ -13,6 +14,8 @@ class H264Parser { public: static Nal findNal(const uint8_t *data, uint32_t size); + + static std::vector RemoveEmulationBytes(std::vector const from); private: diff --git a/rtsp-server/xop/H264Source.cpp b/rtsp-server/xop/H264Source.cpp index c6d9a900..dc60a190 100644 --- a/rtsp-server/xop/H264Source.cpp +++ b/rtsp-server/xop/H264Source.cpp @@ -8,6 +8,8 @@ #endif #include "H264Source.h" +#include +#include #include #include #if defined(WIN32) || defined(_WIN32) @@ -15,21 +17,32 @@ #else #include #endif +extern "C" { +#include "libb64/include/b64/cencode.h" +} using namespace xop; using namespace std; -H264Source::H264Source(uint32_t framerate) - : framerate_(framerate) +H264Source::H264Source(const vector sps, const vector pps, + uint32_t framerate) + : framerate_(framerate), + sps_(sps), pps_(pps) { payload_ = 96; media_type_ = H264; clock_rate_ = 90000; } -H264Source* H264Source::CreateNew(uint32_t framerate) +H264Source *H264Source::CreateNew(uint32_t framerate) +{ + return new H264Source(vector(), vector(), framerate); +} + +H264Source *H264Source::CreateNew(const vector sps, + const vector pps, uint32_t framerate) { - return new H264Source(framerate); + return new H264Source(sps, pps, framerate); } H264Source::~H264Source() @@ -47,17 +60,30 @@ string H264Source::GetMediaDescription(uint16_t port) string H264Source::GetAttribute() { - char buf[500] = { 0 }; - sprintf(buf, "a=rtpmap:96 H264/90000"); + auto rtpmap = string("a=rtpmap:96 H264/90000\r\n"); + + if (!sps_.empty() && !pps_.empty()) { + char const *fmtp = "a=fmtp:96 packetization-mode=1;" + "profile-level-id=%06X;" + "sprop-parameter-sets=%s,%s"; + + auto pps_base64 = Base64Encode(pps_.data(), pps_.size()); + auto sps_base64 = Base64Encode(sps_.data(), sps_.size()); + + uint32_t profile_level_id = (sps_.at(1) << 16) | + (sps_.at(2) << 8) | sps_.at(3); + + size_t buf_size = 1 + strlen(fmtp) + 6 + sps_base64.length() + + pps_base64.length(); + auto buf = vector(buf_size); - sprintf(buf+strlen(buf), - "a=fmtp:96 profile-level-id=1;" - "mode=AAC-hbr;" - "sizelength=13;indexlength=3;indexdeltalength=3;" - "config=%04u"); + sprintf(buf.data(), fmtp, profile_level_id, sps_base64.c_str(), + pps_base64.c_str()); - return string(buf); - //return string("a=rtpmap:96 H264/90000"); + rtpmap.append(buf.data()); + } + + return rtpmap; } bool H264Source::HandleFrame(MediaChannelId channel_id, AVFrame frame) @@ -149,4 +175,18 @@ uint32_t H264Source::GetTimestamp() return (uint32_t)((time_point.time_since_epoch().count() + 500) / 1000 * 90 ); //#endif } - \ No newline at end of file + +std::string H264Source::Base64Encode(const void *input, size_t size) +{ + std::vector buffer(size / 3 * 4 + (size % 3 > 0 ? 4 : 0) + 1); + base64_encodestate b64encoder; + base64_init_encodestate(&b64encoder); + + auto length = base64_encode_block( + reinterpret_cast(input), + int(size), buffer.data(), &b64encoder); + base64_encode_blockend(buffer.data() + length, &b64encoder); + + return std::string(buffer.cbegin(), buffer.cend() - 1); +} + diff --git a/rtsp-server/xop/H264Source.h b/rtsp-server/xop/H264Source.h index f1e0ee6f..84f79797 100644 --- a/rtsp-server/xop/H264Source.h +++ b/rtsp-server/xop/H264Source.h @@ -13,7 +13,11 @@ namespace xop class H264Source : public MediaSource { public: - static H264Source* CreateNew(uint32_t framerate=25); + static H264Source *CreateNew(uint32_t framerate = 25); + + static H264Source *CreateNew(const std::vector sps, + const std::vector pps, + uint32_t framerate = 25); ~H264Source(); void SetFramerate(uint32_t framerate) @@ -31,9 +35,19 @@ class H264Source : public MediaSource static uint32_t GetTimestamp(); private: - H264Source(uint32_t framerate); + H264Source(const std::vector sps, + const std::vector pps, + uint32_t framerate); + + static std::string Base64Encode(const void *input, size_t size); uint32_t framerate_ = 25; + + uint32_t profileLevelId_; + + std::vector sps_; + + std::vector pps_; }; } diff --git a/rtsp-server/xop/H265Source.cpp b/rtsp-server/xop/H265Source.cpp index 530eb7fd..898e1e74 100644 --- a/rtsp-server/xop/H265Source.cpp +++ b/rtsp-server/xop/H265Source.cpp @@ -1,4 +1,4 @@ -// PHZ +// PHZ // 2018-6-7 #if defined(WIN32) || defined(_WIN32) diff --git a/rtsp_output.cpp b/rtsp_output.cpp index 1082aaf3..538c2e8e 100644 --- a/rtsp_output.cpp +++ b/rtsp_output.cpp @@ -4,8 +4,10 @@ #include #include #include +#include #include "threadsafe_queue.h" #include "rtsp_output.h" +#include "helper.h" #define USEC_IN_SEC 1000000 @@ -69,7 +71,7 @@ static void send_prestart_signal(rtsp_out_data *out_data) } static bool rtsp_output_start_hotkey(void *data, obs_hotkey_pair_id id, - obs_hotkey_t *hotkey, bool pressed) + obs_hotkey_t *hotkey, bool pressed) { UNUSED_PARAMETER(id); UNUSED_PARAMETER(hotkey); @@ -85,7 +87,7 @@ static bool rtsp_output_start_hotkey(void *data, obs_hotkey_pair_id id, } static bool rtsp_output_stop_hotkey(void *data, obs_hotkey_pair_id id, - obs_hotkey_t *hotkey, bool pressed) + obs_hotkey_t *hotkey, bool pressed) { UNUSED_PARAMETER(id); UNUSED_PARAMETER(hotkey); @@ -122,8 +124,7 @@ static void *rtsp_output_create(obs_data_t *settings, obs_output_t *output) add_prestart_signal(data); data->start_stop_hotkey = obs_hotkey_pair_register_output( - output, - "RtspOutput.Start", obs_module_text("StartOutput"), + output, "RtspOutput.Start", obs_module_text("StartOutput"), "RtspOutput.Stop", obs_module_text("StopOutput"), rtsp_output_start_hotkey, rtsp_output_stop_hotkey, data, data); @@ -136,8 +137,7 @@ static void set_output_error(rtsp_out_data *out_data, char code, ...) { char *message; char *lookup_string; - switch (code) - { + switch (code) { case ERROR_BEGIN_DATA_CAPTURE: message = "can't begin data capture"; lookup_string = "ErrorBeginDataCapture"; @@ -167,8 +167,8 @@ static void set_output_error(rtsp_out_data *out_data, char code, ...) #if defined(WIN32) || defined(_WIN32) vsprintf_s(buffer, obs_module_text(lookup_string), args); #else - vsnprintf(buffer, sizeof(buffer), obs_module_text(lookup_string), - args); + vsnprintf(buffer, sizeof(buffer), + obs_module_text(lookup_string), args); #endif va_end(args); obs_output_set_last_error(out_data->output, buffer); @@ -216,14 +216,36 @@ static bool rtsp_output_start(void *data) uint32_t audio_sample_rate = obs_encoder_get_sample_rate( obs_output_get_audio_encoder(out_data->output, 0)); double video_frame_rate = video_output_get_frame_rate(video); + const uint8_t *sps = nullptr; + size_t sps_size = 0; + const uint8_t *pps = nullptr; + size_t pps_size = 0; + { + uint8_t *extra_data = nullptr; + size_t extra_data_size = 0; + if (obs_encoder_get_extra_data( + obs_output_get_video_encoder(out_data->output), + &extra_data, &extra_data_size)) { + rtsp_output_avc_get_sps_pps(extra_data, extra_data_size, + &sps, &sps_size, &pps, + &pps_size); + } + } + out_data->audio_timestamp_clock = audio_sample_rate; out_data->frame_queue = std::make_unique>(); xop::MediaSession *session = xop::MediaSession::CreateNew("live"); - session->AddSource(xop::channel_0, xop::H264Source::CreateNew( - (uint32_t)video_frame_rate)); + + session->AddSource( + xop::channel_0, + xop::H264Source::CreateNew( + xop::H264Parser::RemoveEmulationBytes( + vector(sps, sps + sps_size)), + vector(pps, pps + pps_size), + (uint32_t)video_frame_rate)); session->AddSource(xop::channel_1, xop::AACSource::CreateNew(audio_sample_rate, audio_channels, false)); @@ -332,13 +354,6 @@ static void rtsp_output_video(void *param, struct encoder_packet *packet) xop::AVFrame videoFrame = {0}; videoFrame.timestamp = get_timestamp(90000, packet); - if (packet->pts == packet->dts) - videoFrame.type = xop::VIDEO_FRAME_I; - else if (packet->pts > packet->dts) - videoFrame.type = xop::VIDEO_FRAME_P; - else - videoFrame.type = xop::VIDEO_FRAME_B; - if (packet->keyframe) { uint8_t *header; size_t header_size = get_video_header( @@ -349,10 +364,12 @@ static void rtsp_output_video(void *param, struct encoder_packet *packet) memcpy(videoFrame.buffer.get(), header, header_size); memcpy(videoFrame.buffer.get() + header_size, packet->data, packet->size); + videoFrame.type = xop::VIDEO_FRAME_I; } else { - videoFrame.size = packet->size; - videoFrame.buffer.reset(new uint8_t[videoFrame.size]); - memcpy(videoFrame.buffer.get(), packet->data, packet->size); + videoFrame.size = packet->size; + videoFrame.buffer.reset(new uint8_t[videoFrame.size]); + memcpy(videoFrame.buffer.get(), packet->data, packet->size); + videoFrame.type = xop::VIDEO_FRAME_P; } struct queue_frame queue_frame; From 667ca7ab62b08e962699b596c3a6019a7a4a1d23 Mon Sep 17 00:00:00 2001 From: scottxu Date: Tue, 2 Mar 2021 21:59:17 +0800 Subject: [PATCH 3/9] Add libb64. --- rtsp-server/3rdpart/libb64/CMakeLists.txt | 16 ++ rtsp-server/3rdpart/libb64/LICENSE | 29 ++++ rtsp-server/3rdpart/libb64/README | 138 ++++++++++++++++++ .../3rdpart/libb64/include/b64/cdecode.h | 28 ++++ .../3rdpart/libb64/include/b64/cencode.h | 31 ++++ .../3rdpart/libb64/include/b64/decode.h | 70 +++++++++ .../3rdpart/libb64/include/b64/encode.h | 77 ++++++++++ rtsp-server/3rdpart/libb64/src/cdecode.c | 88 +++++++++++ rtsp-server/3rdpart/libb64/src/cencode.c | 109 ++++++++++++++ 9 files changed, 586 insertions(+) create mode 100644 rtsp-server/3rdpart/libb64/CMakeLists.txt create mode 100644 rtsp-server/3rdpart/libb64/LICENSE create mode 100644 rtsp-server/3rdpart/libb64/README create mode 100644 rtsp-server/3rdpart/libb64/include/b64/cdecode.h create mode 100644 rtsp-server/3rdpart/libb64/include/b64/cencode.h create mode 100644 rtsp-server/3rdpart/libb64/include/b64/decode.h create mode 100644 rtsp-server/3rdpart/libb64/include/b64/encode.h create mode 100644 rtsp-server/3rdpart/libb64/src/cdecode.c create mode 100644 rtsp-server/3rdpart/libb64/src/cencode.c diff --git a/rtsp-server/3rdpart/libb64/CMakeLists.txt b/rtsp-server/3rdpart/libb64/CMakeLists.txt new file mode 100644 index 00000000..9da8083c --- /dev/null +++ b/rtsp-server/3rdpart/libb64/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.5) +project(libb64) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +include_directories("include") + +file(GLOB libb64_SOURCES + src/*.c) + +file(GLOB libb64_HEADERS + include/b64/*.h) + +add_library(libb64 STATIC + ${libb64_SOURCES} + ${libb64_HEADERS}) diff --git a/rtsp-server/3rdpart/libb64/LICENSE b/rtsp-server/3rdpart/libb64/LICENSE new file mode 100644 index 00000000..a6b56069 --- /dev/null +++ b/rtsp-server/3rdpart/libb64/LICENSE @@ -0,0 +1,29 @@ +Copyright-Only Dedication (based on United States law) +or Public Domain Certification + +The person or persons who have associated work with this document (the +"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of +his knowledge, the work of authorship identified is in the public domain of the +country from which the work is published, or (b) hereby dedicates whatever +copyright the dedicators holds in the work of authorship identified below (the +"Work") to the public domain. A certifier, moreover, dedicates any copyright +interest he may have in the associated work, and for these purposes, is +described as a "dedicator" below. + +A certifier has taken reasonable steps to verify the copyright status of this +work. Certifier recognizes that his good faith efforts may not shield him from +liability if in fact the work certified is not in the public domain. + +Dedicator makes this dedication for the benefit of the public at large and to +the detriment of the Dedicator's heirs and successors. Dedicator intends this +dedication to be an overt act of relinquishment in perpetuity of all present +and future rights under copyright law, whether vested or contingent, in the +Work. Dedicator understands that such relinquishment of all rights includes +the relinquishment of all rights to enforce (by lawsuit or otherwise) those +copyrights in the Work. + +Dedicator recognizes that, once placed in the public domain, the Work may be +freely reproduced, distributed, transmitted, used, modified, built upon, or +otherwise exploited by anyone for any purpose, commercial or non-commercial, +and in any way, including by methods that have not yet been invented or +conceived. \ No newline at end of file diff --git a/rtsp-server/3rdpart/libb64/README b/rtsp-server/3rdpart/libb64/README new file mode 100644 index 00000000..be93b7b4 --- /dev/null +++ b/rtsp-server/3rdpart/libb64/README @@ -0,0 +1,138 @@ +b64: Base64 Encoding/Decoding Routines +====================================== + +Overview: +-------- +libb64 is a library of ANSI C routines for fast encoding/decoding data into and +from a base64-encoded format. C++ wrappers are included, as well as the source +code for standalone encoding and decoding executables. + +base64 consists of ASCII text, and is therefore a useful encoding for storing +binary data in a text file, such as xml, or sending binary data over text-only +email. + +References: +---------- +* Wikipedia article: + http://en.wikipedia.org/wiki/Base64 +* base64, another implementation of a commandline en/decoder: + http://www.fourmilab.ch/webtools/base64/ + +Why? +--- +I did this because I need an implementation of base64 encoding and decoding, +without any licensing problems. Most OS implementations are released under +either the GNU/GPL, or a BSD-variant, which is not what I require. + +Also, the chance to actually use the co-routine implementation in code is rare, +and its use here is fitting. I couldn't pass up the chance. +For more information on this technique, see "Coroutines in C", by Simon Tatham, +which can be found online here: +http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html + +So then, under which license do I release this code? On to the next section... + +License: +------- +This work is released under into the Public Domain. +It basically boils down to this: I put this work in the public domain, and you +can take it and do whatever you want with it. + +An example of this "license" is the Creative Commons Public Domain License, a +copy of which can be found in the LICENSE file, and also online at +http://creativecommons.org/licenses/publicdomain/ + +Commandline Use: +--------------- +There is a new executable available, it is simply called base64. +It can encode and decode files, as instructed by the user. + +To encode a file: +$ ./base64 -e filea fileb +fileb will now be the base64-encoded version of filea. + +To decode a file: +$ ./base64 -d fileb filec +filec will now be identical to filea. + +Programming: +----------- +Some C++ wrappers are provided as well, so you don't have to get your hands +dirty. Encoding from standard input to standard output is as simple as + + #include + #include + int main() + { + base64::encoder E; + E.encode(std::cin, std::cout); + return 0; + } + +Both standalone executables and a static library is provided in the package, + +Implementation: +-------------- +It is DAMN fast, if I may say so myself. The C code uses a little trick which +has been used to implement coroutines, of which one can say that this +implementation is an example. + +(To see how the libb64 codebase compares with some other BASE64 implementations +available, see the BENCHMARKS file) + +The trick involves the fact that a switch-statement may legally cross into +sub-blocks. A very thorough and enlightening essay on co-routines in C, using +this method, can be found in the above mentioned "Coroutines in C", by Simon +Tatham: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html + +For example, an RLE decompressing routine, adapted from the article: +1 static int STATE = 0; +2 static int len, c; +3 switch (STATE) +4 { +5 while (1) +6 { +7 c = getchar(); +8 if (c == EOF) return EOF; +9 if (c == 0xFF) { +10 len = getchar(); +11 c = getchar(); +12 while (len--) +13 { +14 STATE = 0; +15 return c; +16 case 0: +17 } +18 } else +19 STATE = 1; +20 return c; +21 case 1: +22 } +23 } +24 } + +As can be seen from this example, a coroutine depends on a state variable, +which it sets directly before exiting (lines 14 and 119). The next time the +routine is entered, the switch moves control to the specific point directly +after the previous exit (lines 16 and 21).hands + +(As an aside, in the mentioned article the combination of the top-level switch, +the various setting of the state, the return of a value, and the labelling of +the exit point is wrapped in #define macros, making the structure of the +routine even clearer.) + +The obvious problem with any such routine is the static keyword. +Any static variables in a function spell doom for multithreaded applications. +Also, in situations where this coroutine is used by more than one other +coroutines, the consistency is disturbed. + +What is needed is a structure for storing these variabled, which is passed to +the routine seperately. This obviously breaks the modularity of the function, +since now the caller has to worry about and care for the internal state of the +routine (the callee). This allows for a fast, multithreading-enabled +implementation, which may (obviously) be wrapped in a C++ object for ease of +use. + +The base64 encoding and decoding functionality in this package is implemented +in exactly this way, providing both a high-speed high-maintanence C interface, +and a wrapped C++ which is low-maintanence and only slightly less performant. diff --git a/rtsp-server/3rdpart/libb64/include/b64/cdecode.h b/rtsp-server/3rdpart/libb64/include/b64/cdecode.h new file mode 100644 index 00000000..d0d7f489 --- /dev/null +++ b/rtsp-server/3rdpart/libb64/include/b64/cdecode.h @@ -0,0 +1,28 @@ +/* +cdecode.h - c header for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CDECODE_H +#define BASE64_CDECODE_H + +typedef enum +{ + step_a, step_b, step_c, step_d +} base64_decodestep; + +typedef struct +{ + base64_decodestep step; + char plainchar; +} base64_decodestate; + +void base64_init_decodestate(base64_decodestate* state_in); + +int base64_decode_value(char value_in); + +int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in); + +#endif /* BASE64_CDECODE_H */ diff --git a/rtsp-server/3rdpart/libb64/include/b64/cencode.h b/rtsp-server/3rdpart/libb64/include/b64/cencode.h new file mode 100644 index 00000000..c1e3464a --- /dev/null +++ b/rtsp-server/3rdpart/libb64/include/b64/cencode.h @@ -0,0 +1,31 @@ +/* +cencode.h - c header for a base64 encoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CENCODE_H +#define BASE64_CENCODE_H + +typedef enum +{ + step_A, step_B, step_C +} base64_encodestep; + +typedef struct +{ + base64_encodestep step; + char result; + int stepcount; +} base64_encodestate; + +void base64_init_encodestate(base64_encodestate* state_in); + +char base64_encode_value(char value_in); + +int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in); + +int base64_encode_blockend(char* code_out, base64_encodestate* state_in); + +#endif /* BASE64_CENCODE_H */ diff --git a/rtsp-server/3rdpart/libb64/include/b64/decode.h b/rtsp-server/3rdpart/libb64/include/b64/decode.h new file mode 100644 index 00000000..12b16eac --- /dev/null +++ b/rtsp-server/3rdpart/libb64/include/b64/decode.h @@ -0,0 +1,70 @@ +// :mode=c++: +/* +decode.h - c++ wrapper for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ +#ifndef BASE64_DECODE_H +#define BASE64_DECODE_H + +#include + +namespace base64 +{ + extern "C" + { + #include "cdecode.h" + } + + struct decoder + { + base64_decodestate _state; + int _buffersize; + + decoder(int buffersize_in = BUFFERSIZE) + : _buffersize(buffersize_in) + {} + + int decode(char value_in) + { + return base64_decode_value(value_in); + } + + int decode(const char* code_in, const int length_in, char* plaintext_out) + { + return base64_decode_block(code_in, length_in, plaintext_out, &_state); + } + + void decode(std::istream& istream_in, std::ostream& ostream_in) + { + base64_init_decodestate(&_state); + // + const int N = _buffersize; + char* code = new char[N]; + char* plaintext = new char[N]; + int codelength; + int plainlength; + + do + { + istream_in.read((char*)code, N); + codelength = istream_in.gcount(); + plainlength = decode(code, codelength, plaintext); + ostream_in.write((const char*)plaintext, plainlength); + } + while (istream_in.good() && codelength > 0); + // + base64_init_decodestate(&_state); + + delete [] code; + delete [] plaintext; + } + }; + +} // namespace base64 + + + +#endif // BASE64_DECODE_H + diff --git a/rtsp-server/3rdpart/libb64/include/b64/encode.h b/rtsp-server/3rdpart/libb64/include/b64/encode.h new file mode 100644 index 00000000..5d807d97 --- /dev/null +++ b/rtsp-server/3rdpart/libb64/include/b64/encode.h @@ -0,0 +1,77 @@ +// :mode=c++: +/* +encode.h - c++ wrapper for a base64 encoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ +#ifndef BASE64_ENCODE_H +#define BASE64_ENCODE_H + +#include + +namespace base64 +{ + extern "C" + { + #include "cencode.h" + } + + struct encoder + { + base64_encodestate _state; + int _buffersize; + + encoder(int buffersize_in = BUFFERSIZE) + : _buffersize(buffersize_in) + {} + + int encode(char value_in) + { + return base64_encode_value(value_in); + } + + int encode(const char* code_in, const int length_in, char* plaintext_out) + { + return base64_encode_block(code_in, length_in, plaintext_out, &_state); + } + + int encode_end(char* plaintext_out) + { + return base64_encode_blockend(plaintext_out, &_state); + } + + void encode(std::istream& istream_in, std::ostream& ostream_in) + { + base64_init_encodestate(&_state); + // + const int N = _buffersize; + char* plaintext = new char[N]; + char* code = new char[2*N]; + int plainlength; + int codelength; + + do + { + istream_in.read(plaintext, N); + plainlength = istream_in.gcount(); + // + codelength = encode(plaintext, plainlength, code); + ostream_in.write(code, codelength); + } + while (istream_in.good() && plainlength > 0); + + codelength = encode_end(code); + ostream_in.write(code, codelength); + // + base64_init_encodestate(&_state); + + delete [] code; + delete [] plaintext; + } + }; + +} // namespace base64 + +#endif // BASE64_ENCODE_H + diff --git a/rtsp-server/3rdpart/libb64/src/cdecode.c b/rtsp-server/3rdpart/libb64/src/cdecode.c new file mode 100644 index 00000000..34509c96 --- /dev/null +++ b/rtsp-server/3rdpart/libb64/src/cdecode.c @@ -0,0 +1,88 @@ +/* +cdecoder.c - c source to a base64 decoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#include + +int base64_decode_value(char value_in) +{ + static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,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,-1,-1,-1,-1,-1,-1,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}; + static const char decoding_size = sizeof(decoding); + value_in -= 43; + if (value_in < 0 || value_in > decoding_size) return -1; + return decoding[(int)value_in]; +} + +void base64_init_decodestate(base64_decodestate* state_in) +{ + state_in->step = step_a; + state_in->plainchar = 0; +} + +int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in) +{ + const char* codechar = code_in; + char* plainchar = plaintext_out; + char fragment; + + *plainchar = state_in->plainchar; + + switch (state_in->step) + { + while (1) + { + case step_a: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_a; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar = (fragment & 0x03f) << 2; + case step_b: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_b; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; + *plainchar = (fragment & 0x00f) << 4; + case step_c: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_c; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; + *plainchar = (fragment & 0x003) << 6; + case step_d: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_d; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03f); + } + } + /* control should not reach here */ + return plainchar - plaintext_out; +} + diff --git a/rtsp-server/3rdpart/libb64/src/cencode.c b/rtsp-server/3rdpart/libb64/src/cencode.c new file mode 100644 index 00000000..03ba5b68 --- /dev/null +++ b/rtsp-server/3rdpart/libb64/src/cencode.c @@ -0,0 +1,109 @@ +/* +cencoder.c - c source to a base64 encoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#include + +const int CHARS_PER_LINE = 72; + +void base64_init_encodestate(base64_encodestate* state_in) +{ + state_in->step = step_A; + state_in->result = 0; + state_in->stepcount = 0; +} + +char base64_encode_value(char value_in) +{ + static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (value_in > 63) return '='; + return encoding[(int)value_in]; +} + +int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) +{ + const char* plainchar = plaintext_in; + const char* const plaintextend = plaintext_in + length_in; + char* codechar = code_out; + char result; + char fragment; + + result = state_in->result; + + switch (state_in->step) + { + while (1) + { + case step_A: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_A; + return codechar - code_out; + } + fragment = *plainchar++; + result = (fragment & 0x0fc) >> 2; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x003) << 4; + case step_B: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_B; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0f0) >> 4; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x00f) << 2; + case step_C: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_C; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0c0) >> 6; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x03f) >> 0; + *codechar++ = base64_encode_value(result); + + ++(state_in->stepcount); + if (state_in->stepcount == CHARS_PER_LINE/4) + { + *codechar++ = '\n'; + state_in->stepcount = 0; + } + } + } + /* control should not reach here */ + return codechar - code_out; +} + +int base64_encode_blockend(char* code_out, base64_encodestate* state_in) +{ + char* codechar = code_out; + + switch (state_in->step) + { + case step_B: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + *codechar++ = '='; + break; + case step_C: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + break; + case step_A: + break; + } + *codechar++ = '\n'; + + return codechar - code_out; +} + From 2a91db8395c3030da726bd0e58bedb8b7782beb0 Mon Sep 17 00:00:00 2001 From: scottxu Date: Tue, 2 Mar 2021 22:00:06 +0800 Subject: [PATCH 4/9] optimize code. --- CMakeLists.txt | 8 ++++---- rtsp-server/CMakeLists.txt | 7 ++++++- rtspoutput.rc.in | 6 +++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b9ae3e1..7ebdce6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,10 +28,10 @@ set(obs-rtspserver_HEADERS ) if(WIN32) - set(OBS_VIRTUALOUTPUT_VERSION_MAJOR 2) - set(OBS_VIRTUALOUTPUT_VERSION_MINOR 0) - set(OBS_VIRTUALOUTPUT_VERSION_PATCH 5) - set(OBS_VIRTUALOUTPUT_VERSION_STRING "2.0.5") + set(OBS_RTSPSERVER_VERSION_MAJOR 1) + set(OBS_RTSPSERVER_VERSION_MINOR 4) + set(OBS_RTSPSERVER_VERSION_PATCH 0) + set(OBS_RTSPSERVER_VERSION_STRING "1.4.0") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/rtspoutput.rc.in ${CMAKE_CURRENT_SOURCE_DIR}/rtspoutput.rc) endif() diff --git a/rtsp-server/CMakeLists.txt b/rtsp-server/CMakeLists.txt index 45ef87e3..611f329c 100644 --- a/rtsp-server/CMakeLists.txt +++ b/rtsp-server/CMakeLists.txt @@ -4,6 +4,7 @@ project(rtsp-server) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) include_directories("3rdpart") +add_subdirectory(3rdpart/libb64) file(GLOB rtsp-server_net_SOURCES net/*.cpp) @@ -29,4 +30,8 @@ set(rtsp-server_HEADERS add_library(rtsp-server STATIC ${rtsp-server_SOURCES} - ${rtsp-server_HEADERS}) + ${rtsp-server_HEADERS} + ) + +target_link_libraries(rtsp-server + libb64) diff --git a/rtspoutput.rc.in b/rtspoutput.rc.in index 46aefa81..0fb353c2 100644 --- a/rtspoutput.rc.in +++ b/rtspoutput.rc.in @@ -1,5 +1,5 @@ 1 VERSIONINFO -FILEVERSION ${OBS_VIRTUALOUTPUT_VERSION_MAJOR},${OBS_VIRTUALOUTPUT_VERSION_MINOR},${OBS_VIRTUALOUTPUT_VERSION_PATCH},0 +FILEVERSION ${OBS_RTSPSERVER_VERSION_MAJOR},${OBS_RTSPSERVER_VERSION_MINOR},${OBS_RTSPSERVER_VERSION_PATCH},0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -7,11 +7,11 @@ BEGIN BEGIN VALUE "CompanyName", "OBS" VALUE "FileDescription", "OBS RTSP Server Plugin" - VALUE "FileVersion", "${OBS_VIRTUALOUTPUT_VERSION_STRING}" + VALUE "FileVersion", "${OBS_RTSPSERVER_VERSION_STRING}" VALUE "InternalName", "obs-rtspserver" VALUE "OriginalFilename", "obs-rtspserver.dll" VALUE "ProductName", "OBS Studio" - VALUE "ProductVersion", "${OBS_VIRTUALOUTPUT_VERSION_STRING}" + VALUE "ProductVersion", "${OBS_RTSPSERVER_VERSION_STRING}" VALUE "Comments", "RTSP server for OBS" END END From dc7016388faa8b5efb7bd42fa66a3dd13d1da09c Mon Sep 17 00:00:00 2001 From: scottxu Date: Tue, 2 Mar 2021 22:03:00 +0800 Subject: [PATCH 5/9] Update version number. --- CMakeLists.txt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ebdce6c..d57b6ea8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ if(WIN32) set(OBS_RTSPSERVER_VERSION_MAJOR 1) set(OBS_RTSPSERVER_VERSION_MINOR 4) set(OBS_RTSPSERVER_VERSION_PATCH 0) - set(OBS_RTSPSERVER_VERSION_STRING "1.4.0") + set(OBS_RTSPSERVER_VERSION_STRING "1.4.1") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/rtspoutput.rc.in ${CMAKE_CURRENT_SOURCE_DIR}/rtspoutput.rc) endif() @@ -51,11 +51,6 @@ include_directories( "rtsp-server") include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/UI/obs-frontend-api") - -include_directories(../live/BasicUsageEnvironment/include) -include_directories(../live/groupsock/include) -include_directories(../live/liveMedia/include) -include_directories(../live/UsageEnvironment/include) target_link_libraries(obs-rtspserver ${FFMPEG_LIBRARIES} @@ -75,7 +70,7 @@ else() set_target_properties(obs-rtspserver PROPERTIES FOLDER "plugins" - VERSION "1.4.0" + VERSION "1.4.1" PRODUCTNAME "OBS RTSP Server Plugin") endif() From ad2ac8869ebb5aa9b4d75df5150fb5b36e416e96 Mon Sep 17 00:00:00 2001 From: scottxu Date: Tue, 2 Mar 2021 22:15:59 +0800 Subject: [PATCH 6/9] Update main.yml Add libx11-xcb-dev package. --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6b3aa1f7..a84e3723 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -136,6 +136,7 @@ jobs: libqt5x11extras5-dev libspeexdsp-dev libswresample-dev libswscale-dev libudev-dev libv4l-dev \ libvlc-dev libx11-dev libx264-dev libxcb-shm0-dev libxcb-xinerama0-dev libxcomposite-dev \ libxinerama-dev pkg-config python3-dev qtbase5-dev libqt5svg5-dev swig libxcb-randr0-dev \ + libx11-xcb-dev # libxcb-xfixes0-dev libx11-xcb-dev libxcb1-dev \ # libx32gcc-4.8-dev libc6-dev-i386 lib32stdc++6 g++-multilib gcc-multilib \ # libmbedtls-dev:i386 libasound2-dev:i386 libavcodec-dev:i386 libavdevice-dev:i386 \ From 01eaa63a919a5cf56007ef9b09e7a263e4c75391 Mon Sep 17 00:00:00 2001 From: scottxu Date: Tue, 2 Mar 2021 22:24:36 +0800 Subject: [PATCH 7/9] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a84e3723..a68085b6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -136,7 +136,7 @@ jobs: libqt5x11extras5-dev libspeexdsp-dev libswresample-dev libswscale-dev libudev-dev libv4l-dev \ libvlc-dev libx11-dev libx264-dev libxcb-shm0-dev libxcb-xinerama0-dev libxcomposite-dev \ libxinerama-dev pkg-config python3-dev qtbase5-dev libqt5svg5-dev swig libxcb-randr0-dev \ - libx11-xcb-dev + libx11-xcb-dev xcb-util-renderutil # libxcb-xfixes0-dev libx11-xcb-dev libxcb1-dev \ # libx32gcc-4.8-dev libc6-dev-i386 lib32stdc++6 g++-multilib gcc-multilib \ # libmbedtls-dev:i386 libasound2-dev:i386 libavcodec-dev:i386 libavdevice-dev:i386 \ From 79c349a7221a709c1d8edb5938bab4a8d349592f Mon Sep 17 00:00:00 2001 From: scottxu Date: Tue, 2 Mar 2021 22:29:12 +0800 Subject: [PATCH 8/9] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a68085b6..9fa01796 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -136,7 +136,7 @@ jobs: libqt5x11extras5-dev libspeexdsp-dev libswresample-dev libswscale-dev libudev-dev libv4l-dev \ libvlc-dev libx11-dev libx264-dev libxcb-shm0-dev libxcb-xinerama0-dev libxcomposite-dev \ libxinerama-dev pkg-config python3-dev qtbase5-dev libqt5svg5-dev swig libxcb-randr0-dev \ - libx11-xcb-dev xcb-util-renderutil + libx11-xcb-dev libxcb-xfixes0-dev # libxcb-xfixes0-dev libx11-xcb-dev libxcb1-dev \ # libx32gcc-4.8-dev libc6-dev-i386 lib32stdc++6 g++-multilib gcc-multilib \ # libmbedtls-dev:i386 libasound2-dev:i386 libavcodec-dev:i386 libavdevice-dev:i386 \ From ee9222566ecbf5b85ce7a7fd76024e4c42cd92ee Mon Sep 17 00:00:00 2001 From: scottxu Date: Tue, 2 Mar 2021 22:46:39 +0800 Subject: [PATCH 9/9] fix error. --- rtsp-server/xop/H264Source.h | 1 + 1 file changed, 1 insertion(+) diff --git a/rtsp-server/xop/H264Source.h b/rtsp-server/xop/H264Source.h index 84f79797..5a1464cb 100644 --- a/rtsp-server/xop/H264Source.h +++ b/rtsp-server/xop/H264Source.h @@ -6,6 +6,7 @@ #include "MediaSource.h" #include "rtp.h" +#include namespace xop {