diff --git a/src/main_screen.cpp b/src/main_screen.cpp index 0b9c3b7..380d8e2 100644 --- a/src/main_screen.cpp +++ b/src/main_screen.cpp @@ -647,7 +647,7 @@ Screen* MainScreen::tick(float time_delta, bool& quit) { //const float av_interval = std::pow(tav.toxavIterationInterval(), 1.18)/1000.f; const float av_interval = tav.toxavIterationInterval()/1000.f; - tavvoip.tick(); + const float av_voip_interval = tavvoip.tick(); #endif tcm.iterate(time_delta); // compute @@ -694,6 +694,10 @@ Screen* MainScreen::tick(float time_delta, bool& quit) { _min_tick_interval, av_interval ); + _min_tick_interval = std::min( + _min_tick_interval, + av_voip_interval + ); #endif //std::cout << "MS: min tick interval: " << _min_tick_interval << "\n"; diff --git a/src/tox_av.hpp b/src/tox_av.hpp index 34746a9..0791513 100644 --- a/src/tox_av.hpp +++ b/src/tox_av.hpp @@ -109,6 +109,9 @@ struct ToxAVI : public ToxAVEventProviderI { ToxAVI(Tox* tox); virtual ~ToxAVI(void); + // NOTE: interval timers are only interesting for receiving streams + // if we are only sending, it will always report 195ms + // interface // if iterate is called on a different thread, it will fire events there uint32_t toxavIterationInterval(void) const; diff --git a/src/tox_av_voip_model.cpp b/src/tox_av_voip_model.cpp index 7ad2f40..4951b0c 100644 --- a/src/tox_av_voip_model.cpp +++ b/src/tox_av_voip_model.cpp @@ -104,6 +104,7 @@ struct ToxAVCallVideoSink : public FrameStream2SinkI { uint32_t _fid; std::shared_ptr _writer; + uint64_t _last_ts {0}; ToxAVCallVideoSink(ToxAVI& toxav, uint32_t fid) : _toxav(toxav), _fid(fid) {} ~ToxAVCallVideoSink(void) { @@ -139,6 +140,7 @@ struct ToxAVCallVideoSink : public FrameStream2SinkI { if (sub == _writer) { _writer = nullptr; + _last_ts = 0; /*auto err = */_toxav.toxavVideoSetBitRate(_fid, 0); // print warning? on error? @@ -350,7 +352,7 @@ void ToxAVVoIPModel::audio_thread_tick(void) { //* `((sample rate) * (audio length) / 1000)`, where audio length can be //* 2.5, 5, 10, 20, 40 or 60 milliseconds. - // we likely needs to subdivide/repackage + // we likely need to subdivide/repackage // frame size should be an option exposed to the user // with 10ms as a default ? // the larger the frame size, the less overhead but the more delay @@ -372,7 +374,7 @@ void ToxAVVoIPModel::audio_thread_tick(void) { void ToxAVVoIPModel::video_thread_tick(void) { //for (const auto& [oc, vsink] : _os.registry().view().each()) { std::lock_guard lg{_video_sinks_mutex}; - for (const auto* vsink : _video_sinks) { + for (auto* vsink : _video_sinks) { if (!vsink->_writer) { continue; } @@ -389,6 +391,15 @@ void ToxAVVoIPModel::video_thread_tick(void) { continue; } + // interval estimate based on 2 frames + if (vsink->_last_ts != 0 && vsink->_last_ts < new_frame.timestampUS) { + const auto interval_us = new_frame.timestampUS - vsink->_last_ts; + if (_video_recent_interval > interval_us) { + _video_recent_interval = interval_us; + } + } + vsink->_last_ts = new_frame.timestampUS; + // conversion is done in the sink's stream SDL_Surface* surf = new_frame.surface.get(); assert(surf != nullptr); @@ -519,7 +530,7 @@ ToxAVVoIPModel::~ToxAVVoIPModel(void) { } } -void ToxAVVoIPModel::tick(void) { +float ToxAVVoIPModel::tick(void) { std::lock_guard lg{_e_queue_mutex}; while (!_e_queue.empty()) { const auto& e_var = _e_queue.front(); @@ -536,6 +547,14 @@ void ToxAVVoIPModel::tick(void) { _e_queue.pop_front(); } + + // TODO: audio (ez, just do 60ms if sending audio) + auto interval = _video_recent_interval/1'000'000.f; + + // funky if tickrate momentarily higher than frame rate + _video_recent_interval = 10'000'000; + + return interval; } ObjectHandle ToxAVVoIPModel::enter(const Contact4 c, const Components::VoIP::DefaultConfig& defaults) { diff --git a/src/tox_av_voip_model.hpp b/src/tox_av_voip_model.hpp index 5a1ceae..6aa9f6e 100644 --- a/src/tox_av_voip_model.hpp +++ b/src/tox_av_voip_model.hpp @@ -10,6 +10,7 @@ #include #include #include +#include // fwd struct ToxAVCallAudioSink; @@ -24,7 +25,7 @@ class ToxAVVoIPModel : protected ToxAVEventI, public VoIPModelI { uint64_t _pad0; // these events need to be worked on the main thread instead - // TODO: replac ewith lockless queue + // TODO: replace with lockless queue std::deque< std::variant< Events::FriendCall, @@ -42,6 +43,11 @@ class ToxAVVoIPModel : protected ToxAVEventI, public VoIPModelI { std::mutex _video_sinks_mutex; uint64_t _pad3; + // filled with min() in video_thread_tick() + // ms, 10sec means none + std::atomic _video_recent_interval{10'000'000}; + uint64_t _pad4; + // for faster lookup std::unordered_map _audio_sources; std::unordered_map _video_sources; @@ -54,8 +60,7 @@ class ToxAVVoIPModel : protected ToxAVEventI, public VoIPModelI { void destroySession(ObjectHandle session); - // TODO: this needs to move to the toxav thread - // we could use "events" as pre/post audio/video iterate... + // we use "events" as pre/post audio/video iterate... void audio_thread_tick(void); void video_thread_tick(void); @@ -67,7 +72,7 @@ class ToxAVVoIPModel : protected ToxAVEventI, public VoIPModelI { ~ToxAVVoIPModel(void); // handle events coming from toxav thread(s) - void tick(void); + float tick(void); public: // voip model ObjectHandle enter(const Contact4 c, const Components::VoIP::DefaultConfig& defaults) override;