libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
mjpegstreammediaio.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_HTTP
13#include <promeki/atomic.h>
14#include <promeki/buffer.h>
16#include <promeki/duration.h>
17#include <promeki/error.h>
18#include <promeki/framerate.h>
19#include <promeki/httphandler.h>
20#include <promeki/list.h>
21#include <promeki/map.h>
22#include <promeki/mediadesc.h>
24#include <promeki/mutex.h>
25#include <promeki/namespace.h>
26#include <promeki/string.h>
27#include <promeki/timestamp.h>
28#include <promeki/uniqueptr.h>
30
31PROMEKI_NAMESPACE_BEGIN
32
33class HttpServer;
34
69class MjpegStreamSubscriber {
70 public:
72 virtual ~MjpegStreamSubscriber() = default;
73
90 virtual void onFrame(const Buffer &jpeg, const TimeStamp &ts) = 0;
91
101 virtual void onClosed() = 0;
102};
103
114struct MjpegStreamSnapshot {
116 int64_t framesEncoded = 0;
118 int64_t framesRateLimited = 0;
120 int64_t framesEncodeError = 0;
122 int64_t totalEncodeUs = 0;
124 int64_t peakEncodeUs = 0;
126 int64_t encodeSamples = 0;
128 int64_t totalEncodedBytes = 0;
129};
130
198class MjpegStreamMediaIO : public DedicatedThreadMediaIO {
199 PROMEKI_OBJECT(MjpegStreamMediaIO, DedicatedThreadMediaIO)
200 public:
202 static const String HttpBoundary;
203
205 static const String LatestJpegContentType;
206
208 MjpegStreamMediaIO(ObjectBase *parent = nullptr);
209
218 ~MjpegStreamMediaIO() override;
219
247 int attachSubscriber(MjpegStreamSubscriber *s);
248
263 void detachSubscriber(int id);
264
272 bool isStreaming() const;
273
289 Buffer latestJpeg() const;
290
303 TimeStamp latestJpegTimestamp() const;
304
315 MjpegStreamSnapshot snapshot() const;
316
363 void registerHttpRoute(HttpServer &server, const String &path);
364
390 static HttpHandlerFunc buildMultipartHandler(MjpegStreamMediaIO *sink);
391
392 Error proposeInput(const MediaDesc &offered, MediaDesc *preferred) const override;
393
394 protected:
395 Error executeCmd(MediaIOCommandOpen &cmd) override;
396 Error executeCmd(MediaIOCommandClose &cmd) override;
397 Error executeCmd(MediaIOCommandWrite &cmd) override;
398 Error executeCmd(MediaIOCommandStats &cmd) override;
399
400 private:
401
403 Error encodeFrame(const Frame &frame, Buffer *out, TimeStamp *ts);
404
406 bool latestRingEntry(Buffer *out, TimeStamp *outTs) const;
407
409 void publishEncoded(const Buffer &jpeg, const TimeStamp &ts);
410
411 // ---- Resolved configuration (latched at open time) ----
412 FrameRate _maxRate;
413 Duration _period;
414 int _quality = 80;
415 int _ringDepth = 1;
416 bool _isOpen = false;
417
418 // ---- Encoder strand state ----
420 UniquePtr<VideoEncoder> _encoder;
424 TimeStamp _lastEncoded;
425 bool _hasLastEncoded = false;
426 int64_t _frameSerial = 0;
427
428 // ---- Ring + subscribers (mutex-guarded) ----
429 struct RingEntry {
430 Buffer jpeg;
431 TimeStamp timestamp;
432 };
433
434 mutable Mutex _stateMutex;
435 List<RingEntry> _ring;
436 Map<int, MjpegStreamSubscriber *> _subscribers;
437 int _nextSubscriberId = 1;
438 MjpegStreamSnapshot _stats;
442 bool _isStreaming = false;
443};
444
449class MjpegStreamFactory : public MediaIOFactory {
450 public:
451 MjpegStreamFactory() = default;
452
453 String name() const override { return String("MjpegStream"); }
454 String displayName() const override { return String("MJPEG Preview Stream"); }
455 String description() const override {
456 return String("Frame-rate-limited motion-JPEG preview sink "
457 "(HTTP multipart-friendly).");
458 }
459
460 bool canBeSink() const override { return true; }
461
462 Config::SpecMap configSpecs() const override;
463
464 MediaIO *create(const Config &config, ObjectBase *parent = nullptr) const override;
465};
466
467PROMEKI_NAMESPACE_END
468
469#endif // PROMEKI_ENABLE_HTTP