libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
rtmpmediaio.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_NETWORK
13#include <promeki/atomic.h>
14#include <promeki/audiodesc.h>
17#include <promeki/enums_rtmp.h>
18#include <promeki/frame.h>
19#include <promeki/framenumber.h>
20#include <promeki/framerate.h>
21#include <promeki/histogram.h>
22#include <promeki/imagedesc.h>
24#include <promeki/namespace.h>
25#include <promeki/pacinggate.h>
26#include <promeki/queue.h>
27#include <promeki/rtmpsession.h>
28#include <promeki/string.h>
29#include <promeki/uniqueptr.h>
30#include <promeki/url.h>
31
32PROMEKI_NAMESPACE_BEGIN
33
34class RtmpClient;
35class Thread;
36
118class RtmpMediaIO : public DedicatedThreadMediaIO {
119 PROMEKI_OBJECT(RtmpMediaIO, DedicatedThreadMediaIO)
120 public:
121 // ---- Telemetry keys ----
122
124 static inline const MediaIOStats::ID StatsFramesSent{"FramesSent"};
126 static inline const MediaIOStats::ID StatsFramesReceived{"FramesReceived"};
128 static inline const MediaIOStats::ID StatsVideoMessagesSent{"VideoMessagesSent"};
130 static inline const MediaIOStats::ID StatsAudioMessagesSent{"AudioMessagesSent"};
132 static inline const MediaIOStats::ID StatsVideoMessagesReceived{"VideoMessagesReceived"};
134 static inline const MediaIOStats::ID StatsAudioMessagesReceived{"AudioMessagesReceived"};
136 static inline const MediaIOStats::ID StatsBytesSent{"BytesSent"};
138 static inline const MediaIOStats::ID StatsBytesReceived{"BytesReceived"};
140 static inline const MediaIOStats::ID StatsSendQueueDepth{"SendQueueDepth"};
142 static inline const MediaIOStats::ID StatsReadQueueDepth{"ReadQueueDepth"};
144 static inline const MediaIOStats::ID StatsSendQueueOverflows{"SendQueueOverflows"};
146 static inline const MediaIOStats::ID StatsConnectDurationMs{"ConnectDurationMs"};
148 static inline const MediaIOStats::ID StatsHandshakeDurationMs{"HandshakeDurationMs"};
150 static inline const MediaIOStats::ID StatsVideoFramesDroppedPreIdr{"VideoFramesDroppedPreIdr"};
151
153 static inline const MediaIOStats::ID StatsPacingTicksOnTime{"PacingTicksOnTime"};
155 static inline const MediaIOStats::ID StatsPacingTicksLate{"PacingTicksLate"};
157 static inline const MediaIOStats::ID StatsPacingTicksSkipped{"PacingTicksSkipped"};
159 static inline const MediaIOStats::ID StatsPacingReanchors{"PacingReanchors"};
161 static inline const MediaIOStats::ID StatsPacingClockKind{"PacingClockKind"};
162
164 explicit RtmpMediaIO(ObjectBase *parent = nullptr);
165
167 ~RtmpMediaIO() override;
168
176 uint64_t objectId() const { return _objectId; }
177
197 Error proposeInput(const MediaDesc &offered, MediaDesc *preferred) const override;
198
199 protected:
200 Error executeCmd(MediaIOCommandOpen &cmd) override;
201 Error executeCmd(MediaIOCommandClose &cmd) override;
202 Error executeCmd(MediaIOCommandRead &cmd) override;
203 Error executeCmd(MediaIOCommandWrite &cmd) override;
204 Error executeCmd(MediaIOCommandStats &cmd) override;
205
224 Error executeCmd(MediaIOCommandSetClock &cmd) override;
225
226 // Wakes the reader-side executeCmd(Read) loop so close()
227 // can drain a strand parked on the reader queue pop.
228 void cancelBlockingWork() override;
229
230 private:
231 class PacketizerThread;
232 class DepacketizerThread;
233
235 void resetAll();
236
238 RtmpConnectOptions buildConnectOptions(const MediaIO::Config &cfg) const;
239
241 static bool hasVideoEssence(const Frame &frame);
242
244 static bool hasAudioEssence(const Frame &frame);
245
257 bool paceVideoFrame();
258
270 void armVideoPaceGate();
271
273 String paceClockKind() const;
274
288 void onClientDisconnected(Error reason);
289
290 // Owned RtmpClient — instantiated in executeCmd(Open),
291 // destroyed in resetAll. Lives across the open/close
292 // cycle so the writer / reader threads it owns service
293 // the wire.
294 UniquePtr<RtmpClient> _client;
295
296 // Worker threads. Both are nullptr when idle.
297 UniquePtr<PacketizerThread> _packetizer;
298 UniquePtr<DepacketizerThread> _depacketizer;
299
300 // Strand-side bounded reader queue — populated by the
301 // depacketizer thread, drained by executeCmd(Read).
302 Queue<Frame> _readerQueue;
303
304 // Cancellation latch for executeCmd(Read). Set by
305 // cancelBlockingWork(); cleared at every Open.
306 Atomic<bool> _readCancelled{false};
307
308 // Latched when @c RtmpClient::disconnectedSignal fires
309 // (peer went away, socket I/O error, etc.). Polled by
310 // the packetizer / depacketizer worker loops so they
311 // exit cleanly instead of spewing per-frame send
312 // failures into the log, and consulted by
313 // executeCmd(Read) / executeCmd(Write) so the failure
314 // surfaces to the pipeline as a write/read error.
315 // @c _disconnectErrorCode stores the disconnect reason
316 // as an @c Error::Code (int) to keep the atomic POD;
317 // reconstruct an @c Error via @c Error(static_cast).
318 Atomic<bool> _clientDisconnected{false};
319 Atomic<int> _disconnectErrorCode{0};
320
321 // Sink-side state set at Open from the MediaConfig +
322 // pending descriptor, read by the packetizer thread.
323 ImageDesc _imageDesc;
324 AudioDesc _audioDesc;
325 Url _url;
326 String _streamKey;
327 bool _readerMode = false;
328 bool _dropUntilKeyframe = true;
329 bool _repeatParameterSets = true;
330 bool _enhancedRtmp = true;
331 bool _emitAnnexB = false;
332 bool _dataEnabled = true;
333 int _sendQueueDepth = 64;
334 int _readQueueDepth = 64;
335
336 // Per-instance frame counters used to populate
337 // currentFrame / frameCount on read / write commands.
338 // Strand-owned; updated only inside executeCmd().
339 FrameCount _frameCount{0};
340 FrameCount _framesSent{0};
341
342 // Cumulative stats counters bumped by the worker
343 // threads, read by executeCmd(Stats). Atomics for
344 // lock-free cross-thread aggregation.
345 Atomic<int64_t> _readerFramesReceived{0};
346 Atomic<int64_t> _videoFramesDroppedPreIdr{0};
347 Atomic<int64_t> _sendQueueOverflows{0};
348
349 // Strand-side video pacing. Mirrors RtpMediaIO's
350 // _videoGate but defaults to an Internal wall-clock
351 // binding since TCP has no kernel-pacing analog to fall
352 // back on. Touched only from the strand.
353 RtmpVideoPacing _videoPacingMode{RtmpVideoPacing::Internal};
354 PacingGate _videoPaceGate;
355 FrameRate _frameRate;
356 int _paceSkipThresholdMs = 0;
357 int _paceReanchorThresholdMs = 0;
358 bool _paceClockIsExternal = false;
359
360 // Open-time telemetry — captured around the
361 // RtmpClient::open / publish calls.
362 int64_t _connectDurationMs = 0;
363 int64_t _handshakeDurationMs = 0;
364
365 // Process-local monotonic counter that gives every
366 // RtmpMediaIO a distinct id within one process.
367 static Atomic<uint64_t> _nextObjectId;
368 uint64_t _objectId = 0;
369};
370
375class RtmpFactory : public MediaIOFactory {
376 public:
377 RtmpFactory() = default;
378
379 String name() const override { return String("Rtmp"); }
380 String displayName() const override { return String("RTMP Stream"); }
381 String description() const override {
382 return String("RTMP / RTMPS publisher and subscriber "
383 "(H.264 + HEVC video, AAC audio)");
384 }
385 StringList schemes() const override {
386 return StringList{String("rtmp"), String("rtmps")};
387 }
388
389 bool canBeSource() const override { return true; }
390 bool canBeSink() const override { return true; }
391
392 Config::SpecMap configSpecs() const override;
393 Error urlToConfig(const Url &url, Config *outConfig) const override;
394 MediaIO *create(const Config &config, ObjectBase *parent = nullptr) const override;
395};
396
397PROMEKI_NAMESPACE_END
398
399#endif // PROMEKI_ENABLE_NETWORK