Instrument Neutral Distributed Interface INDI  1.9.2
streammanager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2020 by Pawel Soja <kernel32.pl@gmail.com>
3  Copyright (C) 2015 by Jasem Mutlaq <mutlaqja@ikarustech.com>
4  Copyright (C) 2014 by geehalel <geehalel@gmail.com>
5 
6  Stream Recorder
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Lesser General Public
10  License as published by the Free Software Foundation; either
11  version 2.1 of the License, or (at your option) any later version.
12 
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  Lesser General Public License for more details.
17 
18  You should have received a copy of the GNU Lesser General Public
19  License along with this library; if not, write to the Free Software
20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 
22 */
23 
24 #include <config.h>
25 #include "streammanager.h"
26 #include "streammanager_p.h"
27 #include "indiccd.h"
28 #include "indisensorinterface.h"
29 #include "indilogger.h"
30 #include "indiutility.h"
31 #include "indisinglethreadpool.h"
32 #include "indielapsedtimer.h"
33 
34 #include <cerrno>
35 #include <sys/stat.h>
36 
37 #include <algorithm>
38 
39 static const char * STREAM_TAB = "Streaming";
40 
41 namespace INDI
42 {
43 
45  : currentDevice(defaultDevice)
46 {
49 
51 
52  LOGF_DEBUG("Using default recorder (%s)", recorder->getName());
53 
55 
57 
58  LOGF_DEBUG("Using default encoder (%s)", encoder->getName());
59 
61 }
62 
64 {
65  if (framesThread.joinable())
66  {
67  framesThreadTerminate = true;
68  framesIncoming.abort();
69  framesThread.join();
70  }
71 }
72 
74  : d_ptr(new StreamManagerPrivate(mainDevice))
75 { }
76 
78 { }
79 
81 {
82  return currentDevice->getDeviceName();
83 }
84 
85 const char * StreamManager::getDeviceName() const
86 {
87  D_PTR(const StreamManager);
88  return d->getDeviceName();
89 }
90 
92 {
93  /* Video Stream */
94  StreamSP[0].fill("STREAM_ON", "Stream On", ISS_OFF);
95  StreamSP[1].fill("STREAM_OFF", "Stream Off", ISS_ON);
97  StreamSP.fill(getDeviceName(), "SENSOR_DATA_STREAM", "Video Stream",
98  STREAM_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
99  else
100  StreamSP.fill(getDeviceName(), "CCD_VIDEO_STREAM", "Video Stream",
101  STREAM_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
102 
103  StreamTimeNP[0].fill("STREAM_DELAY_TIME", "Delay (s)", "%.3f", 0, 60, 0.001, 0);
104  StreamTimeNP.fill(getDeviceName(), "STREAM_DELAY", "Video Stream Delay", STREAM_TAB, IP_RO, 0, IPS_IDLE);
105 
106  StreamExposureNP[STREAM_EXPOSURE].fill("STREAMING_EXPOSURE_VALUE", "Duration (s)", "%.6f", 0.000001, 60, 0.1, 0.1);
107  StreamExposureNP[STREAM_DIVISOR ].fill("STREAMING_DIVISOR_VALUE", "Divisor", "%.f", 1, 15, 1.0, 1.0);
108  StreamExposureNP.fill(getDeviceName(), "STREAMING_EXPOSURE", "Expose", STREAM_TAB, IP_RW, 60, IPS_IDLE);
109 
110  /* Measured FPS */
111  FpsNP[FPS_INSTANT].fill("EST_FPS", "Instant.", "%.2f", 0.0, 999.0, 0.0, 30);
112  FpsNP[FPS_AVERAGE].fill("AVG_FPS", "Average (1 sec.)", "%.2f", 0.0, 999.0, 0.0, 30);
113  FpsNP.fill(getDeviceName(), "FPS", "FPS", STREAM_TAB, IP_RO, 60, IPS_IDLE);
114 
115  /* Record Frames */
116  /* File */
117  std::string defaultDirectory = std::string(getenv("HOME")) + std::string("/indi__D_");
118  RecordFileTP[0].fill("RECORD_FILE_DIR", "Dir.", defaultDirectory.data());
119  RecordFileTP[1].fill("RECORD_FILE_NAME", "Name", "indi_record__T_");
120  RecordFileTP.fill(getDeviceName(), "RECORD_FILE", "Record File",
121  STREAM_TAB, IP_RW, 0, IPS_IDLE);
122 
123  /* Record Options */
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);
126  RecordOptionsNP.fill(getDeviceName(), "RECORD_OPTIONS",
127  "Record Options", STREAM_TAB, IP_RW, 60, IPS_IDLE);
128 
129  /* Record Switch */
130  RecordStreamSP[RECORD_ON ].fill("RECORD_ON", "Record On", ISS_OFF);
131  RecordStreamSP[RECORD_TIME ].fill("RECORD_DURATION_ON", "Record (Duration)", ISS_OFF);
132  RecordStreamSP[RECORD_FRAME].fill("RECORD_FRAME_ON", "Record (Frames)", ISS_OFF);
133  RecordStreamSP[RECORD_OFF ].fill("RECORD_OFF", "Record Off", ISS_ON);
134  RecordStreamSP.fill(getDeviceName(), "RECORD_STREAM", "Video Record", STREAM_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
135 
137  {
138  // CCD Streaming Frame
139  StreamFrameNP[0].fill("X", "Left", "%.f", 0, 0, 0, 0);
140  StreamFrameNP[1].fill("Y", "Top", "%.f", 0, 0, 0, 0);
141  StreamFrameNP[2].fill("WIDTH", "Width", "%.f", 0, 0, 0, 0);
142  StreamFrameNP[3].fill("HEIGHT", "Height", "%.f", 0, 0, 0, 0);
143  StreamFrameNP.fill(getDeviceName(), "CCD_STREAM_FRAME", "Frame", STREAM_TAB, IP_RW,
144  60, IPS_IDLE);
145  }
146 
147  // Encoder Selection
148  EncoderSP[ENCODER_RAW ].fill("RAW", "RAW", ISS_ON);
149  EncoderSP[ENCODER_MJPEG].fill("MJPEG", "MJPEG", ISS_OFF);
151  EncoderSP.fill(getDeviceName(), "SENSOR_STREAM_ENCODER", "Encoder", STREAM_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
152  else
153  EncoderSP.fill(getDeviceName(), "CCD_STREAM_ENCODER", "Encoder", STREAM_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
154 
155  // Recorder Selector
156  RecorderSP[RECORDER_RAW].fill("SER", "SER", ISS_ON);
157  RecorderSP[RECORDER_OGV].fill("OGV", "OGV", ISS_OFF);
159  RecorderSP.fill(getDeviceName(), "SENSOR_STREAM_RECORDER", "Recorder", STREAM_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
160  else
161  RecorderSP.fill(getDeviceName(), "CCD_STREAM_RECORDER", "Recorder", STREAM_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
162 
163  // If we do not have theora installed, let's just define SER default recorder
164 #ifndef HAVE_THEORA
165  RecorderSP.resize(1);
166 #endif
167 
168  // Limits
169  LimitsNP[LIMITS_BUFFER_MAX ].fill("LIMITS_BUFFER_MAX", "Maximum Buffer Size (MB)", "%.0f", 1, 1024*64, 1, 512);
170  LimitsNP[LIMITS_PREVIEW_FPS].fill("LIMITS_PREVIEW_FPS", "Maximum Preview FPS", "%.0f", 1, 120, 1, 10);
171  LimitsNP.fill(getDeviceName(), "LIMITS", "Limits", STREAM_TAB, IP_RW, 0, IPS_IDLE);
172  return true;
173 }
174 
176 {
177  D_PTR(StreamManager);
178  return d->initProperties();
179 }
180 
182 {
183  if (dev != nullptr && strcmp(getDeviceName(), dev))
184  return;
185 
186  if (currentDevice->isConnected())
187  {
199  }
200 }
201 
202 void StreamManager::ISGetProperties(const char * dev)
203 {
204  D_PTR(StreamManager);
205  d->ISGetProperties(dev);
206 }
207 
209 {
210  if (currentDevice->isConnected())
211  {
213  {
214  imageBP = currentDevice->getBLOB("CCD1");
215  }
217  {
218  imageBP = currentDevice->getBLOB("SENSOR");
219  }
220 
233  }
234  else
235  {
248  }
249 
250  return true;
251 }
252 
254 {
255  D_PTR(StreamManager);
256  return d->updateProperties();
257 }
258 
259 /*
260  * The camera driver is expected to send the FULL FRAME of the Camera after BINNING without any subframing at all
261  * Subframing for streaming/recording is done in the stream manager.
262  * Therefore nbytes is expected to be SubW/BinX * SubH/BinY * Bytes_Per_Pixels * Number_Color_Components
263  * Binned frame must be sent from the camera driver for this to work consistentaly for all drivers.*/
264 void StreamManagerPrivate::newFrame(const uint8_t * buffer, uint32_t nbytes)
265 {
266  // close the data stream on the same thread as the data stream
267  // manually triggered to stop recording.
269  {
270  stopRecording();
271  return;
272  }
273 
274  // Discard every N frame.
275  // do not count it to fps statistics
276  // N is StreamExposureNP[STREAM_DIVISOR].getValue()
278  if (
279  (StreamExposureNP[STREAM_DIVISOR].value > 1) &&
280  (frameCountDivider % static_cast<int>(StreamExposureNP[STREAM_DIVISOR].value)) == 0
281  )
282  {
283  return;
284  }
285 
286  if (FPSAverage.newFrame())
287  {
288  FpsNP[1].setValue(FPSAverage.framesPerSecond());
289  }
290 
291  if (FPSFast.newFrame())
292  {
293  FpsNP[0].setValue(FPSFast.framesPerSecond());
294  if (fastFPSUpdate.try_lock()) // don't block stream thread / record thread
295  std::thread([&](){ FpsNP.apply(); fastFPSUpdate.unlock(); }).detach();
296  }
297 
299  {
300  size_t allocatedSize = nbytes * framesIncoming.size() / 1024 / 1024; // allocated size in MB
301  if (allocatedSize > LimitsNP[LIMITS_BUFFER_MAX].getValue())
302  {
303  LOG_WARN("Frame buffer is full, skipping frame...");
304  return;
305  }
306 
307  std::vector<uint8_t> copyBuffer(buffer, buffer + nbytes); // copy the frame
308 
309  framesIncoming.push(TimeFrame{FPSFast.deltaTime(), std::move(copyBuffer)}); // push it into the queue
310  }
311 
313  {
314  FPSRecorder.newFrame(); // count frames and total time
315 
316  // captured all frames, stream should be close
317  if (
318  (RecordStreamSP[RECORD_FRAME].getState() == ISS_ON && FPSRecorder.totalFrames() >= (RecordOptionsNP[1].value)) ||
319  (RecordStreamSP[RECORD_TIME ].getState() == ISS_ON && FPSRecorder.totalTime() >= (RecordOptionsNP[0].value * 1000.0))
320  )
321  {
322  LOG_INFO("Waiting for all buffered frames to be recorded");
323  framesIncoming.waitForEmpty();
324  // duplicated message
325 #if 0
326  LOGF_INFO(
327  "Ending record after %g millisecs and %d frames",
330  );
331 #endif
336 
337  stopRecording();
338  }
339  }
340 }
341 
342 void StreamManager::newFrame(const uint8_t * buffer, uint32_t nbytes)
343 {
344  D_PTR(StreamManager);
345  d->newFrame(buffer, nbytes);
346 }
347 
348 
350 {
351  FrameInfo srcFrameInfo;
352 
353  uint8_t components = (PixelFormat == INDI_RGB) ? 3 : 1;
354  uint8_t bytesPerComponent = (PixelDepth + 7) / 8;
355 
356  dstFrameInfo.bytesPerColor = components * bytesPerComponent;
357 
359  {
360  srcFrameInfo = FrameInfo(
361  dynamic_cast<const INDI::CCD*>(currentDevice)->PrimaryCCD,
362  components * bytesPerComponent
363  );
364  }
366  {
367  srcFrameInfo = FrameInfo(
368  *dynamic_cast<const INDI::SensorInterface*>(currentDevice),
369  components * bytesPerComponent
370  );
371  }
372 
373  // If stream frame was not yet initilized, let's do that now
374  if (dstFrameInfo.pixels() == 0)
375  {
376  //if (dynamic_cast<INDI::CCD*>(currentDevice)->PrimaryCCD.getNAxis() == 2)
377  // binFactor = dynamic_cast<INDI::CCD*>(currentDevice)->PrimaryCCD.getBinX();
378  dstFrameInfo = srcFrameInfo;
382  }
383 
384  return srcFrameInfo;
385 }
386 
388  const uint8_t *srcBuffer,
389  const FrameInfo &srcFrameInfo,
390  uint8_t *dstBuffer,
391  const FrameInfo &dstFrameInfo
392 )
393 {
394  size_t srcOffset = srcFrameInfo.bytesPerColor * (dstFrameInfo.y * srcFrameInfo.w + dstFrameInfo.x);
395  uint32_t srcStride = srcFrameInfo.lineSize();
396  uint32_t dstStride = dstFrameInfo.lineSize();
397 
398  srcBuffer += srcOffset;
399 
400  // Copy line-by-line
401  for (size_t i = 0; i < dstFrameInfo.h; ++i)
402  {
403  memcpy(dstBuffer, srcBuffer, dstStride);
404  dstBuffer += dstStride;
405  srcBuffer += srcStride;
406  }
407 }
408 
410 {
411  TimeFrame sourceTimeFrame;
412  sourceTimeFrame.time = 0;
413 
414  std::vector<uint8_t> subframeBuffer; // Subframe buffer for recording/streaming
415  std::vector<uint8_t> downscaleBuffer; // Downscale buffer for streaming
416 
417  INDI::SingleThreadPool previewThreadPool;
418  INDI::ElapsedTimer previewElapsed;
419 
420  while(!framesThreadTerminate)
421  {
422  if (framesIncoming.pop(sourceTimeFrame) == false)
423  continue;
424 
425  FrameInfo srcFrameInfo = updateSourceFrameInfo();
426 
427  std::vector<uint8_t> *sourceBuffer = &sourceTimeFrame.frame;
428 
429  if (sourceBuffer->size() != srcFrameInfo.totalSize())
430  {
431  LOG_ERROR("Invalid source buffer size, skipping frame...");
432  continue;
433  }
434 
435  // Check if we need to subframe
436  if (
437  PixelFormat != INDI_JPG &&
438  dstFrameInfo.pixels() != 0 &&
439  dstFrameInfo != srcFrameInfo
440  )
441  {
442  subframeBuffer.resize(dstFrameInfo.totalSize());
443  subframe(sourceBuffer->data(), srcFrameInfo, subframeBuffer.data(), dstFrameInfo);
444 
445  sourceBuffer = &subframeBuffer;
446  }
447 
448  // For recording, save immediately.
449  {
450  std::lock_guard<std::mutex> lock(recordMutex);
451  if (
453  recordStream(sourceBuffer->data(), sourceBuffer->size(), sourceTimeFrame.time) == false
454  )
455  {
456  LOG_ERROR("Recording failed.");
458  }
459  }
460 
461  // For streaming, downscale to 8bit if higher than 8bit to reduce bandwidth
462  // You can reduce the number of frames by setting a frame limit.
464  {
465  // Downscale to 8bit always for streaming to reduce bandwidth
466  if (PixelFormat != INDI_JPG && PixelDepth > 8)
467  {
468  // Allocale new buffer if size changes
469  downscaleBuffer.resize(dstFrameInfo.pixels());
470 
471  // Apply gamma
473  reinterpret_cast<const uint16_t*>(sourceBuffer->data()),
474  downscaleBuffer.size(),
475  downscaleBuffer.data()
476  );
477 
478  sourceBuffer = &downscaleBuffer;
479  }
480 
481  //uploadStream(sourceBuffer->data(), sourceBuffer->size());
482  previewThreadPool.tryStart(std::bind([this, &previewElapsed](const std::atomic_bool &isAboutToQuit, std::vector<uint8_t> frame){
483  INDI_UNUSED(isAboutToQuit);
484  previewElapsed.start();
485  uploadStream(frame.data(), frame.size());
486  StreamTimeNP[0].setValue(previewElapsed.nsecsElapsed() / 1000000000.0);
488 
489  }, std::placeholders::_1, std::move(*sourceBuffer)));
490  }
491  }
492 }
493 
494 void StreamManagerPrivate::setSize(uint16_t width, uint16_t height)
495 {
496  if (width != StreamFrameNP[CCDChip::FRAME_W].value || height != StreamFrameNP[CCDChip::FRAME_H].getValue())
497  {
498  if (PixelFormat == INDI_JPG)
499  LOG_WARN("Cannot subframe JPEG streams.");
500 
501  StreamFrameNP[CCDChip::FRAME_X].setValue(0);
502  StreamFrameNP[CCDChip::FRAME_X].setMax(width - 1);
503  StreamFrameNP[CCDChip::FRAME_Y].setValue(0);
504  StreamFrameNP[CCDChip::FRAME_Y].setMax(height - 1);
505  StreamFrameNP[CCDChip::FRAME_W].setValue(width);
506  StreamFrameNP[CCDChip::FRAME_W].setMin(10);
507  StreamFrameNP[CCDChip::FRAME_W].setMax(width);
508  StreamFrameNP[CCDChip::FRAME_H].setValue(height);
509  StreamFrameNP[CCDChip::FRAME_H].setMin(10);
510  StreamFrameNP[CCDChip::FRAME_H].setMax(height);
511 
514  }
515 
520 
521  // Width & Height are BINNED dimensions.
522  // Since they're the final size to make it to encoders and recorders.
523  rawWidth = width;
524  rawHeight = height;
525 
526  for (EncoderInterface * oneEncoder : encoderManager.getEncoderList())
527  oneEncoder->setSize(rawWidth, rawHeight);
528  for (RecorderInterface * oneRecorder : recorderManager.getRecorderList())
529  oneRecorder->setSize(rawWidth, rawHeight);
530 }
531 
533 {
534  D_PTR(StreamManager);
535  std::lock_guard<std::mutex> lock(d->recordMutex);
536  return d->recorder->close();
537 }
538 
539 bool StreamManagerPrivate::setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
540 {
541  if (pixelFormat == PixelFormat && pixelDepth == PixelDepth)
542  return true;
543 
544  bool recorderOK = recorder->setPixelFormat(pixelFormat, pixelDepth);
545  if (recorderOK == false)
546  {
547  LOGF_ERROR("Pixel format %d is not supported by %s recorder.", pixelFormat, recorder->getName());
548  }
549  else
550  {
551  LOGF_DEBUG("Pixel format %d is supported by %s recorder.", pixelFormat, recorder->getName());
552  }
553  bool encoderOK = encoder->setPixelFormat(pixelFormat, pixelDepth);
554  if (encoderOK == false)
555  {
556  LOGF_ERROR("Pixel format %d is not supported by %s encoder.", pixelFormat, encoder->getName());
557  }
558  else
559  {
560  LOGF_DEBUG("Pixel format %d is supported by %s encoder.", pixelFormat, encoder->getName());
561  }
562 
563  PixelFormat = pixelFormat;
564  PixelDepth = pixelDepth;
565  return true;
566 }
567 
568 bool StreamManager::setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
569 {
570  D_PTR(StreamManager);
571  return d->setPixelFormat(pixelFormat, pixelDepth);
572 }
573 
574 
575 void StreamManager::setSize(uint16_t width, uint16_t height)
576 {
577  D_PTR(StreamManager);
578  d->setSize(width, height);
579 }
580 
581 bool StreamManagerPrivate::recordStream(const uint8_t * buffer, uint32_t nbytes, double deltams)
582 {
583  INDI_UNUSED(deltams);
584  if (!isRecording)
585  return false;
586 
587  return recorder->writeFrame(buffer, nbytes);
588 }
589 
590 std::string StreamManagerPrivate::expand(const std::string &fname, const std::map<std::string, std::string> &patterns)
591 {
592  std::string result = fname;
593 
594  std::time_t t = std::time(nullptr);
595  std::tm tm = *std::gmtime(&t);
596 
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");
601 
602  for(const auto &pattern: extendedPatterns)
603  {
604  replace_all(result, pattern.first, pattern.second);
605  }
606 
607  // Replace all : to - to be valid filename on Windows
608  std::replace(result.begin(), result.end(), ':', '-'); // it's really needed now?
609 
610  return result;
611 }
612 
614 {
615  char errmsg[MAXRBUF];
616  std::string filename, expfilename, expfiledir;
617  std::string filtername;
618  std::map<std::string, std::string> patterns;
619  if (isRecording)
620  return true;
621 
623  {
624  /* get filter name for pattern substitution */
625  if (dynamic_cast<INDI::CCD*>(currentDevice)->CurrentFilterSlot != -1
626  && dynamic_cast<INDI::CCD*>(currentDevice)->CurrentFilterSlot <= static_cast<int>(dynamic_cast<INDI::CCD*>
627  (currentDevice)->FilterNames.size()))
628  {
629  filtername = dynamic_cast<INDI::CCD*>(currentDevice)->FilterNames.at(dynamic_cast<INDI::CCD*>
631  patterns["_F_"] = filtername;
632  LOGF_DEBUG("Adding filter pattern %s", filtername.c_str());
633  }
634  }
635 
636  recorder->setFPS(FpsNP[FPS_AVERAGE].value);
637 
638  /* pattern substitution */
639  recordfiledir.assign(RecordFileTP[0].text);
640  expfiledir = expand(recordfiledir, patterns);
641  if (expfiledir.at(expfiledir.size() - 1) != '/')
642  expfiledir += '/';
643  recordfilename.assign(RecordFileTP[1].text);
644  expfilename = expand(recordfilename, patterns);
645  if (expfilename.size() < 4 || expfilename.substr(expfilename.size() - 4, 4) != recorder->getExtension())
646  expfilename += recorder->getExtension();
647 
648  filename = expfiledir + expfilename;
649  //LOGF_INFO("Expanded file is %s", filename.c_str());
650  //filename=recordfiledir+recordfilename;
651  LOGF_INFO("Record file is %s", filename.c_str());
652  /* Create/open file/dir */
653  if (mkpath(expfiledir, 0755))
654  {
655  LOGF_WARN("Can not create record directory %s: %s", expfiledir.c_str(),
656  strerror(errno));
657  return false;
658  }
659  if (!recorder->open(filename.c_str(), errmsg))
660  {
663  LOGF_WARN("Can not open record file: %s", errmsg);
664  return false;
665  }
666 
667 #if 0
668  /* start capture */
669  // TODO direct recording should this be part of StreamManager?
670  if (direct_record)
671  {
672  LOG_INFO("Using direct recording (no software cropping).");
673  //v4l_base->doDecode(false);
674  //v4l_base->doRecord(true);
675  }
676  else
677  {
678  //if (ImageColorS[IMAGE_GRAYSCALE].s == ISS_ON)
679  if (dynamic_cast<INDI::CCD*>(currentDevice)->PrimaryCCD.getNAxis() == 2)
680  recorder->setDefaultMono();
681  else
682  recorder->setDefaultColor();
683  }
684 #endif
685  FPSRecorder.reset();
686  frameCountDivider = 0;
687 
688  if (isStreaming == false)
689  {
690  FPSAverage.reset();
691  FPSFast.reset();
692  }
693 
695  {
696  if (isStreaming == false && dynamic_cast<INDI::CCD*>(currentDevice)->StartStreaming() == false)
697  {
698  LOG_ERROR("Failed to start recording.");
703  }
704  }
706  {
707  if (isStreaming == false && dynamic_cast<INDI::SensorInterface*>(currentDevice)->StartStreaming() == false)
708  {
709  LOG_ERROR("Failed to start recording.");
714  }
715  }
716  isRecording = true;
717  return true;
718 }
719 
721 {
722  if (!isRecording && force == false)
723  return true;
724 
726  {
727  if (!isStreaming)
728  dynamic_cast<INDI::CCD*>(currentDevice)->StopStreaming();
729  }
731  {
732  if (!isStreaming)
733  dynamic_cast<INDI::SensorInterface*>(currentDevice)->StopStreaming();
734 
735  }
736 
737  isRecording = false;
738  isRecordingAboutToClose = false;
739 
740  {
741  std::lock_guard<std::mutex> lock(recordMutex);
742  recorder->close();
743  }
744 
745  if (force)
746  return false;
747 
748  LOGF_INFO(
749  "Record Duration: %g millisec / %d frames",
752  );
753 
754  return true;
755 }
756 
757 bool StreamManagerPrivate::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
758 {
759  /* ignore if not ours */
760  if (dev != nullptr && strcmp(getDeviceName(), dev))
761  return false;
762 
763  /* Video Stream */
764  if (StreamSP.isNameMatch(name))
765  {
766  for (int i = 0; i < n; i++)
767  {
768  if (!strcmp(names[i], "STREAM_ON") && states[i] == ISS_ON)
769  {
770  setStream(true);
771  break;
772  }
773  else if (!strcmp(names[i], "STREAM_OFF") && states[i] == ISS_ON)
774  {
775  setStream(false);
776  break;
777  }
778  }
779  return true;
780  }
781 
782  // Record Stream
783  if (RecordStreamSP.isNameMatch(name))
784  {
785  int prevSwitch = RecordStreamSP.findOnSwitchIndex();
786  RecordStreamSP.update(states, names, n);
787 
788  if (isRecording && RecordStreamSP[RECORD_OFF].getState() != ISS_ON)
789  {
791  RecordStreamSP[prevSwitch].setState(ISS_ON);
793  LOG_WARN("Recording device is busy.");
794  return true;
795  }
796 
797  if (
798  RecordStreamSP[RECORD_ON ].getState() == ISS_ON ||
799  RecordStreamSP[RECORD_TIME ].getState() == ISS_ON ||
800  RecordStreamSP[RECORD_FRAME].getState() == ISS_ON
801  )
802  {
803  if (!isRecording)
804  {
807  LOGF_INFO("Starting video record (Duration): %g secs.", RecordOptionsNP[0].getValue());
808  else if (RecordStreamSP[RECORD_FRAME].s == ISS_ON)
809  LOGF_INFO("Starting video record (Frame count): %d.", static_cast<int>(RecordOptionsNP[1].value));
810  else
811  LOG_INFO("Starting video record.");
812 
813  if (!startRecording())
814  {
818  }
819  }
820  }
821  else
822  {
824  Format.clear();
825  FpsNP[FPS_INSTANT].setValue(0);
826  FpsNP[FPS_AVERAGE].setValue(0);
827  if (isRecording)
828  {
829  LOG_INFO("Recording stream has been disabled. Closing the stream...");
831  }
832  }
833 
835  return true;
836  }
837 
838  // Encoder Selection
839  if (EncoderSP.isNameMatch(name))
840  {
841  EncoderSP.update(states, names, n);
843 
844  const char * selectedEncoder = EncoderSP.findOnSwitch()->name;
845 
846  for (EncoderInterface * oneEncoder : encoderManager.getEncoderList())
847  {
848  if (!strcmp(selectedEncoder, oneEncoder->getName()))
849  {
850  encoderManager.setEncoder(oneEncoder);
851 
852  oneEncoder->setPixelFormat(PixelFormat, PixelDepth);
853 
854  encoder = oneEncoder;
855 
857  }
858  }
859  EncoderSP.apply();
860  return true;
861  }
862 
863  // Recorder Selection
864  if (RecorderSP.isNameMatch(name))
865  {
866  RecorderSP.update(states, names, n);
868 
869  const char * selectedRecorder = RecorderSP.findOnSwitch()->name;
870 
871  for (RecorderInterface * oneRecorder : recorderManager.getRecorderList())
872  {
873  if (!strcmp(selectedRecorder, oneRecorder->getName()))
874  {
875  recorderManager.setRecorder(oneRecorder);
876 
877  oneRecorder->setPixelFormat(PixelFormat, PixelDepth);
878 
879  recorder = oneRecorder;
880 
882  }
883  }
884  RecorderSP.apply();
885  return true;
886  }
887 
888  // No properties were processed
889  return false;
890 }
891 
892 bool StreamManager::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
893 {
894  D_PTR(StreamManager);
895  return d->ISNewSwitch(dev, name, states, names, n);
896 }
897 
898 bool StreamManagerPrivate::ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n)
899 {
900  /* ignore if not ours */
901  if (dev != nullptr && strcmp(getDeviceName(), dev))
902  return false;
903 
904  if (RecordFileTP.isNameMatch(name))
905  {
906  IText * tp = RecordFileTP.findWidgetByName("RECORD_FILE_NAME");
907  if (strchr(tp->text, '/'))
908  {
909  LOG_WARN("Dir. separator (/) not allowed in filename.");
910  return true;
911  }
912 
913  RecordFileTP.update(texts, names, n);
915  return true;
916  }
917 
918  // No Properties were processed.
919  return false;
920 }
921 
922 bool StreamManager::ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n)
923 {
924  D_PTR(StreamManager);
925  return d->ISNewText(dev, name, texts, names, n);
926 }
927 
928 bool StreamManagerPrivate::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
929 {
930  /* ignore if not ours */
931  if (dev != nullptr && strcmp(getDeviceName(), dev))
932  return false;
933 
934  if (StreamExposureNP.isNameMatch(name))
935  {
936  StreamExposureNP.update(values, names, n);
939  return true;
940  }
941 
942  /* Limits */
943  if (LimitsNP.isNameMatch(name))
944  {
945  LimitsNP.update(values, names, n);
946 
947  FPSPreview.setTimeWindow(1000.0 / LimitsNP[LIMITS_PREVIEW_FPS].getValue());
948  FPSPreview.reset();
949 
951  LimitsNP.apply();
952  return true;
953  }
954 
955  /* Record Options */
956  if (RecordOptionsNP.isNameMatch(name))
957  {
958  if (isRecording)
959  {
960  LOG_WARN("Recording device is busy");
961  return true;
962  }
963 
964  RecordOptionsNP.update(values, names, n);
967  return true;
968  }
969 
970  /* Stream Frame */
971  if (StreamFrameNP.isNameMatch(name))
972  {
973  if (isRecording)
974  {
975  LOG_WARN("Recording device is busy");
976  return true;
977  }
978 
979  FrameInfo srcFrameInfo;
980 
982  {
983  srcFrameInfo = FrameInfo(dynamic_cast<const INDI::CCD*>(currentDevice)->PrimaryCCD);
984  }
986  {
987  srcFrameInfo = FrameInfo(*dynamic_cast<const INDI::SensorInterface*>(currentDevice));
988  }
989 
990  StreamFrameNP.update(values, names, n);
992 
993  double subW = srcFrameInfo.w - StreamFrameNP[CCDChip::FRAME_X].getValue();
994  double subH = srcFrameInfo.h - StreamFrameNP[CCDChip::FRAME_Y].getValue();
995 
998 
1000 
1001  StreamFrameNP.apply();
1002  return true;
1003  }
1004 
1005  // No properties were processed
1006  return false;
1007 }
1008 
1009 bool StreamManager::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
1010 {
1011  D_PTR(StreamManager);
1012  return d->ISNewNumber(dev, name, values, names, n);
1013 }
1014 
1016 {
1017  if (enable)
1018  {
1019  if (!isStreaming)
1020  {
1022 #if 0
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);
1027  else
1028  LOGF_INFO("Starting the video stream with target FPS %.f", StreamOptionsN[OPTION_TARGET_FPS].value);
1029 #endif
1030  LOGF_INFO("Starting the video stream with target exposure %.6f s (Max theoritical FPS %.f)",
1031  StreamExposureNP[0].getValue(), 1 / StreamExposureNP[0].getValue());
1032 
1033  FPSAverage.reset();
1034  FPSFast.reset();
1035  FPSPreview.reset();
1036  FPSPreview.setTimeWindow(1000.0 / LimitsNP[LIMITS_PREVIEW_FPS].getValue());
1037  frameCountDivider = 0;
1038 
1040  {
1041  if (dynamic_cast<INDI::CCD*>(currentDevice)->StartStreaming() == false)
1042  {
1043  StreamSP.reset();
1044  StreamSP[1].setState(ISS_ON);
1046  LOG_ERROR("Failed to start streaming.");
1047  StreamSP.apply();
1048  return false;
1049  }
1050  }
1052  {
1053  if (dynamic_cast<INDI::SensorInterface*>(currentDevice)->StartStreaming() == false)
1054  {
1055  StreamSP.reset();
1056  StreamSP[1].setState(ISS_ON);
1058  LOG_ERROR("Failed to start streaming.");
1059  StreamSP.apply();
1060  return false;
1061  }
1062  }
1063  isStreaming = true;
1064  Format.clear();
1065  FpsNP[FPS_INSTANT].setValue(0);
1066  FpsNP[FPS_AVERAGE].setValue(0);
1067  StreamSP.reset();
1068  StreamSP[0].setState(ISS_ON);
1069  recorder->setStreamEnabled(true);
1070  }
1071  }
1072  else
1073  {
1075  Format.clear();
1076  FpsNP[FPS_INSTANT].setValue(0);
1077  FpsNP[FPS_AVERAGE].setValue(0);
1078  if (isStreaming)
1079  {
1080  if (!isRecording)
1081  {
1083  {
1084  if (dynamic_cast<INDI::CCD*>(currentDevice)->StopStreaming() == false)
1085  {
1087  LOG_ERROR("Failed to stop streaming.");
1088  StreamSP.apply();
1089  return false;
1090  }
1091  }
1093  {
1094  if (dynamic_cast<INDI::SensorInterface*>(currentDevice)->StopStreaming() == false)
1095  {
1097  LOG_ERROR("Failed to stop streaming.");
1098  StreamSP.apply();
1099  return false;
1100  }
1101  }
1102  }
1103 
1104  StreamSP.reset();
1105  StreamSP[1].setState(ISS_ON);
1106  isStreaming = false;
1107  Format.clear();
1108  FpsNP[FPS_INSTANT].setValue(0);
1109  FpsNP[FPS_AVERAGE].setValue(0);
1110 
1111  recorder->setStreamEnabled(false);
1112  }
1113  }
1114 
1115  StreamSP.apply();
1116  return true;
1117 }
1118 
1119 bool StreamManager::setStream(bool enable)
1120 {
1121  D_PTR(StreamManager);
1122  return d->setStream(enable);
1123 }
1124 
1126 {
1127  D_PTR(StreamManager);
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);
1133  return true;
1134 }
1135 
1136 void StreamManagerPrivate::getStreamFrame(uint16_t * x, uint16_t * y, uint16_t * w, uint16_t * h) const
1137 {
1138  *x = StreamFrameNP[CCDChip::FRAME_X].getValue();
1139  *y = StreamFrameNP[CCDChip::FRAME_Y].getValue();
1140  *w = StreamFrameNP[CCDChip::FRAME_W].getValue();
1141  *h = StreamFrameNP[CCDChip::FRAME_H].getValue();
1142 }
1143 
1144 void StreamManagerPrivate::setStreamFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
1145 {
1146  StreamFrameNP[CCDChip::FRAME_X].setValue(x);
1147  StreamFrameNP[CCDChip::FRAME_Y].setValue(y);
1148  StreamFrameNP[CCDChip::FRAME_W].setValue(w);
1149  StreamFrameNP[CCDChip::FRAME_H].setValue(h);
1150 }
1151 
1153 {
1154  setStreamFrame(frameInfo.x, frameInfo.y, frameInfo.w, frameInfo.h);
1155 }
1156 
1157 void StreamManager::getStreamFrame(uint16_t * x, uint16_t * y, uint16_t * w, uint16_t * h) const
1158 {
1159  D_PTR(const StreamManager);
1160  d->getStreamFrame(x, y, w, h);
1161 }
1162 
1163 bool StreamManagerPrivate::uploadStream(const uint8_t * buffer, uint32_t nbytes)
1164 {
1165  // Send as is, already encoded.
1166  if (PixelFormat == INDI_JPG)
1167  {
1168  // Upload to client now
1169 #ifdef HAVE_WEBSOCKET
1170  if (dynamic_cast<INDI::CCD*>(currentDevice)->HasWebSocket()
1171  && dynamic_cast<INDI::CCD*>(currentDevice)->WebSocketS[CCD::WEBSOCKET_ENABLED].s == ISS_ON)
1172  {
1173  if (Format != ".streajpg")
1174  {
1175  Format = ".streajpg";
1176  dynamic_cast<INDI::CCD*>(currentDevice)->wsServer.send_text(Format);
1177  }
1178 
1179  dynamic_cast<INDI::CCD*>(currentDevice)->wsServer.send_binary(buffer, nbytes);
1180  return true;
1181  }
1182 #endif
1183  imageBP->at(0)->setBlob(const_cast<uint8_t *>(buffer));
1184  imageBP->at(0)->setBlobLen(nbytes);
1185  imageBP->at(0)->setSize(nbytes);
1186  imageBP->at(0)->setFormat(".streajpg");
1188  imageBP->apply();
1189  return true;
1190  }
1191 
1192  // Binning for grayscale frames only for now - REMOVE ME
1193 #if 0
1194  if (dynamic_cast<INDI::CCD*>(currentDevice)->PrimaryCCD.getNAxis() == 2)
1195  {
1196  dynamic_cast<INDI::CCD*>(currentDevice)->PrimaryCCD.binFrame();
1197  nbytes /= dynamic_cast<INDI::CCD*>(currentDevice)->PrimaryCCD.getBinX() * dynamic_cast<INDI::CCD*>
1198  (currentDevice)->PrimaryCCD.getBinY();
1199  }
1200 #endif
1201 
1203  {
1204  if (encoder->upload(imageBP->at(0), buffer, nbytes, dynamic_cast<INDI::CCD*>(currentDevice)->PrimaryCCD.isCompressed()))
1205  {
1206 #ifdef HAVE_WEBSOCKET
1207  if (dynamic_cast<INDI::CCD*>(currentDevice)->HasWebSocket()
1208  && dynamic_cast<INDI::CCD*>(currentDevice)->WebSocketS[CCD::WEBSOCKET_ENABLED].s == ISS_ON)
1209  {
1210  if (Format != ".stream")
1211  {
1212  Format = ".stream";
1213  dynamic_cast<INDI::CCD*>(currentDevice)->wsServer.send_text(Format);
1214  }
1215 
1216  dynamic_cast<INDI::CCD*>(currentDevice)->wsServer.send_binary(buffer, nbytes);
1217  return true;
1218  }
1219 #endif
1220  // Upload to client now
1222  imageBP->apply();
1223  return true;
1224  }
1225  }
1227  {
1228  if (encoder->upload(imageBP->at(0), buffer, nbytes, false))//dynamic_cast<INDI::SensorInterface*>(currentDevice)->isCompressed()))
1229  {
1230  // Upload to client now
1232  imageBP->apply();
1233  return true;
1234  }
1235  }
1236 
1237  return false;
1238 }
1239 
1241 {
1242  D_PTR(const StreamManager);
1243  return d->recorder;
1244 }
1245 
1247 {
1248  D_PTR(const StreamManager);
1249  return d->direct_record;
1250 }
1251 
1253 {
1254  D_PTR(const StreamManager);
1255  return d->isStreaming;
1256 }
1257 
1259 {
1260  D_PTR(const StreamManager);
1261  return d->isRecording && !d->isRecordingAboutToClose;
1262 }
1263 
1265 {
1266  D_PTR(const StreamManager);
1267  return (d->isStreaming || d->isRecording);
1268 }
1269 
1271 {
1272  D_PTR(const StreamManager);
1273  return 1.0 / d->StreamExposureNP[0].getValue();
1274 }
1276 {
1277  D_PTR(const StreamManager);
1278  return d->StreamExposureNP[0].getValue();
1279 }
1280 
1282 {
1283  D_PTR(StreamManager);
1284  d->hasStreamingExposure = enable;
1285 }
1286 
1287 }
INDI::StreamManagerPrivate::ISNewSwitch
bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Definition: streammanager.cpp:757
INDI::StreamManagerPrivate::newFrame
void newFrame(const uint8_t *buffer, uint32_t nbytes)
Definition: streammanager.cpp:264
INDI::ElapsedTimer::nsecsElapsed
int64_t nsecsElapsed() const
Returns the number of nanoseconds since this ElapsedTimer was last started.
Definition: indielapsedtimer.cpp:58
IP_RO
@ IP_RO
Definition: indiapi.h:183
INDI::StreamManagerPrivate::rawWidth
uint16_t rawWidth
Definition: streammanager_p.h:235
INDI::StreamManager::isRecording
bool isRecording() const
Definition: streammanager.cpp:1258
INDI::StreamManagerPrivate::setSize
void setSize(uint16_t width, uint16_t height)
Definition: streammanager.cpp:494
indiutility.h
INDI::PropertyNumber::updateMinMax
void updateMinMax()
Definition: indipropertynumber.cpp:55
INDI::PropertyView::setState
void setState(IPState state)
Definition: indipropertyview.h:595
INDI::StreamManagerPrivate::gammaLut16
GammaLut16 gammaLut16
Definition: streammanager_p.h:251
INDI::StreamManagerPrivate::frameCountDivider
uint32_t frameCountDivider
Definition: streammanager_p.h:231
INDI::RecorderInterface::setFPS
virtual bool setFPS(float FPS)
Definition: recorderinterface.h:69
INDI::StreamManagerPrivate::LIMITS_BUFFER_MAX
@ LIMITS_BUFFER_MAX
Definition: streammanager_p.h:208
INDI::StreamManager::setStream
bool setStream(bool enable)
setStream Enables (starts) or disables (stops) streaming.
Definition: streammanager.cpp:1119
INDI::RecorderInterface::setStreamEnabled
virtual void setStreamEnabled(bool enable)=0
INDI::RecorderManager::setRecorder
void setRecorder(RecorderInterface *recorder)
Definition: recordermanager.cpp:69
INDI::StreamManager::close
bool close()
Definition: streammanager.cpp:532
INDI::StreamManager::newFrame
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,...
Definition: streammanager.cpp:342
INDI::StreamManager::StreamManager
StreamManager(DefaultDevice *currentDevice)
Definition: streammanager.cpp:73
INDI::PropertyBasic::isNameMatch
bool isNameMatch(const char *otherName) const
Definition: indipropertybasic.cpp:194
LOGF_ERROR
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
IPS_OK
@ IPS_OK
Definition: indiapi.h:161
INDI::StreamManagerPrivate::recordfiledir
std::string recordfiledir
Definition: streammanager_p.h:219
min
double min(void)
INDI::StreamManagerPrivate::FrameInfo::pixels
size_t pixels() const
Definition: streammanager_p.h:80
INDI::EncoderManager::setEncoder
void setEncoder(EncoderInterface *encoder)
Definition: encodermanager.cpp:61
ISS_OFF
@ ISS_OFF
Definition: indiapi.h:150
INDI::CCD::HasWebSocket
bool HasWebSocket()
Definition: indiccd.h:256
INDI::StreamManager::updateProperties
virtual bool updateProperties()
Definition: streammanager.cpp:253
INDI::StreamManagerPrivate::FrameInfo::w
size_t w
Definition: streammanager_p.h:53
IPS_ALERT
@ IPS_ALERT
Definition: indiapi.h:163
INDI::StreamManagerPrivate::RECORD_ON
@ RECORD_ON
Definition: streammanager_p.h:169
INDI::StreamManager::isBusy
bool isBusy() const
Definition: streammanager.cpp:1264
INDI::EncoderInterface::upload
virtual bool upload(IBLOB *bp, const uint8_t *buffer, uint32_t nbytes, bool isCompressed=false)=0
INDI::StreamManagerPrivate::recordMutex
std::mutex recordMutex
Definition: streammanager_p.h:249
INDI::DefaultDevice::defineProperty
void defineProperty(INumberVectorProperty *property)
Definition: defaultdevice.cpp:997
INDI::StreamManagerPrivate::encoderManager
EncoderManager encoderManager
Definition: streammanager_p.h:222
INDI::BaseDevice::getBLOB
INDI::PropertyView< IBLOB > * getBLOB(const char *name) const
Definition: basedevice.cpp:119
INDI::StreamManagerPrivate::RecordOptionsNP
INDI::PropertyNumber RecordOptionsNP
Definition: streammanager_p.h:190
INDI::StreamManagerPrivate::FrameInfo::y
size_t y
Definition: streammanager_p.h:53
INDI::StreamManagerPrivate::isRecording
std::atomic< bool > isRecording
Definition: streammanager_p.h:211
INDI::StreamManager
Definition: streammanager.h:96
INDI::format_time
std::string format_time(const std::tm &tm, const char *format)
Converts the date and time to string - this function uses 'strftime'.
Definition: indiutility.cpp:64
INDI::StreamManager::ISNewNumber
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Definition: streammanager.cpp:1009
INDI_UNUSED
#define INDI_UNUSED(x)
Definition: indidevapi.h:799
INDI::StreamManagerPrivate::RecordFileTP
INDI::PropertyText RecordFileTP
Definition: streammanager_p.h:176
INDI::StreamManagerPrivate::FPSPreview
FPSMeter FPSPreview
Definition: streammanager_p.h:228
INDI::StreamManagerPrivate::recordfilename
std::string recordfilename
Definition: streammanager_p.h:219
INDI::StreamManager::getRecorder
RecorderInterface * getRecorder() const
Definition: streammanager.cpp:1240
INDI::SensorInterface
The SensorDevice class provides functionality of a Sensor Device within a Sensor.
Definition: indisensorinterface.h:70
INDI::EncoderInterface::getName
const char * getName()
Definition: encoderinterface.cpp:36
INDI::StreamManagerPrivate::isStreaming
std::atomic< bool > isStreaming
Definition: streammanager_p.h:210
INDI::CCDChip::FRAME_X
@ FRAME_X
Definition: indiccdchip.h:71
INDI::StreamManagerPrivate::FrameInfo::totalSize
size_t totalSize() const
Definition: streammanager_p.h:83
INDI::BaseDevice::getDeviceName
const char * getDeviceName() const
Definition: basedevice.cpp:799
INDI::StreamManager::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Definition: streammanager.cpp:892
INDI::FPSMeter::framesPerSecond
double framesPerSecond() const
Number of frames per second counted in the time window.
Definition: fpsmeter.cpp:60
INDI::PropertyText::fill
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
Definition: indipropertytext.cpp:45
INDI::Logger::DBG_SESSION
@ DBG_SESSION
Definition: indilogger.h:194
INDI::StreamManagerPrivate::setStream
bool setStream(bool enable)
Definition: streammanager.cpp:1015
INDI::StreamManager::setStreamingExposureEnabled
void setStreamingExposureEnabled(bool enable)
setStreamingExposureEnabled Can stream exposure time be changed?
Definition: streammanager.cpp:1281
INDI::CCD::CurrentFilterSlot
int CurrentFilterSlot
Definition: indiccd.h:579
INDI::CCD::PrimaryCCD
CCDChip PrimaryCCD
Definition: indiccd.h:583
INDI::StreamManagerPrivate::FrameInfo::x
size_t x
Definition: streammanager_p.h:53
INDI::RecorderInterface::getName
virtual const char * getName()
Definition: recorderinterface.cpp:33
INDI::PropertySwitch::reset
void reset()
Definition: indipropertyswitch.cpp:39
INDI::EncoderInterface::setPixelFormat
virtual bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
Definition: encoderinterface.cpp:53
LOG_INFO
#define LOG_INFO(txt)
Definition: indilogger.h:74
INDI::FPSMeter::setTimeWindow
void setTimeWindow(double timeWindow)
Time window setup.
Definition: fpsmeter.cpp:55
MAXRBUF
#define MAXRBUF
Definition: indidriver.c:52
INDI::StreamManagerPrivate::TimeFrame::time
double time
Definition: streammanager_p.h:240
INDI::StreamManager::ISGetProperties
virtual void ISGetProperties(const char *dev)
Definition: streammanager.cpp:202
INDI::StreamManagerPrivate::getStreamFrame
void getStreamFrame(uint16_t *x, uint16_t *y, uint16_t *w, uint16_t *h) const
Definition: streammanager.cpp:1136
streammanager_p.h
INDI::StreamManagerPrivate::ISGetProperties
void ISGetProperties(const char *dev)
Definition: streammanager.cpp:181
INDI::StreamManagerPrivate::FPS_AVERAGE
@ FPS_AVERAGE
Definition: streammanager_p.h:187
indilogger.h
INDI::StreamManagerPrivate::expand
static std::string expand(const std::string &fname, const std::map< std::string, std::string > &patterns)
Definition: streammanager.cpp:590
INDI::PropertySwitch::fill
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
Definition: indipropertyswitch.cpp:63
INDI::StreamManagerPrivate::framesIncoming
UniqueQueue< TimeFrame > framesIncoming
Definition: streammanager_p.h:246
INDI::CCD
Class to provide general functionality of CCD cameras with a single CCD sensor, or a primary CCD sens...
Definition: indiccd.h:115
INDI_RGB
@ INDI_RGB
Definition: indibasetypes.h:74
INDI::mkpath
int mkpath(std::string s, mode_t mode)
Create a path directory - this function uses 'mkdir'.
Definition: indiutility.cpp:29
INDI::StreamManager::saveConfigItems
virtual bool saveConfigItems(FILE *fp)
Definition: streammanager.cpp:1125
INDI::PropertySwitch::update
bool update(const ISState states[], const char *const names[], int n)
Definition: indipropertyswitch.cpp:57
INDI::StreamManager::initProperties
virtual bool initProperties()
Definition: streammanager.cpp:175
INDI::SingleThreadPool::tryStart
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....
Definition: indisinglethreadpool.cpp:80
LOGF_DEBUG
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
INDI::RecorderInterface::setPixelFormat
virtual bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth=8)=0
INDI::RecorderInterface::open
virtual bool open(const char *filename, char *errmsg)=0
INDI::StreamManagerPrivate::recorderManager
RecorderManager recorderManager
Definition: streammanager_p.h:216
INDI::StreamManager::setPixelFormat
bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth=8)
Definition: streammanager.cpp:568
INDI::StreamManagerPrivate::initProperties
bool initProperties()
Definition: streammanager.cpp:91
indisinglethreadpool.h
INDI::PropertyBasic::getName
const char * getName() const
Definition: indipropertybasic.cpp:124
INDI::EncoderManager::getEncoderList
std::vector< EncoderInterface * > getEncoderList()
Definition: encodermanager.cpp:46
INDI::StreamManagerPrivate::RecordStreamSP
INDI::PropertySwitch RecordStreamSP
Definition: streammanager_p.h:166
IText
One text descriptor.
INDI::StreamManagerPrivate::dstFrameInfo
FrameInfo dstFrameInfo
Definition: streammanager_p.h:158
INDI::StreamManagerPrivate::RecorderSP
INDI::PropertySwitch RecorderSP
Definition: streammanager_p.h:203
streammanager.h
INDI::StreamManagerPrivate::PixelFormat
INDI_PIXEL_FORMAT PixelFormat
Definition: streammanager_p.h:233
INDI::RecorderInterface::getExtension
virtual const char * getExtension()=0
INDI::StreamManagerPrivate::encoder
EncoderInterface * encoder
Definition: streammanager_p.h:223
INDI::FPSMeter::totalFrames
uint64_t totalFrames() const
Total frames.
Definition: fpsmeter.cpp:70
INDI::StreamManagerPrivate::currentDevice
DefaultDevice * currentDevice
Definition: streammanager_p.h:156
INDI::DefaultDevice::getDriverInterface
virtual uint16_t getDriverInterface() override
Definition: defaultdevice.cpp:896
INDI::FPSMeter::reset
void reset()
Reset all frame information.
Definition: fpsmeter.cpp:80
LOGF_WARN
#define LOGF_WARN(fmt,...)
Definition: indilogger.h:81
INDI::CCDChip::FRAME_W
@ FRAME_W
Definition: indiccdchip.h:71
INDI::StreamManagerPrivate::framesThread
std::thread framesThread
Definition: streammanager_p.h:244
IPS_BUSY
@ IPS_BUSY
Definition: indiapi.h:162
ISR_1OFMANY
@ ISR_1OFMANY
Definition: indiapi.h:172
INDI::StreamManagerPrivate::~StreamManagerPrivate
virtual ~StreamManagerPrivate()
Definition: streammanager.cpp:63
indielapsedtimer.h
IPS_IDLE
@ IPS_IDLE
Definition: indiapi.h:160
INDI::StreamManagerPrivate::asyncStreamThread
void asyncStreamThread()
Thread processing frames and forwarding to recording and preview.
Definition: streammanager.cpp:409
INDI::PropertyView::apply
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
INDI::StreamManagerPrivate::FPSRecorder
FPSMeter FPSRecorder
Definition: streammanager_p.h:229
INDI::StreamManager::isStreaming
bool isStreaming() const
Definition: streammanager.cpp:1252
INDI::StreamManagerPrivate::rawHeight
uint16_t rawHeight
Definition: streammanager_p.h:235
INDI::StreamManagerPrivate::ENCODER_RAW
@ ENCODER_RAW
Definition: streammanager_p.h:200
INDI::ElapsedTimer
The ElapsedTimer class provides a fast way to calculate elapsed times.
Definition: indielapsedtimer.h:35
INDI::CCD::WEBSOCKET_ENABLED
@ WEBSOCKET_ENABLED
Definition: indiccd.h:674
INDI::StreamManager::ISNewText
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Definition: streammanager.cpp:922
INDI::StreamManagerPrivate::StreamFrameNP
INDI::PropertyNumber StreamFrameNP
Definition: streammanager_p.h:193
INDI::StreamManagerPrivate::StreamSP
INDI::PropertySwitch StreamSP
Definition: streammanager_p.h:161
INDI::StreamManagerPrivate::StreamExposureNP
INDI::PropertyNumber StreamExposureNP
Definition: streammanager_p.h:178
INDI::RecorderInterface::close
virtual bool close()=0
INDI::EncoderManager::getDefaultEncoder
EncoderInterface * getDefaultEncoder()
Definition: encodermanager.cpp:56
INDI::FPSMeter::deltaTime
double deltaTime() const
Time in milliseconds between last frames.
Definition: fpsmeter.cpp:65
INDI::StreamManagerPrivate::fastFPSUpdate
std::mutex fastFPSUpdate
Definition: streammanager_p.h:248
INDI::StreamManager::getTargetFPS
double getTargetFPS() const
Definition: streammanager.cpp:1270
INDI::RecorderManager::getRecorderList
std::vector< RecorderInterface * > getRecorderList()
Definition: recordermanager.cpp:54
INDI::StreamManager::getStreamFrame
void getStreamFrame(uint16_t *x, uint16_t *y, uint16_t *w, uint16_t *h) const
Definition: streammanager.cpp:1157
INDI::BaseDevice::isConnected
bool isConnected() const
Definition: basedevice.cpp:518
INDI::StreamManagerPrivate::ENCODER_MJPEG
@ ENCODER_MJPEG
Definition: streammanager_p.h:200
INDI::StreamManagerPrivate::FpsNP
INDI::PropertyNumber FpsNP
Definition: streammanager_p.h:186
INDI::StreamManagerPrivate::RECORD_TIME
@ RECORD_TIME
Definition: streammanager_p.h:170
INDI::PropertyBasic::findWidgetByName
WidgetView< T > * findWidgetByName(const char *name) const
Definition: indipropertybasic.cpp:277
INDI::EncoderInterface::init
virtual void init(INDI::DefaultDevice *mainDevice)
Definition: encoderinterface.cpp:41
INDI::StreamManagerPrivate::setStreamFrame
void setStreamFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
Definition: streammanager.cpp:1144
INDI_JPG
@ INDI_JPG
Definition: indibasetypes.h:76
INDI::PropertyNumber::update
bool update(const double values[], const char *const names[], int n)
Definition: indipropertynumber.cpp:39
INDI::PropertyText::update
bool update(const char *const texts[], const char *const names[], int n)
Definition: indipropertytext.cpp:39
INDI::RecorderInterface::writeFrame
virtual bool writeFrame(const uint8_t *frame, uint32_t nbytes)=0
INDI::StreamManagerPrivate::TimeFrame::frame
std::vector< uint8_t > frame
Definition: streammanager_p.h:241
LOGF_INFO
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
INDI::StreamManagerPrivate::FPSFast
FPSMeter FPSFast
Definition: streammanager_p.h:227
INDI::CCDChip::FRAME_Y
@ FRAME_Y
Definition: indiccdchip.h:71
INDI::RecorderManager::getDefaultRecorder
RecorderInterface * getDefaultRecorder()
Definition: recordermanager.cpp:64
INDI::StreamManagerPrivate::direct_record
bool direct_record
Definition: streammanager_p.h:218
LOG_ERROR
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
GammaLut16::apply
void apply(const uint16_t *source, size_t count, uint8_t *destination) const
Definition: gammalut16.cpp:43
INDI::PropertyBasic::apply
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
Definition: indipropertybasic.cpp:243
INDI::StreamManagerPrivate::ISNewNumber
bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Definition: streammanager.cpp:928
INDI::CCDChip::getNAxis
int getNAxis() const
Definition: indiccdchip.cpp:231
INDI::EncoderInterface
The EncoderInterface class is the base class for video streaming encoders.
Definition: encoderinterface.h:43
INDI::StreamManagerPrivate::recorder
RecorderInterface * recorder
Definition: streammanager_p.h:217
INDI::StreamManagerPrivate::RECORD_FRAME
@ RECORD_FRAME
Definition: streammanager_p.h:171
INDI::StreamManagerPrivate::subframe
static void subframe(const uint8_t *srcBuffer, const FrameInfo &srcFrameInfo, uint8_t *dstBuffer, const FrameInfo &dstFrameInfo)
Definition: streammanager.cpp:387
indisensorinterface.h
INDI::StreamManagerPrivate::imageBP
INDI::PropertyView< IBLOB > * imageBP
Definition: streammanager_p.h:196
INDI::StreamManagerPrivate::updateProperties
bool updateProperties()
Definition: streammanager.cpp:208
INDI::StreamManagerPrivate::RECORDER_OGV
@ RECORDER_OGV
Definition: streammanager_p.h:204
INDI::StreamManagerPrivate::FrameInfo
Definition: streammanager_p.h:51
INDI::StreamManagerPrivate::FPSAverage
FPSMeter FPSAverage
Definition: streammanager_p.h:226
INDI::replace_all
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'.
Definition: indiutility.cpp:73
INDI::StreamManager::isDirectRecording
bool isDirectRecording() const
Definition: streammanager.cpp:1246
INDI
Namespace to encapsulate INDI client, drivers, and mediator classes.
Definition: AlignmentSubsystemForClients.cpp:11
INDI::StreamManagerPrivate::RECORD_OFF
@ RECORD_OFF
Definition: streammanager_p.h:172
DEBUGF
#define DEBUGF(priority, msg,...)
Definition: indilogger.h:57
INDI::StreamManager::getTargetExposure
double getTargetExposure() const
Definition: streammanager.cpp:1275
INDI::StreamManagerPrivate::ISNewText
bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Definition: streammanager.cpp:898
indiccd.h
INDI::PropertyBasic::resize
void resize(size_t size)
Definition: indipropertybasic.cpp:298
IP_RW
@ IP_RW
Definition: indiapi.h:185
LOG_WARN
#define LOG_WARN(txt)
Definition: indilogger.h:73
INDI::StreamManagerPrivate::hasStreamingExposure
bool hasStreamingExposure
Definition: streammanager_p.h:213
INDI::PropertyBasic::setState
void setState(IPState state)
Definition: indipropertybasic.cpp:103
INDI::PropertySwitch::findOnSwitchIndex
int findOnSwitchIndex() const
Definition: indipropertyswitch.cpp:45
INDI::FPSMeter::newFrame
bool newFrame()
When you get a frame, call the function to count.
Definition: fpsmeter.cpp:31
INDI::StreamManagerPrivate::LimitsNP
INDI::PropertyNumber LimitsNP
Definition: streammanager_p.h:207
INDI::PropertyNumber::fill
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
Definition: indipropertynumber.cpp:45
INDI::PropertyBasic::getState
IPState getState() const
Definition: indipropertybasic.cpp:166
INDI::RecorderInterface
The RecorderInterface class is the base class for recorders.
Definition: recorderinterface.h:56
ISState
ISState
Switch state.
Definition: indiapi.h:148
INDI::StreamManager::~StreamManager
virtual ~StreamManager()
Definition: streammanager.cpp:77
INDI::StreamManagerPrivate::FPS_INSTANT
@ FPS_INSTANT
Definition: streammanager_p.h:187
INDI::StreamManagerPrivate
Definition: streammanager_p.h:48
INDI::FPSMeter::totalTime
double totalTime() const
Total time.
Definition: fpsmeter.cpp:75
INDI::StreamManagerPrivate::getDeviceName
const char * getDeviceName() const
Definition: streammanager.cpp:80
INDI::StreamManager::getDeviceName
const char * getDeviceName() const
Definition: streammanager.cpp:85
INDI::StreamManagerPrivate::LIMITS_PREVIEW_FPS
@ LIMITS_PREVIEW_FPS
Definition: streammanager_p.h:208
INDI::StreamManagerPrivate::StreamManagerPrivate
StreamManagerPrivate(DefaultDevice *defaultDevice)
Definition: streammanager.cpp:44
INDI::StreamManagerPrivate::uploadStream
bool uploadStream(const uint8_t *buffer, uint32_t nbytes)
uploadStream Upload frame to client using the selected encoder
Definition: streammanager.cpp:1163
INDI::StreamManagerPrivate::FrameInfo::bytesPerColor
size_t bytesPerColor
Definition: streammanager_p.h:54
INDI::BaseDevice::CCD_INTERFACE
@ CCD_INTERFACE
Definition: basedevice.h:73
INDI::StreamManagerPrivate::stopRecording
bool stopRecording(bool force=false)
Definition: streammanager.cpp:720
INDI::StreamManagerPrivate::startRecording
bool startRecording()
Definition: streammanager.cpp:613
INDI::DefaultDevice
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
Definition: defaultdevice.h:118
INDI::StreamManagerPrivate::FrameInfo::lineSize
size_t lineSize() const
Definition: streammanager_p.h:86
INDI::StreamManagerPrivate::EncoderSP
INDI::PropertySwitch EncoderSP
Definition: streammanager_p.h:199
INDI::DefaultDevice::deleteProperty
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
Definition: defaultdevice.cpp:965
INDI::StreamManagerPrivate::TimeFrame
Definition: streammanager_p.h:239
INDI::SingleThreadPool
Definition: indisinglethreadpool.h:29
INDI::StreamManagerPrivate::RECORDER_RAW
@ RECORDER_RAW
Definition: streammanager_p.h:204
INDI::StreamManagerPrivate::StreamTimeNP
INDI::PropertyNumber StreamTimeNP
Definition: streammanager_p.h:163
INDI::StreamManagerPrivate::PixelDepth
uint8_t PixelDepth
Definition: streammanager_p.h:234
errno
int errno
INDI::StreamManagerPrivate::updateSourceFrameInfo
FrameInfo updateSourceFrameInfo()
Definition: streammanager.cpp:349
INDI::StreamManagerPrivate::STREAM_DIVISOR
@ STREAM_DIVISOR
Definition: streammanager_p.h:182
INDI::StreamManagerPrivate::recordStream
bool recordStream(const uint8_t *buffer, uint32_t nbytes, double deltams)
recordStream Calls the backend recorder to record a single frame.
Definition: streammanager.cpp:581
INDI::StreamManagerPrivate::framesThreadTerminate
std::atomic< bool > framesThreadTerminate
Definition: streammanager_p.h:245
INDI::BaseDevice::SENSOR_INTERFACE
@ SENSOR_INTERFACE
Definition: basedevice.h:89
Aux::buffer
std::vector< uint8_t > buffer
Definition: celestronauxpacket.h:38
INDI::PropertyView::at
WidgetType * at(size_t index) const
Definition: indipropertyview.h:242
INDI::CCDChip::FRAME_H
@ FRAME_H
Definition: indiccdchip.h:71
INDI::StreamManagerPrivate::STREAM_EXPOSURE
@ STREAM_EXPOSURE
Definition: streammanager_p.h:181
INDI_PIXEL_FORMAT
INDI_PIXEL_FORMAT
Definition: indibasetypes.h:63
INDI::ElapsedTimer::start
void start()
Starts this timer. Once started, a timer value can be checked with elapsed().
Definition: indielapsedtimer.cpp:36
INDI::StreamManager::setSize
void setSize(uint16_t width, uint16_t height=1)
Definition: streammanager.cpp:575
INDI::PropertySwitch::findOnSwitch
INDI::WidgetView< ISwitch > * findOnSwitch() const
Definition: indipropertyswitch.cpp:51
INDI::StreamManagerPrivate::isRecordingAboutToClose
std::atomic< bool > isRecordingAboutToClose
Definition: streammanager_p.h:212
INDI::StreamManagerPrivate::setPixelFormat
bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
Definition: streammanager.cpp:539
INDI::StreamManagerPrivate::Format
std::string Format
Definition: streammanager_p.h:236
INDI::StreamManagerPrivate::FrameInfo::h
size_t h
Definition: streammanager_p.h:53
ISS_ON
@ ISS_ON
Definition: indiapi.h:151