Instrument Neutral Distributed Interface INDI
1.9.5
|
Go to the documentation of this file.
39 static const char * STREAM_TAB =
"Streaming";
45 : currentDevice(defaultDevice)
88 return d->getDeviceName();
103 StreamTimeNP[0].
fill(
"STREAM_DELAY_TIME",
"Delay (s)",
"%.3f", 0, 60, 0.001, 0);
117 std::string defaultDirectory = std::string(getenv(
"HOME")) + std::string(
"/indi__D_");
124 RecordOptionsNP[0].
fill(
"RECORD_DURATION",
"Duration (sec)",
"%.3f", 0.001, 999999.0, 0.0, 1.0);
125 RecordOptionsNP[1].
fill(
"RECORD_FRAME_TOTAL",
"Frames",
"%.f", 1.0, 999999999.0, 1.0, 30.0);
178 return d->initProperties();
205 d->ISGetProperties(dev);
256 return d->updateProperties();
300 size_t allocatedSize = nbytes *
framesIncoming.size() / 1024 / 1024;
303 LOG_WARN(
"Frame buffer is full, skipping frame...");
307 std::vector<uint8_t> copyBuffer(
buffer,
buffer + nbytes);
322 LOG_INFO(
"Waiting for all buffered frames to be recorded");
327 "Ending record after %g millisecs and %d frames",
345 d->newFrame(
buffer, nbytes);
354 uint8_t bytesPerComponent = (
PixelDepth + 7) / 8;
362 components * bytesPerComponent
369 components * bytesPerComponent
388 const uint8_t *srcBuffer,
395 uint32_t srcStride = srcFrameInfo.
lineSize();
398 srcBuffer += srcOffset;
403 memcpy(dstBuffer, srcBuffer, dstStride);
404 dstBuffer += dstStride;
405 srcBuffer += srcStride;
412 sourceTimeFrame.
time = 0;
414 std::vector<uint8_t> subframeBuffer;
415 std::vector<uint8_t> downscaleBuffer;
427 std::vector<uint8_t> *sourceBuffer = &sourceTimeFrame.
frame;
429 if (sourceBuffer->size() != srcFrameInfo.
totalSize())
431 LOG_ERROR(
"Invalid source buffer size, skipping frame...");
445 sourceBuffer = &subframeBuffer;
453 recordStream(sourceBuffer->data(), sourceBuffer->size(), sourceTimeFrame.
time) ==
false
473 reinterpret_cast<const uint16_t*
>(sourceBuffer->data()),
474 downscaleBuffer.size(),
475 downscaleBuffer.data()
478 sourceBuffer = &downscaleBuffer;
482 previewThreadPool.
tryStart(std::bind([
this, &previewElapsed](
const std::atomic_bool &isAboutToQuit, std::vector<uint8_t> frame){
484 previewElapsed.
start();
489 }, std::placeholders::_1, std::move(*sourceBuffer)));
499 LOG_WARN(
"Cannot subframe JPEG streams.");
535 std::lock_guard<std::mutex> lock(d->recordMutex);
536 return d->recorder->close();
545 if (recorderOK ==
false)
554 if (encoderOK ==
false)
571 return d->setPixelFormat(pixelFormat, pixelDepth);
578 d->setSize(width, height);
592 std::string result = fname;
594 std::time_t t = std::time(
nullptr);
595 std::tm tm = *std::gmtime(&t);
597 auto extendedPatterns = patterns;
598 extendedPatterns[
"_D_"] =
format_time(tm,
"%Y-%m-%d");
599 extendedPatterns[
"_H_"] =
format_time(tm,
"%H-%M-%S");
600 extendedPatterns[
"_T_"] =
format_time(tm,
"%Y-%m-%d" "@" "%H-%M-%S");
602 for(
const auto &pattern: extendedPatterns)
604 replace_all(result, pattern.first, pattern.second);
608 std::replace(result.begin(), result.end(),
':',
'-');
616 std::string filename, expfilename, expfiledir;
617 std::string filtername;
618 std::map<std::string, std::string> patterns;
631 patterns[
"_F_"] = filtername;
632 LOGF_DEBUG(
"Adding filter pattern %s", filtername.c_str());
641 if (expfiledir.at(expfiledir.size() - 1) !=
'/')
645 if (expfilename.size() < 4 || expfilename.substr(expfilename.size() - 4, 4) !=
recorder->
getExtension())
648 filename = expfiledir + expfilename;
651 LOGF_INFO(
"Record file is %s", filename.c_str());
653 if (
mkpath(expfiledir, 0755))
655 LOGF_WARN(
"Can not create record directory %s: %s", expfiledir.c_str(),
663 LOGF_WARN(
"Can not open record file: %s", errmsg);
672 LOG_INFO(
"Using direct recording (no software cropping).");
749 "Record Duration: %g millisec / %d frames",
766 for (
int i = 0; i < n; i++)
768 if (!strcmp(names[i],
"STREAM_ON") && states[i] ==
ISS_ON)
773 else if (!strcmp(names[i],
"STREAM_OFF") && states[i] ==
ISS_ON)
793 LOG_WARN(
"Recording device is busy.");
829 LOG_INFO(
"Recording stream has been disabled. Closing the stream...");
848 if (!strcmp(selectedEncoder, oneEncoder->getName()))
873 if (!strcmp(selectedRecorder, oneRecorder->getName()))
895 return d->ISNewSwitch(dev,
name, states, names, n);
907 if (strchr(tp->text,
'/'))
909 LOG_WARN(
"Dir. separator (/) not allowed in filename.");
925 return d->ISNewText(dev,
name, texts, names, n);
960 LOG_WARN(
"Recording device is busy");
975 LOG_WARN(
"Recording device is busy");
1012 return d->ISNewNumber(dev,
name, values, names, n);
1023 if (StreamOptionsN[OPTION_RATE_DIVISOR].value > 0)
1025 "Starting the video stream with target FPS %.f and rate divisor of %.f",
1026 StreamOptionsN[OPTION_TARGET_FPS].value, StreamOptionsN[OPTION_RATE_DIVISOR].value);
1028 LOGF_INFO(
"Starting the video stream with target FPS %.f", StreamOptionsN[OPTION_TARGET_FPS].value);
1030 LOGF_INFO(
"Starting the video stream with target exposure %.6f s (Max theoritical FPS %.f)",
1046 LOG_ERROR(
"Failed to start streaming.");
1058 LOG_ERROR(
"Failed to start streaming.");
1122 return d->setStream(enable);
1128 d->EncoderSP.save(fp);
1129 d->RecordFileTP.save(fp);
1130 d->RecordOptionsNP.save(fp);
1131 d->RecorderSP.save(fp);
1132 d->LimitsNP.save(fp);
1160 d->getStreamFrame(x, y, w, h);
1169 #ifdef HAVE_WEBSOCKET
1173 if (
Format !=
".streajpg")
1206 #ifdef HAVE_WEBSOCKET
1249 return d->direct_record;
1255 return d->isStreaming;
1261 return d->isRecording && !d->isRecordingAboutToClose;
1267 return (d->isStreaming || d->isRecording);
1273 return 1.0 / d->StreamExposureNP[0].getValue();
1278 return d->StreamExposureNP[0].getValue();
1284 d->hasStreamingExposure = enable;
bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
void newFrame(const uint8_t *buffer, uint32_t nbytes)
int64_t nsecsElapsed() const
Returns the number of nanoseconds since this ElapsedTimer was last started.
void setSize(uint16_t width, uint16_t height)
void setState(IPState state)
uint32_t frameCountDivider
virtual bool setFPS(float FPS)
bool setStream(bool enable)
setStream Enables (starts) or disables (stops) streaming.
virtual void setStreamEnabled(bool enable)=0
void setRecorder(RecorderInterface *recorder)
void newFrame(const uint8_t *buffer, uint32_t nbytes)
newFrame CCD drivers call this function when a new frame is received. It is then streamed,...
StreamManager(DefaultDevice *currentDevice)
bool isNameMatch(const char *otherName) const
#define LOGF_ERROR(fmt,...)
std::string recordfiledir
void setEncoder(EncoderInterface *encoder)
virtual bool updateProperties()
virtual bool upload(IBLOB *bp, const uint8_t *buffer, uint32_t nbytes, bool isCompressed=false)=0
void defineProperty(INumberVectorProperty *property)
EncoderManager encoderManager
INDI::PropertyView< IBLOB > * getBLOB(const char *name) const
INDI::PropertyNumber RecordOptionsNP
std::atomic< bool > isRecording
std::string format_time(const std::tm &tm, const char *format)
Converts the date and time to string - this function uses 'strftime'.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
INDI::PropertyText RecordFileTP
std::string recordfilename
RecorderInterface * getRecorder() const
The SensorDevice class provides functionality of a Sensor Device within a Sensor.
std::atomic< bool > isStreaming
const char * getDeviceName() const
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
double framesPerSecond() const
Number of frames per second counted in the time window.
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool setStream(bool enable)
void setStreamingExposureEnabled(bool enable)
setStreamingExposureEnabled Can stream exposure time be changed?
virtual const char * getName()
virtual bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
void setTimeWindow(double timeWindow)
Time window setup.
virtual void ISGetProperties(const char *dev)
void getStreamFrame(uint16_t *x, uint16_t *y, uint16_t *w, uint16_t *h) const
void ISGetProperties(const char *dev)
static std::string expand(const std::string &fname, const std::map< std::string, std::string > &patterns)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
UniqueQueue< TimeFrame > framesIncoming
Class to provide general functionality of CCD cameras with a single CCD sensor, or a primary CCD sens...
int mkpath(std::string s, mode_t mode)
Create a path directory - this function uses 'mkdir'.
virtual bool saveConfigItems(FILE *fp)
bool update(const ISState states[], const char *const names[], int n)
virtual bool initProperties()
bool tryStart(const std::function< void(const std::atomic_bool &isAboutToClose)> &functionToRun)
If thread isn't available at the time of calling, then this function does nothing and returns false....
#define LOGF_DEBUG(fmt,...)
virtual bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth=8)=0
virtual bool open(const char *filename, char *errmsg)=0
RecorderManager recorderManager
bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth=8)
const char * getName() const
std::vector< EncoderInterface * > getEncoderList()
INDI::PropertySwitch RecordStreamSP
INDI::PropertySwitch RecorderSP
INDI_PIXEL_FORMAT PixelFormat
virtual const char * getExtension()=0
EncoderInterface * encoder
uint64_t totalFrames() const
Total frames.
DefaultDevice * currentDevice
virtual uint16_t getDriverInterface() override
void reset()
Reset all frame information.
#define LOGF_WARN(fmt,...)
virtual ~StreamManagerPrivate()
void asyncStreamThread()
Thread processing frames and forwarding to recording and preview.
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
The ElapsedTimer class provides a fast way to calculate elapsed times.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
INDI::PropertyNumber StreamFrameNP
INDI::PropertySwitch StreamSP
INDI::PropertyNumber StreamExposureNP
EncoderInterface * getDefaultEncoder()
double deltaTime() const
Time in milliseconds between last frames.
double getTargetFPS() const
std::vector< RecorderInterface * > getRecorderList()
void getStreamFrame(uint16_t *x, uint16_t *y, uint16_t *w, uint16_t *h) const
INDI::PropertyNumber FpsNP
WidgetView< T > * findWidgetByName(const char *name) const
virtual void init(INDI::DefaultDevice *mainDevice)
void setStreamFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
bool update(const double values[], const char *const names[], int n)
bool update(const char *const texts[], const char *const names[], int n)
virtual bool writeFrame(const uint8_t *frame, uint32_t nbytes)=0
std::vector< uint8_t > frame
#define LOGF_INFO(fmt,...)
RecorderInterface * getDefaultRecorder()
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
void apply(const uint16_t *source, size_t count, uint8_t *destination) const
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
The EncoderInterface class is the base class for video streaming encoders.
RecorderInterface * recorder
static void subframe(const uint8_t *srcBuffer, const FrameInfo &srcFrameInfo, uint8_t *dstBuffer, const FrameInfo &dstFrameInfo)
INDI::PropertyView< IBLOB > * imageBP
void replace_all(std::string &subject, const std::string &search, const std::string &replace)
Replaces every occurrence of the string 'search' with the string 'replace'.
bool isDirectRecording() const
Namespace to encapsulate INDI client, drivers, and mediator classes.
#define DEBUGF(priority, msg,...)
double getTargetExposure() const
bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
bool hasStreamingExposure
void setState(IPState state)
int findOnSwitchIndex() const
bool newFrame()
When you get a frame, call the function to count.
INDI::PropertyNumber LimitsNP
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.
double totalTime() const
Total time.
const char * getDeviceName() const
const char * getDeviceName() const
StreamManagerPrivate(DefaultDevice *defaultDevice)
bool uploadStream(const uint8_t *buffer, uint32_t nbytes)
uploadStream Upload frame to client using the selected encoder
bool stopRecording(bool force=false)
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
INDI::PropertySwitch EncoderSP
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
INDI::PropertyNumber StreamTimeNP
FrameInfo updateSourceFrameInfo()
bool recordStream(const uint8_t *buffer, uint32_t nbytes, double deltams)
recordStream Calls the backend recorder to record a single frame.
std::atomic< bool > framesThreadTerminate
std::vector< uint8_t > buffer
WidgetType * at(size_t index) const
void start()
Starts this timer. Once started, a timer value can be checked with elapsed().
void setSize(uint16_t width, uint16_t height=1)
INDI::WidgetView< ISwitch > * findOnSwitch() const
std::atomic< bool > isRecordingAboutToClose
bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)