From d430d68fc7013abb45c49cbc7480270f9a9089bb Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sat, 27 Aug 2022 20:38:15 +0200 Subject: [PATCH] Improve RTSP support to configure port and expose low res stream --- README.md | 14 ++++- cmd/camera-streamer.c | 10 +++- device/camera/camera_output.c | 8 ++- rtsp/rtsp.cc | 62 ++++++++++++++------ rtsp/rtsp.h | 7 +++ service/camera-streamer-arducam-16MP.service | 3 +- service/camera-streamer-arducam-64MP.service | 3 +- service/camera-streamer-pi219-8MP.service | 3 +- service/camera-streamer-usb-cam.service | 3 +- 9 files changed, 89 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 1f7f5b2..21a3c2b 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ reboot ## Compile ```bash -apt-get -y install libavformat-dev libavutil-dev libavcodec-dev libcamera-dev v4l-utils pkg-config xxd build-essential +apt-get -y install libavformat-dev libavutil-dev libavcodec-dev libcamera-dev liblivemedia-dev v4l-utils pkg-config xxd build-essential make sudo make install ``` @@ -137,6 +137,18 @@ Camera capture and resolution exposed is controlled by threee parameters: - (ISP mode only) `-camera-high_res_factor` a default resolution exposed via HTTP (`exposed_width = camera_width / factor, exposed_height = camera_height / factor`) - (ISP mode only) `-camera-low_res_factor` a low-resolution exposed via HTTP when `?res=low` is added (ex. `http://:8080/snapshot`) +## RTSP server + +The camera-streamer implements RTSP server via `live555`. Enable it with: + +- adding `-rtsp-port`: will enable RTSP server on 8554 +- adding `-rtsp-port=1111`: will enable RTSP server on custom port + +The camera-streamer will expose two stream (if low res mode is enabled): + +- `rtsp://:8554/stream.h264` - high resolution stream (always enabled if H264 stream is available directly or via encoding) +- `rtsp://:8554/stream_low_res.h264` - low resolution stream if low res mode is configured via `-camera-low_res_factor` + ## List all available controls You can view all available configuration parameters by adding `-log-verbose` diff --git a/cmd/camera-streamer.c b/cmd/camera-streamer.c index c1bdd5f..61e470a 100644 --- a/cmd/camera-streamer.c +++ b/cmd/camera-streamer.c @@ -130,6 +130,10 @@ log_options_t log_options = { .verbose = false }; +rtsp_options_t rtsp_options = { + .port = 0, +}; + option_value_t camera_formats[] = { { "DEFAULT", 0 }, { "YUYV", V4L2_PIX_FMT_YUYV }, @@ -181,6 +185,8 @@ option_t all_options[] = { DEFINE_OPTION(http, port, uint, "Set the HTTP web-server port."), DEFINE_OPTION(http, maxcons, uint, "Set maximum number of concurrent HTTP connections."), + DEFINE_OPTION_DEFAULT(rtsp, port, uint, "8554", "Set the RTSP server port (default: 8854)."), + DEFINE_OPTION_DEFAULT(log, debug, bool, "1", "Enable debug logging."), DEFINE_OPTION_DEFAULT(log, verbose, bool, "1", "Enable verbose logging."), DEFINE_OPTION_PTR(log, filter, list, "Enable debug logging from the given files. Ex.: `-log-filter=buffer.cc`"), @@ -214,7 +220,9 @@ int main(int argc, char *argv[]) goto error; } - rtsp_server(); + if (rtsp_options.port > 0 && rtsp_server() < 0) { + goto error; + } while (true) { camera = camera_open(&camera_options); diff --git a/device/camera/camera_output.c b/device/camera/camera_output.c index edf2235..8067283 100644 --- a/device/camera/camera_output.c +++ b/device/camera/camera_output.c @@ -31,6 +31,12 @@ static void h264_capture(buffer_t *buf) rtsp_h264_capture(buf); } +static void h264_lowres_capture(buffer_t *buf) +{ + http_h264_lowres_capture(buf); + rtsp_h264_low_res_capture(buf); +} + static bool h264_needs_buffer() { return http_h264_needs_buffer() | rtsp_h264_needs_buffer(); @@ -38,7 +44,7 @@ static bool h264_needs_buffer() static link_callbacks_t h264_callbacks[2] = { { "H264-CAPTURE", h264_capture, h264_needs_buffer }, - { "H264-LOW-CAPTURE", http_h264_lowres_capture, http_h264_needs_buffer } + { "H264-LOW-CAPTURE", h264_lowres_capture, h264_needs_buffer } }; static int camera_configure_h264_output(camera_t *camera, buffer_list_t *src_capture, int res) diff --git a/rtsp/rtsp.cc b/rtsp/rtsp.cc index 3ed123b..4dc1adc 100644 --- a/rtsp/rtsp.cc +++ b/rtsp/rtsp.cc @@ -23,10 +23,14 @@ static pthread_t rtsp_thread; static pthread_mutex_t rtsp_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; static class DynamicH264Stream *rtsp_streams; +static const char *stream_name = "stream.h264"; +static const char *stream_low_res_name = "stream_low_res.h264"; + class DynamicH264Stream : public FramedSource { public: - DynamicH264Stream(UsageEnvironment& env) : FramedSource(env), fHaveStartedReading(False) + DynamicH264Stream(UsageEnvironment& env, Boolean lowResMode) + : FramedSource(env), fHaveStartedReading(False), fLowResMode(lowResMode) { } @@ -59,12 +63,16 @@ public: pthread_mutex_unlock(&rtsp_lock); } - void receiveData(buffer_t *buf) + void receiveData(buffer_t *buf, bool lowResMode) { if (!isCurrentlyAwaitingData()) { return; // we're not ready for the data yet } + if (fLowResMode != lowResMode) { + return; + } + if (h264_is_key_frame(buf)) { fHadKeyFrame = true; } @@ -100,6 +108,7 @@ private: Boolean fHaveStartedReading; Boolean fHadKeyFrame; Boolean fRequestedKeyFrame; + Boolean fLowResMode; public: DynamicH264Stream *pNextStream; @@ -108,21 +117,24 @@ public: class DynamicH264VideoFileServerMediaSubsession : public OnDemandServerMediaSubsession { public: - DynamicH264VideoFileServerMediaSubsession(UsageEnvironment& env, Boolean reuseFirstSource) - : OnDemandServerMediaSubsession(env, reuseFirstSource) + DynamicH264VideoFileServerMediaSubsession(UsageEnvironment& env, Boolean reuseFirstSource, Boolean lowResMode) + : OnDemandServerMediaSubsession(env, reuseFirstSource), fLowResMode(lowResMode) { } virtual FramedSource* createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) { estBitrate = 500; // kbps, estimate - return H264VideoStreamFramer::createNew(envir(), new DynamicH264Stream(envir())); + return H264VideoStreamFramer::createNew(envir(), new DynamicH264Stream(envir(), fLowResMode)); } virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /*inputSource*/) { return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic); } + +private: + Boolean fLowResMode; }; class DynamicRTSPServer: public RTSPServerSupportingHTTPStreaming @@ -153,7 +165,17 @@ protected: protected: // redefined virtual functions virtual ServerMediaSession* lookupServerMediaSession(char const* streamName, Boolean isFirstLookupInSession) { - LOG_INFO(NULL, "Requesting %s stream...", streamName); + bool lowResMode = false; + + if (strcmp(streamName, stream_name) == 0) { + LOG_INFO(NULL, "Requesting %s stream...", streamName); + } else if (strcmp(streamName, stream_low_res_name) == 0) { + LOG_INFO(NULL, "Requesting %s stream (low resolution mode)...", streamName); + lowResMode = true; + } else { + LOG_INFO(NULL, "No stream available: '%s'", streamName); + return NULL; + } auto sms = RTSPServer::lookupServerMediaSession(streamName); @@ -167,11 +189,9 @@ protected: // redefined virtual functions sms = ServerMediaSession::createNew(envir(), streamName, streamName, "streamed by the LIVE555 Media Server");; OutPacketBuffer::maxSize = 2000000; // allow for some possibly large H.264 frames - auto subsession = new DynamicH264VideoFileServerMediaSubsession(envir(), false); + auto subsession = new DynamicH264VideoFileServerMediaSubsession(envir(), false, lowResMode); sms->addSubsession(subsession); addServerMediaSession(sms); - - LOG_INFO(NULL, "StreamName=%s SMS=%p Sub=%p", streamName, sms, subsession); return sms; } }; @@ -199,17 +219,12 @@ extern "C" int rtsp_server() #endif RTSPServer* rtspServer; - portNumBits rtspServerPortNum = 554; - rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB); - if (rtspServer == NULL) { - rtspServerPortNum = 8554; - rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB); - } + rtspServer = DynamicRTSPServer::createNew(*env, rtsp_options.port, authDB); if (rtspServer == NULL) { LOG_ERROR(NULL, "Failed to create RTSP server: %s", env->getResultMsg()); return -1; } - LOG_INFO(NULL, "Running RTSP server on '%d'", rtspServerPortNum); + LOG_INFO(NULL, "Running RTSP server on '%d'", rtsp_options.port); // if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) { // LOG_INFO(NULL, "Running RTSP-over-HTTP tunneling on '%d'", rtspServer->httpServerPortNum()); @@ -234,7 +249,16 @@ extern "C" void rtsp_h264_capture(buffer_t *buf) { pthread_mutex_lock(&rtsp_lock); for (DynamicH264Stream *stream = rtsp_streams; stream; stream = stream->pNextStream) { - stream->receiveData(buf); + stream->receiveData(buf, false); + } + pthread_mutex_unlock(&rtsp_lock); +} + +extern "C" void rtsp_h264_low_res_capture(struct buffer_s *buf) +{ + pthread_mutex_lock(&rtsp_lock); + for (DynamicH264Stream *stream = rtsp_streams; stream; stream = stream->pNextStream) { + stream->receiveData(buf, true); } pthread_mutex_unlock(&rtsp_lock); } @@ -255,4 +279,8 @@ extern "C" void rtsp_h264_capture(buffer_t *buf) { } +extern "C" void rtsp_h264_low_res_capture(struct buffer_s *buf) +{ +} + #endif // USE_RTSP \ No newline at end of file diff --git a/rtsp/rtsp.h b/rtsp/rtsp.h index 1b9d26c..eaa3322 100644 --- a/rtsp/rtsp.h +++ b/rtsp/rtsp.h @@ -1,5 +1,12 @@ #pragma once +typedef struct rtsp_options_s { + uint port; +} rtsp_options_t; + +extern rtsp_options_t rtsp_options; + int rtsp_server(); bool rtsp_h264_needs_buffer(); void rtsp_h264_capture(struct buffer_s *buf); +void rtsp_h264_low_res_capture(struct buffer_s *buf); diff --git a/service/camera-streamer-arducam-16MP.service b/service/camera-streamer-arducam-16MP.service index 1fb08fb..04294b4 100644 --- a/service/camera-streamer-arducam-16MP.service +++ b/service/camera-streamer-arducam-16MP.service @@ -20,7 +20,8 @@ ExecStart=/usr/local/bin/camera-streamer \ ; bump brightness slightly -camera-options=brightness=0.1 \ ; disable auto-focus - -camera-auto_focus=1 + -camera-auto_focus=1 \ + -rtsp-port DynamicUser=yes SupplementaryGroups=video i2c diff --git a/service/camera-streamer-arducam-64MP.service b/service/camera-streamer-arducam-64MP.service index a198684..3f86797 100644 --- a/service/camera-streamer-arducam-64MP.service +++ b/service/camera-streamer-arducam-64MP.service @@ -22,7 +22,8 @@ ExecStart=/usr/local/bin/camera-streamer \ ; bump brightness slightly -camera-options=brightness=0.1 \ ; disable auto-focus - -camera-auto_focus=0 + -camera-auto_focus=0 \ + -rtsp-port DynamicUser=yes SupplementaryGroups=video i2c diff --git a/service/camera-streamer-pi219-8MP.service b/service/camera-streamer-pi219-8MP.service index a335968..90e0abd 100644 --- a/service/camera-streamer-pi219-8MP.service +++ b/service/camera-streamer-pi219-8MP.service @@ -21,7 +21,8 @@ ExecStart=/usr/local/bin/camera-streamer \ ; the low-res is 820x616 -camera-low_res_factor=4 \ ; bump brightness slightly - -camera-options=brightness=0.1 + -camera-options=brightness=0.1 \ + -rtsp-port DynamicUser=yes SupplementaryGroups=video i2c diff --git a/service/camera-streamer-usb-cam.service b/service/camera-streamer-usb-cam.service index 8441419..4fb3f6d 100644 --- a/service/camera-streamer-usb-cam.service +++ b/service/camera-streamer-usb-cam.service @@ -14,7 +14,8 @@ ExecStart=/usr/local/bin/camera-streamer \ ; the high-res is 1920x1080 -camera-high_res_factor=1.0 \ ; the low-res is 960x540 - -camera-low_res_factor=2.0 + -camera-low_res_factor=2.0 \ + -rtsp-port DynamicUser=yes SupplementaryGroups=video i2c