Improve RTSP support to configure port and expose low res stream
This commit is contained in:
parent
e4be9cac48
commit
d430d68fc7
14
README.md
14
README.md
@ -43,7 +43,7 @@ reboot
|
|||||||
## Compile
|
## Compile
|
||||||
|
|
||||||
```bash
|
```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
|
make
|
||||||
sudo make install
|
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-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://<ip>:8080/snapshot`)
|
- (ISP mode only) `-camera-low_res_factor` a low-resolution exposed via HTTP when `?res=low` is added (ex. `http://<ip>: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://<ip>:8554/stream.h264` - high resolution stream (always enabled if H264 stream is available directly or via encoding)
|
||||||
|
- `rtsp://<ip>:8554/stream_low_res.h264` - low resolution stream if low res mode is configured via `-camera-low_res_factor`
|
||||||
|
|
||||||
## List all available controls
|
## List all available controls
|
||||||
|
|
||||||
You can view all available configuration parameters by adding `-log-verbose`
|
You can view all available configuration parameters by adding `-log-verbose`
|
||||||
|
@ -130,6 +130,10 @@ log_options_t log_options = {
|
|||||||
.verbose = false
|
.verbose = false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
rtsp_options_t rtsp_options = {
|
||||||
|
.port = 0,
|
||||||
|
};
|
||||||
|
|
||||||
option_value_t camera_formats[] = {
|
option_value_t camera_formats[] = {
|
||||||
{ "DEFAULT", 0 },
|
{ "DEFAULT", 0 },
|
||||||
{ "YUYV", V4L2_PIX_FMT_YUYV },
|
{ "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, port, uint, "Set the HTTP web-server port."),
|
||||||
DEFINE_OPTION(http, maxcons, uint, "Set maximum number of concurrent HTTP connections."),
|
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, debug, bool, "1", "Enable debug logging."),
|
||||||
DEFINE_OPTION_DEFAULT(log, verbose, bool, "1", "Enable verbose 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`"),
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
rtsp_server();
|
if (rtsp_options.port > 0 && rtsp_server() < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
camera = camera_open(&camera_options);
|
camera = camera_open(&camera_options);
|
||||||
|
@ -31,6 +31,12 @@ static void h264_capture(buffer_t *buf)
|
|||||||
rtsp_h264_capture(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()
|
static bool h264_needs_buffer()
|
||||||
{
|
{
|
||||||
return http_h264_needs_buffer() | rtsp_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] = {
|
static link_callbacks_t h264_callbacks[2] = {
|
||||||
{ "H264-CAPTURE", h264_capture, h264_needs_buffer },
|
{ "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)
|
static int camera_configure_h264_output(camera_t *camera, buffer_list_t *src_capture, int res)
|
||||||
|
62
rtsp/rtsp.cc
62
rtsp/rtsp.cc
@ -23,10 +23,14 @@ static pthread_t rtsp_thread;
|
|||||||
static pthread_mutex_t rtsp_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
static pthread_mutex_t rtsp_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||||
static class DynamicH264Stream *rtsp_streams;
|
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
|
class DynamicH264Stream : public FramedSource
|
||||||
{
|
{
|
||||||
public:
|
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);
|
pthread_mutex_unlock(&rtsp_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void receiveData(buffer_t *buf)
|
void receiveData(buffer_t *buf, bool lowResMode)
|
||||||
{
|
{
|
||||||
if (!isCurrentlyAwaitingData()) {
|
if (!isCurrentlyAwaitingData()) {
|
||||||
return; // we're not ready for the data yet
|
return; // we're not ready for the data yet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fLowResMode != lowResMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (h264_is_key_frame(buf)) {
|
if (h264_is_key_frame(buf)) {
|
||||||
fHadKeyFrame = true;
|
fHadKeyFrame = true;
|
||||||
}
|
}
|
||||||
@ -100,6 +108,7 @@ private:
|
|||||||
Boolean fHaveStartedReading;
|
Boolean fHaveStartedReading;
|
||||||
Boolean fHadKeyFrame;
|
Boolean fHadKeyFrame;
|
||||||
Boolean fRequestedKeyFrame;
|
Boolean fRequestedKeyFrame;
|
||||||
|
Boolean fLowResMode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DynamicH264Stream *pNextStream;
|
DynamicH264Stream *pNextStream;
|
||||||
@ -108,21 +117,24 @@ public:
|
|||||||
class DynamicH264VideoFileServerMediaSubsession : public OnDemandServerMediaSubsession
|
class DynamicH264VideoFileServerMediaSubsession : public OnDemandServerMediaSubsession
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DynamicH264VideoFileServerMediaSubsession(UsageEnvironment& env, Boolean reuseFirstSource)
|
DynamicH264VideoFileServerMediaSubsession(UsageEnvironment& env, Boolean reuseFirstSource, Boolean lowResMode)
|
||||||
: OnDemandServerMediaSubsession(env, reuseFirstSource)
|
: OnDemandServerMediaSubsession(env, reuseFirstSource), fLowResMode(lowResMode)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual FramedSource* createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate)
|
virtual FramedSource* createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate)
|
||||||
{
|
{
|
||||||
estBitrate = 500; // kbps, estimate
|
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*/)
|
virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /*inputSource*/)
|
||||||
{
|
{
|
||||||
return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
|
return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Boolean fLowResMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DynamicRTSPServer: public RTSPServerSupportingHTTPStreaming
|
class DynamicRTSPServer: public RTSPServerSupportingHTTPStreaming
|
||||||
@ -153,7 +165,17 @@ protected:
|
|||||||
protected: // redefined virtual functions
|
protected: // redefined virtual functions
|
||||||
virtual ServerMediaSession* lookupServerMediaSession(char const* streamName, Boolean isFirstLookupInSession)
|
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);
|
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");;
|
sms = ServerMediaSession::createNew(envir(), streamName, streamName, "streamed by the LIVE555 Media Server");;
|
||||||
OutPacketBuffer::maxSize = 2000000; // allow for some possibly large H.264 frames
|
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);
|
sms->addSubsession(subsession);
|
||||||
addServerMediaSession(sms);
|
addServerMediaSession(sms);
|
||||||
|
|
||||||
LOG_INFO(NULL, "StreamName=%s SMS=%p Sub=%p", streamName, sms, subsession);
|
|
||||||
return sms;
|
return sms;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -199,17 +219,12 @@ extern "C" int rtsp_server()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
RTSPServer* rtspServer;
|
RTSPServer* rtspServer;
|
||||||
portNumBits rtspServerPortNum = 554;
|
rtspServer = DynamicRTSPServer::createNew(*env, rtsp_options.port, authDB);
|
||||||
rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);
|
|
||||||
if (rtspServer == NULL) {
|
|
||||||
rtspServerPortNum = 8554;
|
|
||||||
rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);
|
|
||||||
}
|
|
||||||
if (rtspServer == NULL) {
|
if (rtspServer == NULL) {
|
||||||
LOG_ERROR(NULL, "Failed to create RTSP server: %s", env->getResultMsg());
|
LOG_ERROR(NULL, "Failed to create RTSP server: %s", env->getResultMsg());
|
||||||
return -1;
|
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)) {
|
// if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
|
||||||
// LOG_INFO(NULL, "Running RTSP-over-HTTP tunneling on '%d'", rtspServer->httpServerPortNum());
|
// 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);
|
pthread_mutex_lock(&rtsp_lock);
|
||||||
for (DynamicH264Stream *stream = rtsp_streams; stream; stream = stream->pNextStream) {
|
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);
|
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
|
#endif // USE_RTSP
|
@ -1,5 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
typedef struct rtsp_options_s {
|
||||||
|
uint port;
|
||||||
|
} rtsp_options_t;
|
||||||
|
|
||||||
|
extern rtsp_options_t rtsp_options;
|
||||||
|
|
||||||
int rtsp_server();
|
int rtsp_server();
|
||||||
bool rtsp_h264_needs_buffer();
|
bool rtsp_h264_needs_buffer();
|
||||||
void rtsp_h264_capture(struct buffer_s *buf);
|
void rtsp_h264_capture(struct buffer_s *buf);
|
||||||
|
void rtsp_h264_low_res_capture(struct buffer_s *buf);
|
||||||
|
@ -20,7 +20,8 @@ ExecStart=/usr/local/bin/camera-streamer \
|
|||||||
; bump brightness slightly
|
; bump brightness slightly
|
||||||
-camera-options=brightness=0.1 \
|
-camera-options=brightness=0.1 \
|
||||||
; disable auto-focus
|
; disable auto-focus
|
||||||
-camera-auto_focus=1
|
-camera-auto_focus=1 \
|
||||||
|
-rtsp-port
|
||||||
|
|
||||||
DynamicUser=yes
|
DynamicUser=yes
|
||||||
SupplementaryGroups=video i2c
|
SupplementaryGroups=video i2c
|
||||||
|
@ -22,7 +22,8 @@ ExecStart=/usr/local/bin/camera-streamer \
|
|||||||
; bump brightness slightly
|
; bump brightness slightly
|
||||||
-camera-options=brightness=0.1 \
|
-camera-options=brightness=0.1 \
|
||||||
; disable auto-focus
|
; disable auto-focus
|
||||||
-camera-auto_focus=0
|
-camera-auto_focus=0 \
|
||||||
|
-rtsp-port
|
||||||
|
|
||||||
DynamicUser=yes
|
DynamicUser=yes
|
||||||
SupplementaryGroups=video i2c
|
SupplementaryGroups=video i2c
|
||||||
|
@ -21,7 +21,8 @@ ExecStart=/usr/local/bin/camera-streamer \
|
|||||||
; the low-res is 820x616
|
; the low-res is 820x616
|
||||||
-camera-low_res_factor=4 \
|
-camera-low_res_factor=4 \
|
||||||
; bump brightness slightly
|
; bump brightness slightly
|
||||||
-camera-options=brightness=0.1
|
-camera-options=brightness=0.1 \
|
||||||
|
-rtsp-port
|
||||||
|
|
||||||
DynamicUser=yes
|
DynamicUser=yes
|
||||||
SupplementaryGroups=video i2c
|
SupplementaryGroups=video i2c
|
||||||
|
@ -14,7 +14,8 @@ ExecStart=/usr/local/bin/camera-streamer \
|
|||||||
; the high-res is 1920x1080
|
; the high-res is 1920x1080
|
||||||
-camera-high_res_factor=1.0 \
|
-camera-high_res_factor=1.0 \
|
||||||
; the low-res is 960x540
|
; the low-res is 960x540
|
||||||
-camera-low_res_factor=2.0
|
-camera-low_res_factor=2.0 \
|
||||||
|
-rtsp-port
|
||||||
|
|
||||||
DynamicUser=yes
|
DynamicUser=yes
|
||||||
SupplementaryGroups=video i2c
|
SupplementaryGroups=video i2c
|
||||||
|
Loading…
x
Reference in New Issue
Block a user