39static const char * STREAM_TAB =
"Streaming";
45 : currentDevice(defaultDevice)
92 return d->getDeviceName();
114 StreamTimeNP[0].
fill(
"STREAM_DELAY_TIME",
"Delay (s)",
"%.3f", 0, 60, 0.001, 0);
131 std::string defaultDirectory = std::string(getenv(
"HOME")) + std::string(
"/Videos/indi__D_");
139 RecordOptionsNP[0].
fill(
"RECORD_DURATION",
"Duration (sec)",
"%.3f", 0.001, 999999.0, 0.0, 1.0);
140 RecordOptionsNP[1].
fill(
"RECORD_FRAME_TOTAL",
"Frames",
"%.f", 1.0, 999999999.0, 1.0, 30.0);
199 return d->initProperties();
227 d->ISGetProperties(dev);
280 return d->updateProperties();
328 size_t allocatedSize = nbytes *
framesIncoming.size() / 1024 / 1024;
331 LOG_WARN(
"Frame buffer is full, skipping frame...");
335 std::vector<uint8_t> copyBuffer(buffer, buffer + nbytes);
350 LOG_INFO(
"Waiting for all buffered frames to be recorded");
355 "Ending record after %g millisecs and %d frames",
373 d->newFrame(buffer, nbytes, timestamp);
382 uint8_t bytesPerComponent = (
PixelDepth + 7) / 8;
390 components * bytesPerComponent
397 components * bytesPerComponent
416 const uint8_t *srcBuffer,
423 uint32_t srcStride = srcFrameInfo.
lineSize();
426 srcBuffer += srcOffset;
431 memcpy(dstBuffer, srcBuffer, dstStride);
432 dstBuffer += dstStride;
433 srcBuffer += srcStride;
440 sourceTimeFrame.
time = 0;
442 std::vector<uint8_t> subframeBuffer;
443 std::vector<uint8_t> downscaleBuffer;
455 std::vector<uint8_t> *sourceBuffer = &sourceTimeFrame.
frame;
462 LOGF_ERROR(
"Source buffer size %d is less than frame size %d, skipping frame...", sourceBuffer->size(),
477 sourceBuffer = &subframeBuffer;
506 reinterpret_cast<const uint16_t*
>(sourceBuffer->data()),
507 downscaleBuffer.size(),
508 downscaleBuffer.data()
511 sourceBuffer = &downscaleBuffer;
515 previewThreadPool.
start(std::bind([
this, &previewElapsed](
const std::atomic_bool & isAboutToQuit,
516 std::vector<uint8_t> frame)
519 previewElapsed.
start();
524 }, std::placeholders::_1, std::move(*sourceBuffer)));
534 LOG_WARN(
"Cannot subframe JPEG streams.");
570 std::lock_guard<std::mutex> lock(d->recordMutex);
571 return d->recorder->close();
580 if (recorderOK ==
false)
589 if (encoderOK ==
false)
606 return d->setPixelFormat(pixelFormat, pixelDepth);
613 d->setSize(width, height);
627 std::string result = fname;
629 std::time_t t = std::time(
nullptr);
630 std::tm tm = *std::gmtime(&t);
632 auto extendedPatterns = patterns;
633 extendedPatterns[
"_D_"] =
format_time(tm,
"%Y-%m-%d");
634 extendedPatterns[
"_H_"] =
format_time(tm,
"%H-%M-%S");
635 extendedPatterns[
"_T_"] =
format_time(tm,
"%Y-%m-%d" "@" "%H-%M-%S");
637 for(
const auto &pattern : extendedPatterns)
639 replace_all(result, pattern.first, pattern.second);
643 std::replace(result.begin(), result.end(),
':',
'-');
651 std::string filename, expfilename, expfiledir;
652 std::string filtername;
653 std::map<std::string, std::string> patterns;
666 patterns[
"_F_"] = filtername;
667 LOGF_DEBUG(
"Adding filter pattern %s", filtername.c_str());
676 if (
const char * home = getenv(
"HOME"))
677 patterns[
"_HOME_"] = home;
682 if (expfiledir.at(expfiledir.size() - 1) !=
'/')
686 if (expfilename.size() < 4 || expfilename.substr(expfilename.size() - 4, 4) !=
recorder->
getExtension())
689 filename = expfiledir + expfilename;
692 LOGF_INFO(
"Record file is %s", filename.c_str());
694 if (
mkpath(expfiledir, 0755))
696 LOGF_WARN(
"Can not create record directory %s: %s", expfiledir.c_str(),
704 LOGF_WARN(
"Can not open record file: %s", errmsg);
713 LOG_INFO(
"Using direct recording (no software cropping).");
790 "Record Duration: %g millisec / %d frames",
807 for (
int i = 0; i < n; i++)
809 if (!strcmp(names[i],
"STREAM_ON") && states[i] ==
ISS_ON)
814 else if (!strcmp(names[i],
"STREAM_OFF") && states[i] ==
ISS_ON)
834 LOG_WARN(
"Recording device is busy.");
870 LOG_INFO(
"Recording stream has been disabled. Closing the stream...");
889 if (!strcmp(selectedEncoder, oneEncoder->getName()))
914 if (!strcmp(selectedRecorder, oneRecorder->getName()))
947 return d->ISNewSwitch(dev, name, states, names, n);
959 if (strchr(tp->getText(),
'/'))
961 LOG_WARN(
"Dir. separator (/) not allowed in filename.");
977 return d->ISNewText(dev, name, texts, names, n);
1012 LOG_WARN(
"Recording device is busy");
1027 LOG_WARN(
"Recording device is busy");
1064 return d->ISNewNumber(dev, name, values, names, n);
1075 if (StreamOptionsN[OPTION_RATE_DIVISOR].value > 0)
1077 "Starting the video stream with target FPS %.f and rate divisor of %.f",
1078 StreamOptionsN[OPTION_TARGET_FPS].value, StreamOptionsN[OPTION_RATE_DIVISOR].value);
1080 LOGF_INFO(
"Starting the video stream with target FPS %.f", StreamOptionsN[OPTION_TARGET_FPS].value);
1082 LOGF_INFO(
"Starting the video stream with target exposure %.6f s (Max theoretical FPS %.f)",
1098 LOG_ERROR(
"Failed to start streaming.");
1110 LOG_ERROR(
"Failed to start streaming.");
1174 return d->setStream(enable);
1180 d->EncoderSP.save(fp);
1181 d->RecordFileTP.save(fp);
1182 d->RecordOptionsNP.save(fp);
1183 d->RecorderSP.save(fp);
1184 d->LimitsNP.save(fp);
1185 d->FullDepthSP.save(fp);
1213 d->getStreamFrame(x, y, w, h);
1221 imageBP[0].setBlob(
const_cast<uint8_t *
>(buffer));
1222 imageBP[0].setBlobLen(nbytes);
1224 imageBP[0].setFormat(
".stream_jpg");
1274 return d->direct_record;
1280 return d->isStreaming;
1286 return d->isRecording && !d->isRecordingAboutToClose;
1292 return (d->isStreaming || d->isRecording);
1298 return 1.0 / d->StreamExposureNP[0].getValue();
1303 return d->StreamExposureNP[0].getValue();
1309 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...
bool updateProperty(PropertyType &property, ValueType *values, char *names[], int n, std::function< bool()> updater, bool saveConfig=false)
Generic helper function to update an INDI property based on an external operation.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_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 bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth=8)=0
virtual bool setFPS(float FPS)
virtual void setStreamEnabled(bool enable)=0
virtual const char * getExtension()=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::PropertySwitch FullDepthSP
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,...)
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