Add a fifo version of the rtsp server
This commit is contained in:
parent
430f839954
commit
fdf1fbd497
|
@ -0,0 +1,226 @@
|
|||
//
|
||||
// ZoneMinder RTSP Daemon
|
||||
// Copyright (C) 2021 Isaac Connor
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
|
||||
/*
|
||||
|
||||
=head1 NAME
|
||||
|
||||
zm_rtsp_server - The ZoneMinder Server
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
zmc -m <monitor_id>
|
||||
zmc --monitor <monitor_id>
|
||||
zmc -h
|
||||
zmc --help
|
||||
zmc -v
|
||||
zmc --version
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This binary's job is to connect to fifo's provided by local zmc processes
|
||||
and provide that stream over rtsp
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
-m, --monitor_id - ID of a monitor to stream
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
|
||||
=cut
|
||||
|
||||
*/
|
||||
|
||||
#include "zm.h"
|
||||
#include "zm_db.h"
|
||||
#include "zm_define.h"
|
||||
#include "zm_monitor.h"
|
||||
#include "zm_rtsp_server_thread.h"
|
||||
#include "zm_rtsp_server_fifo_video_source.h"
|
||||
#include "zm_signal.h"
|
||||
#include "zm_time.h"
|
||||
#include "zm_utils.h"
|
||||
#include <getopt.h>
|
||||
#include <iostream>
|
||||
|
||||
void Usage() {
|
||||
fprintf(stderr, "zm_rtsp_server -m <monitor_id>\n");
|
||||
|
||||
fprintf(stderr, "Options:\n");
|
||||
fprintf(stderr, " -m, --monitor <monitor_id> : We default to all monitors use this to specify just one\n");
|
||||
fprintf(stderr, " -h, --help : This screen\n");
|
||||
fprintf(stderr, " -v, --version : Report the installed version of ZoneMinder\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
self = argv[0];
|
||||
|
||||
srand(getpid() * time(nullptr));
|
||||
|
||||
int monitor_id = -1;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"monitor", 1, nullptr, 'm'},
|
||||
{"help", 0, nullptr, 'h'},
|
||||
{"version", 0, nullptr, 'v'},
|
||||
{nullptr, 0, nullptr, 0}
|
||||
};
|
||||
|
||||
while (1) {
|
||||
int option_index = 0;
|
||||
|
||||
int c = getopt_long(argc, argv, "m:h:v", long_options, &option_index);
|
||||
if ( c == -1 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 'm':
|
||||
monitor_id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
// fprintf(stderr, "?? getopt returned character code 0%o ??\n", c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
fprintf(stderr, "Extraneous options, ");
|
||||
while (optind < argc)
|
||||
printf("%s ", argv[optind++]);
|
||||
printf("\n");
|
||||
Usage();
|
||||
}
|
||||
|
||||
const char *log_id_string = "zm_rtsp_server";
|
||||
///std::string log_id_string = std::string("zm_rtsp_server");
|
||||
///if ( monitor_id > 0 ) log_id_string += stringtf("_m%d", monitor_id);
|
||||
|
||||
logInit(log_id_string);
|
||||
zmLoadStaticConfig();
|
||||
zmDbConnect();
|
||||
zmLoadDBConfig();
|
||||
logInit(log_id_string);
|
||||
|
||||
hwcaps_detect();
|
||||
|
||||
std::string where = "`Function` != 'None' AND `RTSPServer` != false";
|
||||
if (staticConfig.SERVER_ID)
|
||||
where += stringtf(" AND `ServerId`=%d", staticConfig.SERVER_ID);
|
||||
if (monitor_id > 0)
|
||||
where += stringtf(" AND `Id`=%d", monitor_id);
|
||||
|
||||
std::vector<std::shared_ptr<Monitor>> monitors = Monitor::LoadMonitors(where, Monitor::QUERY);
|
||||
|
||||
if (monitors.empty()) {
|
||||
Error("No monitors found");
|
||||
exit(-1);
|
||||
} else {
|
||||
Debug(2, "%d monitors loaded", monitors.size());
|
||||
}
|
||||
|
||||
Info("Starting RTSP Server version %s", ZM_VERSION);
|
||||
zmSetDefaultHupHandler();
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
|
||||
sigaddset(&block_set, SIGHUP);
|
||||
sigaddset(&block_set, SIGUSR1);
|
||||
sigaddset(&block_set, SIGUSR2);
|
||||
|
||||
int result = 0;
|
||||
|
||||
while (!zm_terminate) {
|
||||
result = 0;
|
||||
|
||||
for (const std::shared_ptr<Monitor> &monitor : monitors) {
|
||||
monitor->LoadCamera();
|
||||
|
||||
while (!monitor->connect()) {
|
||||
Warning("Couldn't connect to monitor %d", monitor->Id());
|
||||
sleep(1);
|
||||
}
|
||||
} // end foreach monitor
|
||||
|
||||
RTSPServerThread ** rtsp_server_threads = nullptr;
|
||||
if (config.min_rtsp_port) {
|
||||
rtsp_server_threads = new RTSPServerThread *[monitors.size()];
|
||||
Debug(1, "Starting RTSP server because min_rtsp_port is set");
|
||||
} else {
|
||||
Debug(1, "Not starting RTSP server because min_rtsp_port not set");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < monitors.size(); i++) {
|
||||
rtsp_server_threads[i] = new RTSPServerThread(monitors[i]);
|
||||
std::string streamname = monitors[i]->GetRTSPStreamName();
|
||||
ServerMediaSession *sms = rtsp_server_threads[i]->addSession(streamname);
|
||||
ZoneMinderFifoVideoSource *video_source = static_cast<ZoneMinderFifoVideoSource *>(rtsp_server_threads[i]->addFifo(sms, monitors[i]->GetVideoFifo()));
|
||||
video_source->setWidth(monitors[i]->Width());
|
||||
video_source->setHeight(monitors[i]->Height());
|
||||
FramedSource *audio_source = rtsp_server_threads[i]->addFifo(sms, monitors[i]->GetAudioFifo());
|
||||
rtsp_server_threads[i]->start();
|
||||
}
|
||||
|
||||
while (!zm_terminate) {
|
||||
sleep(1);
|
||||
// What to do in here? Sleep mostly. Wait for reload commands maybe watch for dead monitors.
|
||||
if ((result < 0) or zm_reload) {
|
||||
// Failure, try reconnecting
|
||||
break;
|
||||
}
|
||||
} // end while ! zm_terminate and connected
|
||||
|
||||
for (size_t i = 0; i < monitors.size(); i++) {
|
||||
rtsp_server_threads[i]->stop();
|
||||
rtsp_server_threads[i]->join();
|
||||
delete rtsp_server_threads[i];
|
||||
rtsp_server_threads[i] = nullptr;
|
||||
}
|
||||
|
||||
delete[] rtsp_server_threads;
|
||||
rtsp_server_threads = nullptr;
|
||||
|
||||
if (zm_reload) {
|
||||
for (std::shared_ptr<Monitor> &monitor : monitors) {
|
||||
monitor->Reload();
|
||||
}
|
||||
logTerm();
|
||||
logInit(log_id_string);
|
||||
zm_reload = false;
|
||||
} // end if zm_reload
|
||||
} // end while ! zm_terminate outer connection loop
|
||||
|
||||
Image::Deinitialise();
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
|
||||
return zm_terminate ? 0 : result;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/* ---------------------------------------------------------------------------
|
||||
**
|
||||
** ADTS_FifoSource.cpp
|
||||
**
|
||||
** ADTS Live555 source
|
||||
**
|
||||
** -------------------------------------------------------------------------*/
|
||||
|
||||
#include "zm_rtsp_server_adts_fifo_source.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#if HAVE_RTSP_SERVER
|
||||
|
||||
static unsigned const samplingFrequencyTable[16] = {
|
||||
96000, 88200, 64000, 48000,
|
||||
44100, 32000, 24000, 22050,
|
||||
16000, 12000, 11025, 8000,
|
||||
7350, 0, 0, 0
|
||||
};
|
||||
// ---------------------------------
|
||||
// ADTS ZoneMinder FramedSource
|
||||
// ---------------------------------
|
||||
//
|
||||
ADTS_ZoneMinderFifoSource::ADTS_ZoneMinderFifoSource(
|
||||
UsageEnvironment& env,
|
||||
std::string fifo,
|
||||
unsigned int queueSize
|
||||
)
|
||||
:
|
||||
ZoneMinderFifoSource(env, fifo, queueSize),
|
||||
samplingFrequencyIndex(0),
|
||||
channels(1)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os <<
|
||||
"profile-level-id=1;"
|
||||
"mode=AAC-hbr;sizelength=13;indexlength=3;"
|
||||
"indexdeltalength=3"
|
||||
<< "\r\n";
|
||||
m_auxLine.assign(os.str());
|
||||
}
|
||||
#endif // HAVE_RTSP_SERVER
|
|
@ -0,0 +1,66 @@
|
|||
/* ---------------------------------------------------------------------------
|
||||
** This software is in the public domain, furnished "as is", without technical
|
||||
** support, and with no warranty, express or implied, as to its usefulness for
|
||||
** any purpose.
|
||||
**
|
||||
** ADTS_ZoneMinderFifoSource.h
|
||||
**
|
||||
** ADTS ZoneMinder live555 source
|
||||
**
|
||||
** -------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef ZM_RTSP_SERVER_ADTS_FIFO_SOURCE_H
|
||||
#define ZM_RTSP_SERVER_ADTS_FIFO_SOURCE_H
|
||||
|
||||
#include "zm_config.h"
|
||||
#include "zm_rtsp_server_fifo_source.h"
|
||||
|
||||
#if HAVE_RTSP_SERVER
|
||||
// ---------------------------------
|
||||
// ADTS(AAC) ZoneMinder FramedSource
|
||||
// ---------------------------------
|
||||
|
||||
class ADTS_ZoneMinderFifoSource : public ZoneMinderFifoSource {
|
||||
public:
|
||||
static ADTS_ZoneMinderFifoSource* createNew(
|
||||
UsageEnvironment& env,
|
||||
std::string fifo,
|
||||
unsigned int queueSize
|
||||
) {
|
||||
return new ADTS_ZoneMinderFifoSource(env, fifo, queueSize);
|
||||
};
|
||||
protected:
|
||||
ADTS_ZoneMinderFifoSource(
|
||||
UsageEnvironment& env,
|
||||
std::string fifo,
|
||||
unsigned int queueSize
|
||||
);
|
||||
|
||||
virtual ~ADTS_ZoneMinderFifoSource() {}
|
||||
|
||||
/*
|
||||
virtual unsigned char* extractFrame(unsigned char* frame, size_t& size, size_t& outsize);
|
||||
virtual unsigned char* findMarker(unsigned char *frame, size_t size, size_t &length);
|
||||
*/
|
||||
public:
|
||||
int samplingFrequency() { return 8000; //m_stream->codecpar->sample_rate;
|
||||
};
|
||||
const char *configStr() { return config.c_str(); };
|
||||
int numChannels() {
|
||||
//Debug(1, "this %p m_stream %p channels %d",
|
||||
//this, m_stream, channels);
|
||||
//Debug(1, "m_stream %p codecpar %p channels %d => %d",
|
||||
//m_stream, m_stream->codecpar, m_stream->codecpar->channels, channels);
|
||||
return 1;
|
||||
//return channels;
|
||||
//return m_stream->codecpar->channels;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string config;
|
||||
int samplingFrequencyIndex;
|
||||
int channels;
|
||||
};
|
||||
#endif // HAVE_RTSP_SERVER
|
||||
|
||||
#endif // ZM_RTSP_SERVER_ADTS_FIFO_SOURCE_H
|
|
@ -0,0 +1,215 @@
|
|||
/* ---------------------------------------------------------------------------
|
||||
** This software is in the public domain, furnished "as is", without technical
|
||||
** support, and with no warranty, express or implied, as to its usefulness for
|
||||
** any purpose.
|
||||
**
|
||||
**
|
||||
** ZoneMinder Live555 source
|
||||
**
|
||||
** -------------------------------------------------------------------------*/
|
||||
|
||||
#include "zm_rtsp_server_fifo_source.h"
|
||||
|
||||
#include "zm_config.h"
|
||||
#include "zm_logger.h"
|
||||
#include "zm_rtsp_server_frame.h"
|
||||
#include "zm_signal.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
#if HAVE_RTSP_SERVER
|
||||
ZoneMinderFifoSource::ZoneMinderFifoSource(
|
||||
UsageEnvironment& env,
|
||||
std::string fifo,
|
||||
unsigned int queueSize
|
||||
) :
|
||||
FramedSource(env),
|
||||
m_fifo(fifo),
|
||||
m_queueSize(queueSize),
|
||||
m_fd(-1)
|
||||
{
|
||||
m_eventTriggerId = envir().taskScheduler().createEventTrigger(ZoneMinderFifoSource::deliverFrameStub);
|
||||
memset(&m_thid, 0, sizeof(m_thid));
|
||||
memset(&m_mutex, 0, sizeof(m_mutex));
|
||||
pthread_mutex_init(&m_mutex, nullptr);
|
||||
pthread_create(&m_thid, nullptr, threadStub, this);
|
||||
m_buffer_ptr = &m_buffer[0];
|
||||
}
|
||||
|
||||
ZoneMinderFifoSource::~ZoneMinderFifoSource() {
|
||||
stop = 1;
|
||||
envir().taskScheduler().deleteEventTrigger(m_eventTriggerId);
|
||||
pthread_join(m_thid, nullptr);
|
||||
while ( m_captureQueue.size() ) {
|
||||
NAL_Frame * f = m_captureQueue.front();
|
||||
m_captureQueue.pop_front();
|
||||
delete f;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
}
|
||||
|
||||
// thread mainloop
|
||||
void* ZoneMinderFifoSource::thread() {
|
||||
stop = 0;
|
||||
|
||||
while (!stop) {
|
||||
getNextFrame();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// getting FrameSource callback
|
||||
void ZoneMinderFifoSource::doGetNextFrame() {
|
||||
deliverFrame();
|
||||
}
|
||||
|
||||
// stopping FrameSource callback
|
||||
void ZoneMinderFifoSource::doStopGettingFrames() {
|
||||
//stop = 1;
|
||||
Debug(1, "ZoneMinderFifoSource::doStopGettingFrames");
|
||||
FramedSource::doStopGettingFrames();
|
||||
}
|
||||
|
||||
// deliver frame to the sink
|
||||
void ZoneMinderFifoSource::deliverFrame() {
|
||||
if (!isCurrentlyAwaitingData()) {
|
||||
Debug(4, "not awaiting data");
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
if (m_captureQueue.empty()) {
|
||||
Debug(4, "Queue is empty");
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
NAL_Frame *frame = m_captureQueue.front();
|
||||
m_captureQueue.pop_front();
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
|
||||
fDurationInMicroseconds = 0;
|
||||
fFrameSize = 0;
|
||||
|
||||
unsigned int nal_size = frame->size();
|
||||
|
||||
if (nal_size > fMaxSize) {
|
||||
fFrameSize = fMaxSize;
|
||||
fNumTruncatedBytes = nal_size - fMaxSize;
|
||||
} else {
|
||||
fFrameSize = nal_size;
|
||||
}
|
||||
Debug(2, "deliverFrame timestamp: %ld.%06ld size: %d queuesize: %d",
|
||||
frame->m_timestamp.tv_sec, frame->m_timestamp.tv_usec,
|
||||
fFrameSize,
|
||||
m_captureQueue.size()
|
||||
);
|
||||
|
||||
fPresentationTime = frame->m_timestamp;
|
||||
memcpy(fTo, frame->buffer(), fFrameSize);
|
||||
|
||||
if (fFrameSize > 0) {
|
||||
// send Frame to the consumer
|
||||
FramedSource::afterGetting(this);
|
||||
}
|
||||
delete frame;
|
||||
} // end void ZoneMinderFifoSource::deliverFrame()
|
||||
|
||||
// FrameSource callback on read event
|
||||
void ZoneMinderFifoSource::incomingPacketHandler() {
|
||||
if (this->getNextFrame() <= 0) {
|
||||
handleClosure(this);
|
||||
}
|
||||
}
|
||||
|
||||
// read from monitor
|
||||
int ZoneMinderFifoSource::getNextFrame() {
|
||||
if (zm_terminate) return -1;
|
||||
|
||||
if (m_fd == -1) {
|
||||
Debug(1, "Opening fifo %s", m_fifo.c_str());
|
||||
m_fd = open(m_fifo.c_str(), O_RDONLY);
|
||||
if (m_fd < 0) {
|
||||
Error("Can't open %s: %s", m_fifo.c_str(), strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int bytes_in_buffer = m_buffer_ptr - &m_buffer[0];
|
||||
int bytes_read = read(m_fd, m_buffer_ptr, BUFFER_SIZE-bytes_in_buffer);
|
||||
if (bytes_read == 0)
|
||||
return -1;
|
||||
if (bytes_read < 0) {
|
||||
Error("Problem during reading: %s", strerror(errno));
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
return -1;
|
||||
}
|
||||
bytes_in_buffer += bytes_read;
|
||||
unsigned int bytes_remaining = bytes_in_buffer;
|
||||
|
||||
timeval tv;
|
||||
gettimeofday(&tv, nullptr);
|
||||
|
||||
std::list< std::pair<unsigned char*, size_t> > framesList = this->splitFrames(m_buffer, bytes_remaining);
|
||||
Debug(1, "Got %d frames, bytes remaining %d", framesList.size(), bytes_remaining);
|
||||
|
||||
if ( bytes_remaining > 0 ) {
|
||||
memmove(&m_buffer[0], &m_buffer[0] + ( bytes_in_buffer - bytes_remaining ), bytes_remaining);
|
||||
m_buffer_ptr = &m_buffer[0] + bytes_remaining;
|
||||
} else {
|
||||
m_buffer_ptr = &m_buffer[0];
|
||||
}
|
||||
|
||||
while (framesList.size()) {
|
||||
std::pair<unsigned char*, size_t> nal = framesList.front();
|
||||
framesList.pop_front();
|
||||
|
||||
NAL_Frame *frame = new NAL_Frame(nal.first, nal.second, tv);
|
||||
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
if (m_captureQueue.size() > 10) {
|
||||
NAL_Frame * f = m_captureQueue.front();
|
||||
while (m_captureQueue.size() and ((f->m_timestamp.tv_sec - tv.tv_sec) > 2)) {
|
||||
m_captureQueue.pop_front();
|
||||
delete f;
|
||||
f = m_captureQueue.front();
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
while ( m_captureQueue.size() >= m_queueSize ) {
|
||||
Debug(2, "Queue full dropping frame %d", m_captureQueue.size());
|
||||
NAL_Frame * f = m_captureQueue.front();
|
||||
m_captureQueue.pop_front();
|
||||
delete f;
|
||||
}
|
||||
#endif
|
||||
m_captureQueue.push_back(frame);
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
|
||||
// post an event to ask to deliver the frame
|
||||
envir().taskScheduler().triggerEvent(m_eventTriggerId, this);
|
||||
} // end while we get frame from data
|
||||
return 1;
|
||||
}
|
||||
|
||||
// split packet in frames
|
||||
std::list< std::pair<unsigned char*,size_t> > ZoneMinderFifoSource::splitFrames(unsigned char* frame, unsigned &frameSize) {
|
||||
std::list< std::pair<unsigned char*,size_t> > frameList;
|
||||
if ( frame != nullptr ) {
|
||||
frameList.push_back(std::pair<unsigned char*,size_t>(frame, frameSize));
|
||||
}
|
||||
// We consume it all
|
||||
frameSize = 0;
|
||||
return frameList;
|
||||
}
|
||||
|
||||
// extract a frame
|
||||
unsigned char* ZoneMinderFifoSource::extractFrame(unsigned char* frame, size_t& size, size_t& outsize) {
|
||||
outsize = size;
|
||||
size = 0;
|
||||
return frame;
|
||||
}
|
||||
#endif // HAVE_RTSP_SERVER
|
|
@ -0,0 +1,82 @@
|
|||
/* ---------------------------------------------------------------------------
|
||||
**
|
||||
** FifoSource.h
|
||||
**
|
||||
** live555 source
|
||||
**
|
||||
** -------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef ZM_RTSP_SERVER_FIFO_SOURCE_H
|
||||
#define ZM_RTSP_SERVER_FIFO_SOURCE_H
|
||||
|
||||
#include "zm_config.h"
|
||||
#include "zm_define.h"
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#if HAVE_RTSP_SERVER
|
||||
#include <liveMedia.hh>
|
||||
|
||||
class NAL_Frame;
|
||||
|
||||
class ZoneMinderFifoSource: public FramedSource {
|
||||
|
||||
public:
|
||||
static ZoneMinderFifoSource* createNew(
|
||||
UsageEnvironment& env,
|
||||
std::string fifo,
|
||||
unsigned int queueSize
|
||||
) {
|
||||
return new ZoneMinderFifoSource(env, fifo, queueSize);
|
||||
};
|
||||
std::string getAuxLine() { return m_auxLine; };
|
||||
int getWidth() { return m_width; };
|
||||
int getHeight() { return m_height; };
|
||||
int setWidth(int width) { return m_width=width; };
|
||||
int setHeight(int height) { return m_height=height; };
|
||||
|
||||
protected:
|
||||
ZoneMinderFifoSource(UsageEnvironment& env, std::string fifo, unsigned int queueSize);
|
||||
virtual ~ZoneMinderFifoSource();
|
||||
|
||||
protected:
|
||||
static void* threadStub(void* clientData) { return ((ZoneMinderFifoSource*) clientData)->thread();};
|
||||
void* thread();
|
||||
static void deliverFrameStub(void* clientData) {((ZoneMinderFifoSource*) clientData)->deliverFrame();};
|
||||
void deliverFrame();
|
||||
static void incomingPacketHandlerStub(void* clientData, int mask) { ((ZoneMinderFifoSource*) clientData)->incomingPacketHandler(); };
|
||||
void incomingPacketHandler();
|
||||
int getNextFrame();
|
||||
void processFrame(char * frame, int frameSize, const timeval &ref);
|
||||
void queueFrame(char * frame, int frameSize, const timeval &tv);
|
||||
|
||||
// split packet in frames
|
||||
virtual std::list< std::pair<unsigned char*, size_t> > splitFrames(unsigned char* frame, unsigned &frameSize);
|
||||
|
||||
// overide FramedSource
|
||||
virtual void doGetNextFrame();
|
||||
virtual void doStopGettingFrames();
|
||||
virtual unsigned char *extractFrame(unsigned char *data, size_t& size, size_t& outsize);
|
||||
|
||||
protected:
|
||||
std::list<NAL_Frame*> m_captureQueue;
|
||||
EventTriggerId m_eventTriggerId;
|
||||
std::string m_fifo;
|
||||
|
||||
int m_width;
|
||||
int m_height;
|
||||
unsigned int m_queueSize;
|
||||
pthread_t m_thid;
|
||||
pthread_mutex_t m_mutex;
|
||||
std::string m_auxLine;
|
||||
int stop;
|
||||
|
||||
int m_fd;
|
||||
#define BUFFER_SIZE 65536
|
||||
unsigned char m_buffer[BUFFER_SIZE];
|
||||
unsigned char *m_buffer_ptr;
|
||||
};
|
||||
#endif // HAVE_RTSP_SERVER
|
||||
|
||||
#endif // ZM_RTSP_SERVER_FIFO_SOURCE_H
|
|
@ -0,0 +1,20 @@
|
|||
/* ---------------------------------------------------------------------------
|
||||
** This software is in the public domain, furnished "as is", without technical
|
||||
** support, and with no warranty, express or implied, as to its usefulness for
|
||||
** any purpose.
|
||||
**
|
||||
**
|
||||
** ZoneMinder Live555 source
|
||||
**
|
||||
** -------------------------------------------------------------------------*/
|
||||
|
||||
#include "zm_rtsp_server_fifo_video_source.h"
|
||||
|
||||
#if HAVE_RTSP_SERVER
|
||||
ZoneMinderFifoVideoSource::ZoneMinderFifoVideoSource(
|
||||
UsageEnvironment& env, std::string fifo, unsigned int queueSize) :
|
||||
ZoneMinderFifoSource(env,fifo,queueSize)
|
||||
{
|
||||
}
|
||||
|
||||
#endif // HAVE_RTSP_SERVER
|
|
@ -0,0 +1,32 @@
|
|||
/* ---------------------------------------------------------------------------
|
||||
**
|
||||
** FifoSource.h
|
||||
**
|
||||
** live555 source
|
||||
**
|
||||
** -------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef ZM_RTSP_SERVER_FIFO_VIDEO_SOURCE_H
|
||||
#define ZM_RTSP_SERVER_FIFO_VIDEO_SOURCE_H
|
||||
|
||||
#include "zm_rtsp_server_fifo_source.h"
|
||||
|
||||
#if HAVE_RTSP_SERVER
|
||||
|
||||
class ZoneMinderFifoVideoSource: public ZoneMinderFifoSource {
|
||||
|
||||
public:
|
||||
int getWidth() { return m_width; };
|
||||
int getHeight() { return m_height; };
|
||||
int setWidth(int width) { return m_width=width; };
|
||||
int setHeight(int height) { return m_height=height; };
|
||||
|
||||
protected:
|
||||
ZoneMinderFifoVideoSource(UsageEnvironment& env, std::string fifo, unsigned int queueSize);
|
||||
|
||||
int m_width;
|
||||
int m_height;
|
||||
};
|
||||
#endif // HAVE_RTSP_SERVER
|
||||
|
||||
#endif // ZM_RTSP_SERVER_FIFO_VIDEO_SOURCE_H
|
|
@ -0,0 +1,234 @@
|
|||
/* ---------------------------------------------------------------------------
|
||||
**
|
||||
** H264_FifoSource.cpp
|
||||
**
|
||||
** H264 Live555 source
|
||||
**
|
||||
** -------------------------------------------------------------------------*/
|
||||
|
||||
#include "zm_rtsp_server_h264_fifo_source.h"
|
||||
|
||||
#include "zm_config.h"
|
||||
#include "zm_logger.h"
|
||||
#include "zm_rtsp_server_frame.h"
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#if HAVE_RTSP_SERVER
|
||||
// live555
|
||||
#include <Base64.hh>
|
||||
|
||||
// ---------------------------------
|
||||
// H264 ZoneMinder FramedSource
|
||||
// ---------------------------------
|
||||
//
|
||||
H264_ZoneMinderFifoSource::H264_ZoneMinderFifoSource(
|
||||
UsageEnvironment& env,
|
||||
std::string fifo,
|
||||
unsigned int queueSize,
|
||||
bool repeatConfig,
|
||||
bool keepMarker)
|
||||
: H26X_ZoneMinderFifoSource(env, fifo, queueSize, repeatConfig, keepMarker)
|
||||
{
|
||||
// extradata appears to simply be the SPS and PPS NAL's
|
||||
//this->splitFrames(m_stream->codecpar->extradata, m_stream->codecpar->extradata_size);
|
||||
}
|
||||
|
||||
// split packet into frames
|
||||
std::list< std::pair<unsigned char*, size_t> > H264_ZoneMinderFifoSource::splitFrames(unsigned char* frame, unsigned &frameSize) {
|
||||
std::list< std::pair<unsigned char*, size_t> > frameList;
|
||||
|
||||
size_t bufSize = frameSize;
|
||||
size_t size = 0;
|
||||
unsigned char* buffer = this->extractFrame(frame, bufSize, size);
|
||||
bool updateAux = false;
|
||||
while ( buffer != nullptr ) {
|
||||
switch ( m_frameType & 0x1F ) {
|
||||
case 7:
|
||||
Debug(4, "SPS_Size: %d bufSize %d", size, bufSize);
|
||||
m_sps.assign((char*)buffer, size);
|
||||
updateAux = true;
|
||||
break;
|
||||
case 8:
|
||||
Debug(4, "PPS_Size: %d bufSize %d", size, bufSize);
|
||||
m_pps.assign((char*)buffer, size);
|
||||
updateAux = true;
|
||||
break;
|
||||
case 5:
|
||||
Debug(4, "IDR_Size: %d bufSize %d", size, bufSize);
|
||||
if ( m_repeatConfig && !m_sps.empty() && !m_pps.empty() ) {
|
||||
frameList.push_back(std::pair<unsigned char*,size_t>((unsigned char*)m_sps.c_str(), m_sps.size()));
|
||||
frameList.push_back(std::pair<unsigned char*,size_t>((unsigned char*)m_pps.c_str(), m_pps.size()));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Debug(4, "Unknown frametype!? %d %d", m_frameType, m_frameType & 0x1F);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( updateAux and !m_sps.empty() and !m_pps.empty() ) {
|
||||
u_int32_t profile_level_id = 0;
|
||||
if ( m_sps.size() >= 4 ) profile_level_id = (m_sps[1]<<16)|(m_sps[2]<<8)|m_sps[3];
|
||||
|
||||
char* sps_base64 = base64Encode(m_sps.c_str(), m_sps.size());
|
||||
char* pps_base64 = base64Encode(m_pps.c_str(), m_pps.size());
|
||||
|
||||
std::ostringstream os;
|
||||
os << "profile-level-id=" << std::hex << std::setw(6) << std::setfill('0') << profile_level_id;
|
||||
os << ";sprop-parameter-sets=" << sps_base64 << "," << pps_base64;
|
||||
os << "a=x-dimensions:" << m_width << "," << m_height << "\r\n";
|
||||
m_auxLine.assign(os.str());
|
||||
Debug(1, "auxLine: %s", m_auxLine.c_str());
|
||||
|
||||
delete [] sps_base64;
|
||||
delete [] pps_base64;
|
||||
}
|
||||
frameList.push_back(std::pair<unsigned char*,size_t>(buffer, size));
|
||||
|
||||
buffer = this->extractFrame(&buffer[size], bufSize, size);
|
||||
} // end while buffer
|
||||
frameSize = bufSize;
|
||||
return frameList;
|
||||
}
|
||||
|
||||
H265_ZoneMinderFifoSource::H265_ZoneMinderFifoSource(
|
||||
UsageEnvironment& env,
|
||||
std::string fifo,
|
||||
unsigned int queueSize,
|
||||
bool repeatConfig,
|
||||
bool keepMarker)
|
||||
: H26X_ZoneMinderFifoSource(env, fifo, queueSize, repeatConfig, keepMarker)
|
||||
{
|
||||
// extradata appears to simply be the SPS and PPS NAL's
|
||||
// this->splitFrames(m_stream->codecpar->extradata, m_stream->codecpar->extradata_size);
|
||||
}
|
||||
|
||||
// split packet in frames
|
||||
std::list< std::pair<unsigned char*,size_t> >
|
||||
H265_ZoneMinderFifoSource::splitFrames(unsigned char* frame, unsigned frameSize) {
|
||||
std::list< std::pair<unsigned char*,size_t> > frameList;
|
||||
|
||||
size_t bufSize = frameSize;
|
||||
size_t size = 0;
|
||||
unsigned char* buffer = this->extractFrame(frame, bufSize, size);
|
||||
while ( buffer != nullptr ) {
|
||||
switch ((m_frameType&0x7E)>>1) {
|
||||
case 32:
|
||||
Debug(4, "VPS_Size: %d bufSize %d", size, bufSize);
|
||||
m_vps.assign((char*)buffer,size);
|
||||
break;
|
||||
case 33:
|
||||
Debug(4, "SPS_Size: %d bufSize %d", size, bufSize);
|
||||
m_sps.assign((char*)buffer,size);
|
||||
break;
|
||||
case 34:
|
||||
Debug(4, "PPS_Size: %d bufSize %d", size, bufSize);
|
||||
m_pps.assign((char*)buffer,size);
|
||||
break;
|
||||
case 19:
|
||||
case 20:
|
||||
Debug(4, "IDR_Size: %d bufSize %d", size, bufSize);
|
||||
if ( m_repeatConfig && !m_vps.empty() && !m_sps.empty() && !m_pps.empty() ) {
|
||||
frameList.push_back(std::pair<unsigned char*,size_t>((unsigned char*)m_vps.c_str(), m_vps.size()));
|
||||
frameList.push_back(std::pair<unsigned char*,size_t>((unsigned char*)m_sps.c_str(), m_sps.size()));
|
||||
frameList.push_back(std::pair<unsigned char*,size_t>((unsigned char*)m_pps.c_str(), m_pps.size()));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Debug(4, "Unknown frametype!? %d %d", m_frameType, ((m_frameType & 0x7E) >> 1));
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !m_vps.empty() && !m_sps.empty() && !m_pps.empty() ) {
|
||||
char* vps_base64 = base64Encode(m_vps.c_str(), m_vps.size());
|
||||
char* sps_base64 = base64Encode(m_sps.c_str(), m_sps.size());
|
||||
char* pps_base64 = base64Encode(m_pps.c_str(), m_pps.size());
|
||||
|
||||
std::ostringstream os;
|
||||
os << "sprop-vps=" << vps_base64;
|
||||
os << ";sprop-sps=" << sps_base64;
|
||||
os << ";sprop-pps=" << pps_base64;
|
||||
os << "a=x-dimensions:" << m_width << "," << m_height << "\r\n";
|
||||
m_auxLine.assign(os.str());
|
||||
Debug(1, "Assigned auxLine to %s", m_auxLine.c_str());
|
||||
|
||||
delete [] vps_base64;
|
||||
delete [] sps_base64;
|
||||
delete [] pps_base64;
|
||||
}
|
||||
frameList.push_back(std::pair<unsigned char*,size_t>(buffer, size));
|
||||
|
||||
buffer = this->extractFrame(&buffer[size], bufSize, size);
|
||||
} // end while buffer
|
||||
if ( bufSize ) {
|
||||
Debug(1, "%d bytes remaining", bufSize);
|
||||
}
|
||||
frameSize = bufSize;
|
||||
return frameList;
|
||||
} // end H265_ZoneMinderFifoSource::splitFrames(unsigned char* frame, unsigned frameSize)
|
||||
|
||||
unsigned char * H26X_ZoneMinderFifoSource::findMarker(
|
||||
unsigned char *frame, size_t size, size_t &length
|
||||
) {
|
||||
//Debug(1, "findMarker %p %d", frame, size);
|
||||
unsigned char *start = nullptr;
|
||||
for ( size_t i = 0; i < size-2; i += 1 ) {
|
||||
//Debug(1, "%d: %d %d %d", i, frame[i], frame[i+1], frame[i+2]);
|
||||
if ( (frame[i] == 0) and (frame[i+1]) == 0 and (frame[i+2] == 1) ) {
|
||||
if ( i and (frame[i-1] == 0) ) {
|
||||
start = frame + i - 1;
|
||||
length = sizeof(H264marker);
|
||||
} else {
|
||||
start = frame + i;
|
||||
length = sizeof(H264shortmarker);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
// extract a frame
|
||||
unsigned char* H26X_ZoneMinderFifoSource::extractFrame(unsigned char* frame, size_t& size, size_t& outsize) {
|
||||
unsigned char *outFrame = nullptr;
|
||||
Debug(4, "ExtractFrame: %p %d", frame, size);
|
||||
outsize = 0;
|
||||
size_t markerLength = 0;
|
||||
size_t endMarkerLength = 0;
|
||||
m_frameType = 0;
|
||||
unsigned char *startFrame = nullptr;
|
||||
if ( size >= 3 )
|
||||
startFrame = this->findMarker(frame, size, markerLength);
|
||||
if ( startFrame != nullptr ) {
|
||||
Debug(4, "startFrame: %p marker Length %d", startFrame, markerLength);
|
||||
m_frameType = startFrame[markerLength];
|
||||
|
||||
int remainingSize = size-(startFrame-frame+markerLength);
|
||||
unsigned char *endFrame = nullptr;
|
||||
if ( remainingSize > 3 ) {
|
||||
endFrame = this->findMarker(startFrame+markerLength, remainingSize, endMarkerLength);
|
||||
}
|
||||
Debug(4, "endFrame: %p marker Length %d, remaining size %d", endFrame, endMarkerLength, remainingSize);
|
||||
|
||||
if ( m_keepMarker ) {
|
||||
size -= startFrame-frame;
|
||||
outFrame = startFrame;
|
||||
} else {
|
||||
size -= startFrame-frame+markerLength;
|
||||
outFrame = &startFrame[markerLength];
|
||||
}
|
||||
|
||||
if ( endFrame != nullptr ) {
|
||||
outsize = endFrame - outFrame;
|
||||
} else {
|
||||
outsize = size;
|
||||
}
|
||||
size -= outsize;
|
||||
Debug(4, "Have frame type: %d size %d, keepmarker %d", m_frameType, outsize, m_keepMarker);
|
||||
} else if ( size >= sizeof(H264shortmarker) ) {
|
||||
Info("No marker found size %d", size);
|
||||
}
|
||||
|
||||
return outFrame;
|
||||
}
|
||||
#endif // HAVE_RTSP_SERVER
|
|
@ -0,0 +1,99 @@
|
|||
/* ---------------------------------------------------------------------------
|
||||
** This software is in the public domain, furnished "as is", without technical
|
||||
** support, and with no warranty, express or implied, as to its usefulness for
|
||||
** any purpose.
|
||||
**
|
||||
** H264_ZoneMinderFifoSource.h
|
||||
**
|
||||
** H264 ZoneMinder live555 source
|
||||
**
|
||||
** -------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef ZM_RTSP_H264_FIFO_SOURCE_H
|
||||
#define ZM_RTSP_H264_FIFO_SOURCE_H
|
||||
|
||||
#include "zm_config.h"
|
||||
#include "zm_rtsp_server_fifo_video_source.h"
|
||||
|
||||
// ---------------------------------
|
||||
// H264 ZoneMinder FramedSource
|
||||
// ---------------------------------
|
||||
#if HAVE_RTSP_SERVER
|
||||
class H26X_ZoneMinderFifoSource : public ZoneMinderFifoVideoSource {
|
||||
protected:
|
||||
H26X_ZoneMinderFifoSource(
|
||||
UsageEnvironment& env,
|
||||
std::string fifo,
|
||||
unsigned int queueSize,
|
||||
bool repeatConfig,
|
||||
bool keepMarker)
|
||||
:
|
||||
ZoneMinderFifoVideoSource(env, fifo, queueSize),
|
||||
m_repeatConfig(repeatConfig),
|
||||
m_keepMarker(keepMarker),
|
||||
m_frameType(0) { }
|
||||
|
||||
virtual ~H26X_ZoneMinderFifoSource() {}
|
||||
|
||||
virtual unsigned char* extractFrame(unsigned char* frame, size_t& size, size_t& outsize);
|
||||
virtual unsigned char* findMarker(unsigned char *frame, size_t size, size_t &length);
|
||||
|
||||
protected:
|
||||
std::string m_sps;
|
||||
std::string m_pps;
|
||||
bool m_repeatConfig;
|
||||
bool m_keepMarker;
|
||||
int m_frameType;
|
||||
};
|
||||
|
||||
class H264_ZoneMinderFifoSource : public H26X_ZoneMinderFifoSource {
|
||||
public:
|
||||
static H264_ZoneMinderFifoSource* createNew(
|
||||
UsageEnvironment& env,
|
||||
std::string fifo,
|
||||
unsigned int queueSize,
|
||||
bool repeatConfig,
|
||||
bool keepMarker) {
|
||||
return new H264_ZoneMinderFifoSource(env, fifo, queueSize, repeatConfig, keepMarker);
|
||||
}
|
||||
|
||||
protected:
|
||||
H264_ZoneMinderFifoSource(
|
||||
UsageEnvironment& env,
|
||||
std::string fifo,
|
||||
unsigned int queueSize,
|
||||
bool repeatConfig,
|
||||
bool keepMarker);
|
||||
|
||||
// overide ZoneMinderFifoSource
|
||||
virtual std::list< std::pair<unsigned char*,size_t> > splitFrames(unsigned char* frame, unsigned &frameSize);
|
||||
};
|
||||
|
||||
class H265_ZoneMinderFifoSource : public H26X_ZoneMinderFifoSource {
|
||||
public:
|
||||
static H265_ZoneMinderFifoSource* createNew(
|
||||
UsageEnvironment& env,
|
||||
std::string fifo,
|
||||
unsigned int queueSize,
|
||||
bool repeatConfig,
|
||||
bool keepMarker) {
|
||||
return new H265_ZoneMinderFifoSource(env, fifo, queueSize, repeatConfig, keepMarker);
|
||||
}
|
||||
|
||||
protected:
|
||||
H265_ZoneMinderFifoSource(
|
||||
UsageEnvironment& env,
|
||||
std::string fifo,
|
||||
unsigned int queueSize,
|
||||
bool repeatConfig,
|
||||
bool keepMarker);
|
||||
|
||||
// overide ZoneMinderFifoSource
|
||||
virtual std::list< std::pair<unsigned char*,size_t> > splitFrames(unsigned char* frame, unsigned frameSize);
|
||||
|
||||
protected:
|
||||
std::string m_vps;
|
||||
};
|
||||
#endif // HAVE_RTSP_SERVER
|
||||
|
||||
#endif // ZM_RTSP_H264_FIFO_SOURCE_H
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "zm_config.h"
|
||||
#include "zm_rtsp_server_adts_source.h"
|
||||
#include "zm_rtsp_server_adts_fifo_source.h"
|
||||
#include <sstream>
|
||||
|
||||
#if HAVE_RTSP_SERVER
|
||||
|
@ -15,16 +16,18 @@
|
|||
// BaseServerMediaSubsession
|
||||
// ---------------------------------
|
||||
FramedSource* BaseServerMediaSubsession::createSource(
|
||||
UsageEnvironment& env, FramedSource* inputSource, const std::string& format)
|
||||
UsageEnvironment& env,
|
||||
FramedSource* inputSource,
|
||||
const std::string& format)
|
||||
{
|
||||
FramedSource* source = nullptr;
|
||||
if ( format == "video/MP2T" ) {
|
||||
if (format == "video/MP2T") {
|
||||
source = MPEG2TransportStreamFramer::createNew(env, inputSource);
|
||||
} else if ( format == "video/H264" ) {
|
||||
} else if (format == "video/H264") {
|
||||
source = H264VideoStreamDiscreteFramer::createNew(env, inputSource);
|
||||
}
|
||||
#if LIVEMEDIA_LIBRARY_VERSION_INT > 1414454400
|
||||
else if ( format == "video/H265" ) {
|
||||
else if (format == "video/H265") {
|
||||
source = H265VideoStreamDiscreteFramer::createNew(env, inputSource);
|
||||
}
|
||||
#endif
|
||||
|
@ -49,21 +52,21 @@ RTPSink* BaseServerMediaSubsession::createSink(
|
|||
) {
|
||||
|
||||
RTPSink* sink = nullptr;
|
||||
if ( format == "video/MP2T" ) {
|
||||
if (format == "video/MP2T") {
|
||||
sink = SimpleRTPSink::createNew(env, rtpGroupsock, rtpPayloadTypeIfDynamic, 90000, "video", "MP2T", 1, true, false);
|
||||
} else if ( format == "video/H264" ) {
|
||||
} else if (format == "video/H264") {
|
||||
sink = H264VideoRTPSink::createNew(env, rtpGroupsock, rtpPayloadTypeIfDynamic);
|
||||
} else if ( format == "video/VP8" ) {
|
||||
} else if (format == "video/VP8") {
|
||||
sink = VP8VideoRTPSink::createNew(env, rtpGroupsock, rtpPayloadTypeIfDynamic);
|
||||
}
|
||||
#if LIVEMEDIA_LIBRARY_VERSION_INT > 1414454400
|
||||
else if ( format == "video/VP9" ) {
|
||||
else if (format == "video/VP9") {
|
||||
sink = VP9VideoRTPSink::createNew(env, rtpGroupsock, rtpPayloadTypeIfDynamic);
|
||||
} else if ( format == "video/H265" ) {
|
||||
} else if (format == "video/H265") {
|
||||
sink = H265VideoRTPSink::createNew(env, rtpGroupsock, rtpPayloadTypeIfDynamic);
|
||||
#endif
|
||||
} else if ( format == "audio/AAC" ) {
|
||||
ADTS_ZoneMinderDeviceSource *adts_source = (ADTS_ZoneMinderDeviceSource *)(m_replicator->inputSource());
|
||||
} else if (format == "audio/AAC") {
|
||||
ADTS_ZoneMinderFifoSource *adts_source = (ADTS_ZoneMinderFifoSource *)(m_replicator->inputSource());
|
||||
sink = MPEG4GenericRTPSink::createNew(env, rtpGroupsock,
|
||||
rtpPayloadTypeIfDynamic,
|
||||
adts_source->samplingFrequency(),
|
||||
|
@ -83,24 +86,20 @@ RTPSink* BaseServerMediaSubsession::createSink(
|
|||
}
|
||||
|
||||
char const* BaseServerMediaSubsession::getAuxLine(
|
||||
ZoneMinderDeviceSource* source,
|
||||
ZoneMinderFifoSource* source,
|
||||
unsigned char rtpPayloadType
|
||||
) {
|
||||
const char* auxLine = nullptr;
|
||||
if ( source ) {
|
||||
if (source) {
|
||||
std::ostringstream os;
|
||||
os << "a=fmtp:" << int(rtpPayloadType) << " ";
|
||||
os << source->getAuxLine();
|
||||
os << "\r\n";
|
||||
int width = source->getWidth();
|
||||
int height = source->getHeight();
|
||||
if ( (width > 0) && (height>0) ) {
|
||||
os << "a=x-dimensions:" << width << "," << height << "\r\n";
|
||||
}
|
||||
auxLine = strdup(os.str().c_str());
|
||||
Debug(1, "auxLine: %s", auxLine);
|
||||
} else {
|
||||
Error("No source auxLine: ");
|
||||
Error("No source auxLine:");
|
||||
return "";
|
||||
}
|
||||
return auxLine;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define ZM_RTSP_SERVER_SERVER_MEDIA_SUBSESSION_H
|
||||
|
||||
#include "zm_config.h"
|
||||
#include "zm_rtsp_server_fifo_source.h"
|
||||
#include <string>
|
||||
|
||||
#if HAVE_RTSP_SERVER
|
||||
|
@ -36,7 +37,7 @@ class BaseServerMediaSubsession {
|
|||
FramedSource *source);
|
||||
|
||||
char const* getAuxLine(
|
||||
ZoneMinderDeviceSource* source,
|
||||
ZoneMinderFifoSource* source,
|
||||
unsigned char rtpPayloadType);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
#include "zm_config.h"
|
||||
#include "zm_rtsp_server_adts_source.h"
|
||||
#include "zm_rtsp_server_adts_fifo_source.h"
|
||||
#include "zm_rtsp_server_h264_device_source.h"
|
||||
#include "zm_rtsp_server_h264_fifo_source.h"
|
||||
#include "zm_rtsp_server_unicast_server_media_subsession.h"
|
||||
|
||||
#if HAVE_RTSP_SERVER
|
||||
|
@ -31,7 +33,6 @@ RTSPServerThread::RTSPServerThread(std::shared_ptr<Monitor> monitor) :
|
|||
return;
|
||||
}
|
||||
const char *prefix = rtspServer->rtspURLPrefix();
|
||||
Debug(1, "RTSP prefix is %s", prefix);
|
||||
delete[] prefix;
|
||||
} // end RTSPServerThread::RTSPServerThread
|
||||
|
||||
|
@ -68,7 +69,60 @@ bool RTSPServerThread::stopped() const {
|
|||
return terminate ? true : false;
|
||||
} // end RTSPServerThread::stopped()
|
||||
|
||||
void RTSPServerThread::addStream(AVStream *video_stream, AVStream *audio_stream) {
|
||||
ServerMediaSession *RTSPServerThread::addSession(std::string &streamname) {
|
||||
ServerMediaSession *sms = ServerMediaSession::createNew(*env, streamname.c_str());
|
||||
if (sms) {
|
||||
rtspServer->addServerMediaSession(sms);
|
||||
char *url = rtspServer->rtspURL(sms);
|
||||
Debug(1, "url is %s for stream %s", url, streamname.c_str());
|
||||
delete[] url;
|
||||
}
|
||||
return sms;
|
||||
}
|
||||
|
||||
FramedSource *RTSPServerThread::addFifo(
|
||||
ServerMediaSession *sms,
|
||||
std::string fifo) {
|
||||
if (!rtspServer) return nullptr;
|
||||
|
||||
int queueSize = 30;
|
||||
bool repeatConfig = true;
|
||||
bool muxTS = false;
|
||||
FramedSource *source = nullptr;
|
||||
|
||||
if (!fifo.empty()) {
|
||||
StreamReplicator* replicator = nullptr;
|
||||
std::string rtpFormat;
|
||||
if (fifo.find("h264")) {
|
||||
rtpFormat = "video/H264";
|
||||
source = H264_ZoneMinderFifoSource::createNew(*env, fifo, queueSize, repeatConfig, muxTS);
|
||||
} else if (fifo.find("h265")) {
|
||||
rtpFormat = "video/H265";
|
||||
source = H265_ZoneMinderFifoSource::createNew(*env, fifo, queueSize, repeatConfig, muxTS);
|
||||
} else if (fifo.find("aac")) {
|
||||
rtpFormat = "audio/AAC";
|
||||
source = ADTS_ZoneMinderFifoSource::createNew(*env, fifo, queueSize);
|
||||
Debug(1, "ADTS source %p", source);
|
||||
} else {
|
||||
Warning("Unknown format in %s", fifo.c_str());
|
||||
}
|
||||
if (source == nullptr) {
|
||||
Error("Unable to create source");
|
||||
} else {
|
||||
replicator = StreamReplicator::createNew(*env, source, false);
|
||||
}
|
||||
sources.push_back(source);
|
||||
|
||||
if (replicator) {
|
||||
sms->addSubsession(UnicastServerMediaSubsession::createNew(*env, replicator, rtpFormat));
|
||||
}
|
||||
} else {
|
||||
Debug(1, "Not Adding stream");
|
||||
}
|
||||
return source;
|
||||
} // end void addFifo
|
||||
|
||||
void RTSPServerThread::addStream(std::string &streamname, AVStream *video_stream, AVStream *audio_stream) {
|
||||
if ( !rtspServer )
|
||||
return;
|
||||
|
||||
|
@ -101,7 +155,7 @@ void RTSPServerThread::addStream(AVStream *video_stream, AVStream *audio_stream)
|
|||
// Create Unicast Session
|
||||
if ( videoReplicator ) {
|
||||
if ( !sms )
|
||||
sms = ServerMediaSession::createNew(*env, "streamname");
|
||||
sms = ServerMediaSession::createNew(*env, streamname.c_str());
|
||||
sms->addSubsession(UnicastServerMediaSubsession::createNew(*env, videoReplicator, rtpFormat));
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +173,7 @@ void RTSPServerThread::addStream(AVStream *video_stream, AVStream *audio_stream)
|
|||
}
|
||||
if ( replicator ) {
|
||||
if ( !sms )
|
||||
sms = ServerMediaSession::createNew(*env, "streamname");
|
||||
sms = ServerMediaSession::createNew(*env, streamname.c_str());
|
||||
sms->addSubsession(UnicastServerMediaSubsession::createNew(*env, replicator, rtpFormat));
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "zm_config.h"
|
||||
#include "zm_ffmpeg.h"
|
||||
#include "zm_thread.h"
|
||||
#include "zm_rtsp_server_server_media_subsession.h"
|
||||
#include "zm_rtsp_server_fifo_source.h"
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
|
@ -28,7 +30,9 @@ class RTSPServerThread : public Thread {
|
|||
public:
|
||||
explicit RTSPServerThread(std::shared_ptr<Monitor> monitor);
|
||||
~RTSPServerThread();
|
||||
void addStream(AVStream *, AVStream *);
|
||||
ServerMediaSession *addSession(std::string &streamname);
|
||||
void addStream(std::string &streamname, AVStream *, AVStream *);
|
||||
FramedSource *addFifo(ServerMediaSession *sms, std::string fifo);
|
||||
int run();
|
||||
void stop();
|
||||
bool stopped() const;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "zm_config.h"
|
||||
#include "zm_rtsp_server_device_source.h"
|
||||
#include "zm_rtsp_server_fifo_source.h"
|
||||
|
||||
#if HAVE_RTSP_SERVER
|
||||
// -----------------------------------------
|
||||
|
@ -45,6 +46,8 @@ RTPSink* UnicastServerMediaSubsession::createNewRTPSink(
|
|||
char const* UnicastServerMediaSubsession::getAuxSDPLine(
|
||||
RTPSink* rtpSink, FramedSource* inputSource
|
||||
) {
|
||||
return this->getAuxLine(dynamic_cast<ZoneMinderDeviceSource*>(m_replicator->inputSource()), rtpSink->rtpPayloadType());
|
||||
return this->getAuxLine(
|
||||
dynamic_cast<ZoneMinderFifoSource*>(m_replicator->inputSource()),
|
||||
rtpSink->rtpPayloadType());
|
||||
}
|
||||
#endif // HAVE_RTSP_SERVER
|
||||
|
|
Loading…
Reference in New Issue