zoneminder/src/zm_rtsp_server_device_sourc...

218 lines
6.2 KiB
C++

/* ---------------------------------------------------------------------------
** 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_device_source.h"
#include "zm_config.h"
#include "zm_logger.h"
#include "zm_rtsp_server_frame.h"
#include "zm_signal.h"
#if HAVE_RTSP_SERVER
ZoneMinderDeviceSource::ZoneMinderDeviceSource(
UsageEnvironment& env,
std::shared_ptr<Monitor> monitor,
AVStream *stream,
unsigned int queueSize
) :
FramedSource(env),
m_eventTriggerId(envir().taskScheduler().createEventTrigger(ZoneMinderDeviceSource::deliverFrameStub)),
m_stream(stream),
m_monitor(std::move(monitor)),
m_packetqueue(nullptr),
m_packetqueue_it(nullptr),
m_queueSize(queueSize)
{
memset(&m_thid, 0, sizeof(m_thid));
memset(&m_mutex, 0, sizeof(m_mutex));
if ( m_monitor ) {
m_packetqueue = m_monitor->GetPacketQueue();
if ( !m_packetqueue ) {
Fatal("No packetqueue");
}
pthread_mutex_init(&m_mutex, nullptr);
pthread_create(&m_thid, nullptr, threadStub, this);
} else {
Error("No monitor in ZoneMinderDeviceSource");
}
}
ZoneMinderDeviceSource::~ZoneMinderDeviceSource() {
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* ZoneMinderDeviceSource::thread() {
stop = 0;
while ( !stop ) {
getNextFrame();
}
return nullptr;
}
// getting FrameSource callback
void ZoneMinderDeviceSource::doGetNextFrame() {
deliverFrame();
}
// stopping FrameSource callback
void ZoneMinderDeviceSource::doStopGettingFrames() {
stop = 1;
Debug(1, "ZoneMinderDeviceSource::doStopGettingFrames");
FramedSource::doStopGettingFrames();
}
// deliver frame to the sink
void ZoneMinderDeviceSource::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 stream: %d timestamp: %ld.%06ld size: %d queuesize: %d",
m_stream->index,
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 ZoneMinderDeviceSource::deliverFrame()
// FrameSource callback on read event
void ZoneMinderDeviceSource::incomingPacketHandler() {
if ( this->getNextFrame() <= 0 ) {
handleClosure(this);
}
}
// read from monitor
int ZoneMinderDeviceSource::getNextFrame() {
if ( zm_terminate )
return -1;
if ( !m_packetqueue_it ) {
m_packetqueue_it = m_packetqueue->get_video_it(true);
}
ZMPacket *zm_packet = m_packetqueue->get_packet(m_packetqueue_it);
while ( zm_packet and (zm_packet->packet.stream_index != m_stream->index) ) {
zm_packet->unlock();
// We want our stream to start at the same it as the video
// but if this is an audio stream we need to increment past that first packet
Debug(4, "Have audio packet, skipping");
m_packetqueue->increment_it(m_packetqueue_it, m_stream->index);
zm_packet = m_packetqueue->get_packet(m_packetqueue_it);
}
if ( !zm_packet ) {
Debug(1, "null zm_packet %p", zm_packet);
return -1;
}
// packet is locked
AVPacket *pkt = &zm_packet->packet;
// Convert pts to timeval
int64_t pts = av_rescale_q(pkt->dts, m_stream->time_base, AV_TIME_BASE_Q);
timeval tv = { pts/1000000, pts%1000000 };
ZM_DUMP_STREAM_PACKET(m_stream, (*pkt), "rtspServer");
Debug(2, "pts %" PRId64 " pkt.pts %" PRId64 " tv %d.%d", pts, pkt->pts, tv.tv_sec, tv.tv_usec);
std::list< std::pair<unsigned char*, size_t> > framesList = this->splitFrames(pkt->data, pkt->size);
zm_packet->unlock();
zm_packet = nullptr;// we no longer have the lock so shouldn't be accessing it
m_packetqueue->increment_it(m_packetqueue_it, m_stream->index);
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() ) {
NAL_Frame * f = m_captureQueue.front();
while ( m_captureQueue.size() and ((f->m_timestamp.tv_sec - tv.tv_sec) > 10) ) {
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> > ZoneMinderDeviceSource::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));
}
return frameList;
}
// extract a frame
unsigned char* ZoneMinderDeviceSource::extractFrame(unsigned char* frame, size_t& size, size_t& outsize) {
outsize = size;
size = 0;
return frame;
}
#endif // HAVE_RTSP_SERVER