src/zm_stream.h
Rama master · réplica de lectura del repositorio original.
//
// ZoneMinder Stream Interfaces, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes
//
// 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.
//
#ifndef ZM_STREAM_H
#define ZM_STREAM_H
#include "zm_box.h"
#include "zm_logger.h"
#include "zm_mpeg.h"
#include "zm_time.h"
#include <atomic>
#include <memory>
#include <mutex>
#include <sys/un.h>
class Image;
class Monitor;
#define BOUNDARY "ZoneMinderFrame"
class StreamBase {
public:
typedef enum {
STREAM_JPEG,
STREAM_RAW,
STREAM_ZIP,
STREAM_SINGLE,
STREAM_MPEG
} StreamType;
typedef enum { FRAME_NORMAL, FRAME_ANALYSIS } FrameType;
protected:
static constexpr Seconds MAX_STREAM_DELAY = Seconds(5);
static constexpr Milliseconds MAX_SLEEP = Milliseconds(500);
static const StreamType DEFAULT_TYPE = STREAM_JPEG;
enum { DEFAULT_RATE=ZM_RATE_BASE };
enum { DEFAULT_SCALE=ZM_SCALE_BASE };
enum { DEFAULT_ZOOM=ZM_SCALE_BASE };
enum { DEFAULT_MAXFPS=10 };
enum { DEFAULT_BITRATE=100000 };
// Offset applied when encoding a signed replay rate as a uint16 for CMD_VARPLAY.
// On the wire: uint16 = rate + VARPLAY_RATE_OFFSET. Receiver subtracts the same offset.
static const int VARPLAY_RATE_OFFSET = 32768;
protected:
typedef struct {
int msg_type;
char msg_data[16];
} CmdMsg;
typedef struct {
int msg_type;
char msg_data[256];
} DataMsg;
typedef enum {
MSG_CMD=1,
MSG_DATA_WATCH,
MSG_DATA_EVENT
} MsgType;
typedef enum {
CMD_NONE=0,
CMD_PAUSE,
// CMD_PLAY resumes or starts playback at normal speed (1x, i.e. replay_rate = ZM_RATE_BASE).
// Use CMD_VARPLAY to resume at an arbitrary rate.
CMD_PLAY,
// CMD_STOP halts all streaming activity. Unlike CMD_PAUSE, no keepalive frames are sent
// and the stream does no work until a new command is received.
CMD_STOP,
CMD_FASTFWD,
CMD_SLOWFWD,
CMD_SLOWREV,
CMD_FASTREV,
CMD_ZOOMIN,
CMD_ZOOMOUT,
CMD_PAN,
CMD_SCALE,
CMD_PREV,
CMD_NEXT,
CMD_SEEK,
// CMD_VARPLAY resumes or starts playback at a caller-specified rate.
// The desired rate is packed as a big-endian uint16 offset by +VARPLAY_RATE_OFFSET so that
// the range [-32768, +32767] maps to [0, 65535]. ZM_RATE_BASE (100) represents 1x speed, so:
// 32868 (= VARPLAY_RATE_OFFSET + 100) encodes 1x forward playback,
// 32668 (= VARPLAY_RATE_OFFSET - 100) encodes 1x reverse playback.
// Negative rates play in reverse; rates > ZM_RATE_BASE play faster than real-time.
// MSG payload: msg_data[1..2] = (rate + VARPLAY_RATE_OFFSET) as network-byte-order uint16.
CMD_VARPLAY,
CMD_GET_IMAGE,
CMD_QUIT,
CMD_MAXFPS,
CMD_ANALYZE_ON,
CMD_ANALYZE_OFF,
CMD_ZOOMSTOP,
// Tell an event stream which event to load next/prev, computed UI-side so the
// filter/sort the user is viewing is honoured without reimplementing filters
// in C++ (refs #4186). MSG payload:
// msg_data[1] = direction (0 = next, 1 = prev)
// msg_data[2..9] = target event id, big-endian uint64 (0 = the filter has
// no neighbour in this direction -> stop at the boundary)
// msg_data[10..15]= the event the UI computed this against, big-endian
// 48-bit (source event id). The stream honours a hint
// only while this still matches the event playing, so a
// hint that arrives after playback has moved on is
// ignored instead of mis-steering the next transition.
// 48 bits is ample for any real event id; a larger id
// would simply fail the match and fall back, never
// mis-match to a wrong event.
CMD_SET_EVENT,
CMD_QUERY=99
} MsgCommand;
protected:
int monitor_id;
std::shared_ptr<Monitor> monitor;
StreamType type;
std::atomic<FrameType> frame_type;
const char *format;
// Written by the command thread (processCommand), read by the streaming
// loop every iteration. Atomic so the accesses are defined rather than a
// data race; see https://github.com/ZoneMinder/zoneminder/issues/4939
std::atomic<int> replay_rate;
std::atomic<int> scale;
int last_scale;
std::atomic<int> zoom;
int last_zoom;
Box last_crop;
// The frame size last_crop is in. Event playback can move to a monitor with
// a different resolution.
int last_crop_width, last_crop_height;
int bitrate;
unsigned short last_x, last_y;
std::atomic<unsigned short> x, y;
bool send_analysis;
bool send_objdetect;
int connkey;
// Id of the user zms authenticated (0 without authentication). Part of the
// socket and lock names, so stream commands reach only that user's streams.
int owner;
int sd;
char loc_sock_path[108];
struct sockaddr_un loc_addr;
char rem_sock_path[108];
struct sockaddr_un rem_addr;
char sock_path_lock[108];
int lock_fd;
std::atomic<bool> paused;
std::atomic<bool> stopped;
std::atomic<int> step;
std::atomic<bool> send_twice; // flag to send the same frame twice
TimePoint now;
TimePoint last_comm_update;
std::atomic<double> maxfps;
double base_fps; // Should be capturing fps, hence a rough target
double effective_fps; // Target fps after taking max_fps into account
double actual_fps; // sliding calculated actual streaming fps achieved
TimePoint last_fps_update;
// frame_count and last_frame_sent are written by the streaming loop and
// read (last_frame_sent also written) by processCommand on the command
// thread, so they are atomic like the playback state above. `now` stays
// the loop's own: the command thread reads the clock itself.
std::atomic<int> frame_count; // Count of frames sent
int last_frame_count; // Used in calculating actual_fps from frame_count - last_frame_count
int frame_mod;
int frames_to_send;
std::atomic<TimePoint> last_frame_sent;
SystemTimePoint last_frame_timestamp;
TimePoint when_to_send_next_frame; // When to send next frame so if now < send_next_frame, skip
VideoStream *vid_stream;
CmdMsg msg;
std::atomic<bool> got_command; // commands like zoom should output a frame even if paused; set on the command thread
uint8_t *temp_img_buffer; // Used when encoding or sending file data
size_t temp_img_buffer_size;
protected:
std::mutex monitor_mutex; // Protects monitor connect/disconnect vs command thread access
bool loadMonitor(int monitor_id);
bool checkInitialised();
void updateFrameRate(double fps);
// pre_scaled_by is the scale the caller already produced the image at, for
// callers that have to colour convert anyway and can therefore scale in the
// same swscale pass. 0 means the image is at full size. See #3681.
Image *prepareImage(Image *image, int pre_scaled_by = 0);
// Dimensions a caller should produce the source image at so the scale folds
// into a colour conversion it has to do anyway. Returns the scale used, which
// is what prepareImage wants as pre_scaled_by, or 0 when the fast path does
// not apply and the image must be produced at base_width x base_height.
int preScaleDimensions(int base_width, int base_height, int &width, int &height) const;
void checkCommandQueue();
virtual void processCommand(const CmdMsg *msg)=0;
void reserveTempImgBuffer(size_t size);
public:
StreamBase():
monitor_id(0),
monitor(nullptr),
type(DEFAULT_TYPE),
frame_type(FRAME_NORMAL),
format(""),
replay_rate(DEFAULT_RATE),
scale(DEFAULT_SCALE),
last_scale(DEFAULT_SCALE),
zoom(DEFAULT_ZOOM),
last_zoom(DEFAULT_ZOOM),
last_crop_width(0),
last_crop_height(0),
bitrate(DEFAULT_BITRATE),
last_x(0),
last_y(0),
x(0),
y(0),
send_analysis(false),
send_objdetect(false),
connkey(0),
owner(0),
sd(-1),
lock_fd(-1),
paused(false),
stopped(false),
step(0),
send_twice(false),
maxfps(DEFAULT_MAXFPS),
base_fps(0.0),
effective_fps(0.0),
actual_fps(0.0),
frame_count(0),
last_frame_count(0),
frame_mod(1),
frames_to_send(-1),
last_frame_sent(TimePoint()),
got_command(false),
temp_img_buffer(nullptr),
temp_img_buffer_size(0) {
memset(&loc_sock_path, 0, sizeof(loc_sock_path));
memset(&loc_addr, 0, sizeof(loc_addr));
memset(&rem_sock_path, 0, sizeof(rem_sock_path));
memset(&rem_addr, 0, sizeof(rem_addr));
memset(&sock_path_lock, 0, sizeof(sock_path_lock));
last_fps_update = std::chrono::steady_clock::now();
vid_stream = nullptr;
msg = { 0, { 0 } };
}
virtual ~StreamBase();
void setStreamType(StreamType p_type) {
type = p_type;
#if ! HAVE_ZLIB_H
if ( type == STREAM_ZIP ) {
Error("zlib is required for zipped images. Falling back to raw image");
type = STREAM_RAW;
}
#endif
}
void setStreamFrameType(FrameType p_type) {
frame_type = p_type;
}
void setStreamFormat(const char *p_format) {
format = p_format;
}
void setStreamScale(int p_scale) {
scale = p_scale;
if ( !scale )
scale = DEFAULT_SCALE;
}
void setStreamReplayRate(int p_rate) {
Debug(1, "Setting replay_rate %d", p_rate);
replay_rate = p_rate;
}
void setStreamMaxFPS(double p_maxfps) {
Debug(1, "Setting max fps to %f", p_maxfps);
maxfps = p_maxfps;
}
void setStreamBitrate(int p_bitrate) {
bitrate = p_bitrate;
}
void setStreamQueue(int p_connkey) {
connkey = p_connkey;
}
void setStreamOwner(int p_owner) {
owner = p_owner;
}
void setFramesToSend(int p_frames_to_send) { frames_to_send = p_frames_to_send; }
bool sendTextFrame(const char *text);
virtual void openComms();
virtual void closeComms();
virtual void runStream()=0;
};
#endif // ZM_STREAM_H