39 static const char * STREAM_TAB =
"Streaming";
45 : currentDevice(defaultDevice)
92 return d->getDeviceName();
107 StreamTimeNP[0].
fill(
"STREAM_DELAY_TIME",
"Delay (s)",
"%.3f", 0, 60, 0.001, 0);
121 std::string defaultDirectory = std::string(getenv(
"HOME")) + std::string(
"/indi__D_");
128 RecordOptionsNP[0].
fill(
"RECORD_DURATION",
"Duration (sec)",
"%.3f", 0.001, 999999.0, 0.0, 1.0);
129 RecordOptionsNP[1].
fill(
"RECORD_FRAME_TOTAL",
"Frames",
"%.f", 1.0, 999999999.0, 1.0, 30.0);
182 return d->initProperties();
209 d->ISGetProperties(dev);
260 return d->updateProperties();
308 size_t allocatedSize = nbytes *
framesIncoming.size() / 1024 / 1024;
311 LOG_WARN(
"Frame buffer is full, skipping frame...");
315 std::vector<uint8_t> copyBuffer(
buffer,
buffer + nbytes);
330 LOG_INFO(
"Waiting for all buffered frames to be recorded");
335 "Ending record after %g millisecs and %d frames",
353 d->newFrame(
buffer, nbytes, timestamp);
362 uint8_t bytesPerComponent = (
PixelDepth + 7) / 8;
370 components * bytesPerComponent
377 components * bytesPerComponent
396 const uint8_t *srcBuffer,
403 uint32_t srcStride = srcFrameInfo.
lineSize();
406 srcBuffer += srcOffset;
411 memcpy(dstBuffer, srcBuffer, dstStride);
412 dstBuffer += dstStride;
413 srcBuffer += srcStride;
420 sourceTimeFrame.
time = 0;
422 std::vector<uint8_t> subframeBuffer;
423 std::vector<uint8_t> downscaleBuffer;
435 std::vector<uint8_t> *sourceBuffer = &sourceTimeFrame.
frame;
439 LOG_ERROR(
"Invalid source buffer size, skipping frame...");
453 sourceBuffer = &subframeBuffer;
481 reinterpret_cast<const uint16_t*
>(sourceBuffer->data()),
482 downscaleBuffer.size(),
483 downscaleBuffer.data()
486 sourceBuffer = &downscaleBuffer;
490 previewThreadPool.
start(std::bind([
this, &previewElapsed](
const std::atomic_bool & isAboutToQuit,
491 std::vector<uint8_t> frame)
494 previewElapsed.
start();
499 }, std::placeholders::_1, std::move(*sourceBuffer)));
509 LOG_WARN(
"Cannot subframe JPEG streams.");
545 std::lock_guard<std::mutex> lock(d->recordMutex);
546 return d->recorder->close();
555 if (recorderOK ==
false)
564 if (encoderOK ==
false)
581 return d->setPixelFormat(pixelFormat, pixelDepth);
588 d->setSize(width, height);
602 std::string result = fname;
604 std::time_t t = std::time(
nullptr);
605 std::tm tm = *std::gmtime(&t);
607 auto extendedPatterns = patterns;
608 extendedPatterns[
"_D_"] =
format_time(tm,
"%Y-%m-%d");
609 extendedPatterns[
"_H_"] =
format_time(tm,
"%H-%M-%S");
610 extendedPatterns[
"_T_"] =
format_time(tm,
"%Y-%m-%d" "@" "%H-%M-%S");
612 for(
const auto &pattern : extendedPatterns)
614 replace_all(result, pattern.first, pattern.second);
618 std::replace(result.begin(), result.end(),
':',
'-');
626 std::string filename, expfilename, expfiledir;
627 std::string filtername;
628 std::map<std::string, std::string> patterns;
641 patterns[
"_F_"] = filtername;
642 LOGF_DEBUG(
"Adding filter pattern %s", filtername.c_str());
651 if (expfiledir.at(expfiledir.size() - 1) !=
'/')
655 if (expfilename.size() < 4 || expfilename.substr(expfilename.size() - 4, 4) !=
recorder->
getExtension())
658 filename = expfiledir + expfilename;
661 LOGF_INFO(
"Record file is %s", filename.c_str());
663 if (
mkpath(expfiledir, 0755))
665 LOGF_WARN(
"Can not create record directory %s: %s", expfiledir.c_str(),
673 LOGF_WARN(
"Can not open record file: %s", errmsg);
682 LOG_INFO(
"Using direct recording (no software cropping).");
759 "Record Duration: %g millisec / %d frames",
776 for (
int i = 0; i < n; i++)
778 if (!strcmp(names[i],
"STREAM_ON") && states[i] ==
ISS_ON)
783 else if (!strcmp(names[i],
"STREAM_OFF") && states[i] ==
ISS_ON)
803 LOG_WARN(
"Recording device is busy.");
839 LOG_INFO(
"Recording stream has been disabled. Closing the stream...");
858 if (!strcmp(selectedEncoder, oneEncoder->getName()))
883 if (!strcmp(selectedRecorder, oneRecorder->getName()))
905 return d->ISNewSwitch(dev, name, states, names, n);
917 if (strchr(tp->getText(),
'/'))
919 LOG_WARN(
"Dir. separator (/) not allowed in filename.");
935 return d->ISNewText(dev, name, texts, names, n);
970 LOG_WARN(
"Recording device is busy");
985 LOG_WARN(
"Recording device is busy");
1022 return d->ISNewNumber(dev, name, values, names, n);
1033 if (StreamOptionsN[OPTION_RATE_DIVISOR].value > 0)
1035 "Starting the video stream with target FPS %.f and rate divisor of %.f",
1036 StreamOptionsN[OPTION_TARGET_FPS].value, StreamOptionsN[OPTION_RATE_DIVISOR].value);
1038 LOGF_INFO(
"Starting the video stream with target FPS %.f", StreamOptionsN[OPTION_TARGET_FPS].value);
1040 LOGF_INFO(
"Starting the video stream with target exposure %.6f s (Max theoritical FPS %.f)",
1056 LOG_ERROR(
"Failed to start streaming.");
1068 LOG_ERROR(
"Failed to start streaming.");
1132 return d->setStream(enable);
1138 d->EncoderSP.save(fp);
1139 d->RecordFileTP.save(fp);
1140 d->RecordOptionsNP.save(fp);
1141 d->RecorderSP.save(fp);
1142 d->LimitsNP.save(fp);
1170 d->getStreamFrame(x, y, w, h);
1179 #ifdef HAVE_WEBSOCKET
1183 if (
Format !=
".streajpg")
1194 imageBP[0].setBlobLen(nbytes);
1196 imageBP[0].setFormat(
".stream_jpg");
1216 #ifdef HAVE_WEBSOCKET
1260 return d->direct_record;
1266 return d->isStreaming;
1272 return d->isRecording && !d->isRecordingAboutToClose;
1278 return (d->isStreaming || d->isRecording);
1284 return 1.0 / d->StreamExposureNP[0].getValue();
1289 return d->StreamExposureNP[0].getValue();
1295 d->hasStreamingExposure = enable;
void apply(const uint16_t *source, size_t count, uint8_t *destination) const
const char * getDeviceName() const
INDI::PropertyBlob getBLOB(const char *name) const
bool isCompressed() const
isCompressed
Class to provide general functionality of CCD cameras with a single CCD sensor, or a primary CCD sens...
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint16_t getDriverInterface() const
The ElapsedTimer class provides a fast way to calculate elapsed times.
int64_t nsecsElapsed() const
Returns the number of nanoseconds since this ElapsedTimer was last started.
void start()
Starts this timer. Once started, a timer value can be checked with elapsed().
The EncoderInterface class is the base class for video streaming encoders.
virtual void init(INDI::DefaultDevice *mainDevice)
virtual bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
virtual bool upload(INDI::WidgetViewBlob *bp, const uint8_t *buffer, uint32_t nbytes, bool isCompressed=false)=0
void setEncoder(EncoderInterface *encoder)
EncoderInterface * getDefaultEncoder()
std::vector< EncoderInterface * > getEncoderList()
void reset()
Reset all frame information.
void setTimeWindow(double timeWindow)
Time window setup.
double deltaTime() const
Time in milliseconds between last frames.
double totalTime() const
Total time.
uint64_t totalFrames() const
Total frames.
double framesPerSecond() const
Number of frames per second counted in the time window.
bool newFrame()
When you get a frame, call the function to count.
void setState(IPState state)
WidgetView< T > * findWidgetByName(const char *name) const
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
const char * getName() const
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
INDI::WidgetViewSwitch * findOnSwitch() const
int findOnSwitchIndex() const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
bool update(const char *const texts[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
The RecorderInterface class is the base class for recorders.
virtual bool writeFrame(const uint8_t *frame, uint32_t nbytes, uint64_t timestamp)=0
virtual const char * getName()
virtual bool open(const char *filename, char *errmsg)=0
virtual const char * getExtension()=0
virtual bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth=8)=0
virtual bool setFPS(float FPS)
virtual void setStreamEnabled(bool enable)=0
std::vector< RecorderInterface * > getRecorderList()
RecorderInterface * getDefaultRecorder()
void setRecorder(RecorderInterface *recorder)
The SensorDevice class provides functionality of a Sensor Device within a Sensor.
void start(const std::function< void(const std::atomic_bool &isAboutToClose)> &functionToRun)
Reserves a thread and uses it to run functionToRun. A running function can check the 'isAboutToClose'...
bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
std::atomic< bool > isRecording
bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
INDI::PropertySwitch RecordStreamSP
bool hasStreamingExposure
static std::string expand(const std::string &fname, const std::map< std::string, std::string > &patterns)
void setStreamFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
INDI::PropertySwitch EncoderSP
std::atomic< bool > isStreaming
INDI::PropertySwitch StreamSP
static void subframe(const uint8_t *srcBuffer, const FrameInfo &srcFrameInfo, uint8_t *dstBuffer, const FrameInfo &dstFrameInfo)
void newFrame(const uint8_t *buffer, uint32_t nbytes, uint64_t timestamp)
std::atomic< bool > isRecordingAboutToClose
bool stopRecording(bool force=false)
UniqueQueue< TimeFrame > framesIncoming
virtual ~StreamManagerPrivate()
INDI::PropertyBlob imageBP
std::atomic< bool > framesThreadTerminate
INDI::PropertyNumber RecordOptionsNP
EncoderInterface * encoder
const char * getDeviceName() const
std::string recordfiledir
uint32_t frameCountDivider
INDI::PropertyNumber StreamTimeNP
INDI::PropertyNumber FpsNP
void setSize(uint16_t width, uint16_t height)
EncoderManager encoderManager
bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
std::string recordfilename
bool uploadStream(const uint8_t *buffer, uint32_t nbytes)
uploadStream Upload frame to client using the selected encoder
INDI_PIXEL_FORMAT PixelFormat
RecorderManager recorderManager
bool recordStream(const uint8_t *buffer, uint32_t nbytes, double deltams, uint64_t timestamp)
recordStream Calls the backend recorder to record a single frame.
RecorderInterface * recorder
INDI::PropertyText RecordFileTP
INDI::PropertyNumber StreamFrameNP
bool setStream(bool enable)
DefaultDevice * currentDevice
INDI::PropertyNumber LimitsNP
StreamManagerPrivate(DefaultDevice *defaultDevice)
void getStreamFrame(uint16_t *x, uint16_t *y, uint16_t *w, uint16_t *h) const
void asyncStreamThread()
Thread processing frames and forwarding to recording and preview.
void ISGetProperties(const char *dev)
FrameInfo updateSourceFrameInfo()
bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
INDI::PropertyNumber StreamExposureNP
INDI::PropertySwitch RecorderSP
void setSize(uint16_t width, uint16_t height=1)
bool isDirectRecording() const
void getStreamFrame(uint16_t *x, uint16_t *y, uint16_t *w, uint16_t *h) const
void newFrame(const uint8_t *buffer, uint32_t nbytes, uint64_t timestamp=0)
newFrame CCD drivers call this function when a new frame is received. It is then streamed,...
void setStreamingExposureEnabled(bool enable)
setStreamingExposureEnabled Can stream exposure time be changed?
bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth=8)
double getTargetExposure() const
bool setStream(bool enable)
setStream Enables (starts) or disables (stops) streaming.
double getTargetFPS() const
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
virtual void ISGetProperties(const char *dev)
virtual bool initProperties()
StreamManager(DefaultDevice *currentDevice)
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
virtual bool saveConfigItems(FILE *fp)
RecorderInterface * getRecorder() const
virtual bool updateProperties()
const char * getDeviceName() const
#define LOGF_INFO(fmt,...)
#define LOGF_WARN(fmt,...)
#define LOGF_DEBUG(fmt,...)
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
#define LOGF_ERROR(fmt,...)
#define DEBUGF(priority, msg,...)
std::vector< uint8_t > buffer
Namespace to encapsulate INDI client, drivers, and mediator classes.
std::string format_time(const std::tm &tm, const char *format)
void replace_all(std::string &subject, const std::string &search, const std::string &replace)
int mkpath(std::string s, mode_t mode)
std::vector< uint8_t > frame