diff --git a/src/debug_video_tap.cpp b/src/debug_video_tap.cpp index 84f59e4..bd78cf9 100644 --- a/src/debug_video_tap.cpp +++ b/src/debug_video_tap.cpp @@ -190,7 +190,8 @@ float DebugVideoTap::render(void) { std::string window_title {"DebugVideoTap #"}; window_title += std::to_string(view._id); ImGui::SetNextWindowSize({400, 420}, ImGuiCond_Appearing); - if (ImGui::Begin(window_title.c_str(), nullptr, ImGuiWindowFlags_NoScrollbar)) { + bool window_open = true; + if (ImGui::Begin(window_title.c_str(), &window_open, ImGuiWindowFlags_NoScrollbar)) { while (auto new_frame_opt = stream->pop()) { // timing if (view._v_last_ts == 0) { @@ -208,40 +209,25 @@ float DebugVideoTap::render(void) { } SDL_Surface* new_frame_surf = new_frame_opt.value().surface.get(); - - SDL_Surface* converted_surf = new_frame_surf; - //if (new_frame_surf->format != SDL_PIXELFORMAT_RGBA32) { - // // we need to convert - // //std::cerr << "DVT: need to convert\n"; - // converted_surf = SDL_ConvertSurfaceAndColorspace(new_frame_surf, SDL_PIXELFORMAT_RGBA32, nullptr, SDL_COLORSPACE_RGB_DEFAULT, 0); - // assert(converted_surf->format == SDL_PIXELFORMAT_RGBA32); - //} - - SDL_LockSurface(converted_surf); - if (view._tex == 0 || (int)view._tex_w != converted_surf->w || (int)view._tex_h != converted_surf->h) { + SDL_LockSurface(new_frame_surf); + if (view._tex == 0 || (int)view._tex_w != new_frame_surf->w || (int)view._tex_h != new_frame_surf->h) { _tu.destroy(view._tex); view._tex = _tu.upload( - static_cast(converted_surf->pixels), - converted_surf->w, - converted_surf->h, + static_cast(new_frame_surf->pixels), + new_frame_surf->w, + new_frame_surf->h, TextureUploaderI::IYUV, // forced conversion TextureUploaderI::LINEAR, TextureUploaderI::STREAMING ); - view._tex_w = converted_surf->w; - view._tex_h = converted_surf->h; + view._tex_w = new_frame_surf->w; + view._tex_h = new_frame_surf->h; } else { //_tu.update(view._tex, static_cast(converted_surf->pixels), converted_surf->w * converted_surf->h * 4); - _tu.update(view._tex, static_cast(converted_surf->pixels), converted_surf->w * converted_surf->h * 3/2); - //_tu.updateRGBA(view._tex, static_cast(converted_surf->pixels), converted_surf->w * converted_surf->h * 4); + _tu.update(view._tex, static_cast(new_frame_surf->pixels), new_frame_surf->w * new_frame_surf->h * 3/2); } - SDL_UnlockSurface(converted_surf); - - //if (new_frame_surf != converted_surf) { - // // clean up temp - // SDL_DestroySurface(converted_surf); - //} + SDL_UnlockSurface(new_frame_surf); } ImGui::Checkbox("mirror ", &view._mirror); @@ -264,6 +250,15 @@ float DebugVideoTap::render(void) { } } ImGui::End(); + + if (!window_open) { + _sm.forEachConnectedExt(_tap, [this, stream_ptr = stream.get()](ObjectHandle o, const void*, const void* writer) { + // very hacky + if (writer == stream_ptr) { + _sm.disconnect(o, _tap); + } + }); + } } return min_interval; diff --git a/src/frame_streams/stream_manager.cpp b/src/frame_streams/stream_manager.cpp index 2bf72d5..3afa283 100644 --- a/src/frame_streams/stream_manager.cpp +++ b/src/frame_streams/stream_manager.cpp @@ -86,7 +86,7 @@ bool StreamManager::connect(Object src, Object sink, bool threaded) { bool StreamManager::disconnect(Object src, Object sink) { auto res = std::find_if( _connections.cbegin(), _connections.cend(), - [&](const auto& a) { return a->src == src && a->sink == sink; } + [src, sink](const auto& a) { return a->src == src && a->sink == sink; } ); if (res == _connections.cend()) { // not found diff --git a/src/frame_streams/stream_manager.hpp b/src/frame_streams/stream_manager.hpp index 5bb481f..5e7e8a2 100644 --- a/src/frame_streams/stream_manager.hpp +++ b/src/frame_streams/stream_manager.hpp @@ -52,6 +52,14 @@ namespace Components { template using FrameStream2Sink = std::unique_ptr>; + // supported sources/sinks have this component + // to signal the backend sender/encoder a generic bitrate + // TODO: split into advanced encoder bitrate / target / range + struct Bitrate { + // in kilo bits per second + int64_t rate{0}; + }; + } // Components @@ -66,6 +74,8 @@ class StreamManager : protected ObjectStoreEventI { struct Data { virtual ~Data(void) {} + virtual const void* getReaderStream(void) = 0; + virtual const void* getWriterStream(void) = 0; }; std::unique_ptr data; // stores reader writer type erased std::function pump_fn; // TODO: make it return next interval? @@ -116,6 +126,29 @@ class StreamManager : protected ObjectStoreEventI { bool disconnect(Object src, Object sink); bool disconnectAll(Object o); + template + void forEachConnected(ObjectHandle o, FN&& fn) { + for (const auto& con : _connections) { + if (con->src == o) { + fn(con->sink); + } else if (con->sink == o) { + fn(con->src); + } + } + } + + template + void forEachConnectedExt(ObjectHandle o, FN&& fn) { + for (const auto& con : _connections) { + if (con->src == o) { + // TODO: very ugly + fn(con->sink, con->data.get()->getReaderStream(), con->data.get()->getWriterStream()); + } else if (con->sink == o) { + fn(con->src, con->data.get()->getReaderStream(), con->data.get()->getWriterStream()); + } + } + } + // do we need the time delta? float tick(float); @@ -183,9 +216,11 @@ bool StreamManager::connect(Object src, Object sink, bool threaded) { auto& sink_stream = h_sink.get>(); struct inlineData : public Connection::Data { - virtual ~inlineData(void) {} + ~inlineData(void) {} std::shared_ptr> reader; std::shared_ptr> writer; + const void* getReaderStream(void) override { return reader.get(); } + const void* getWriterStream(void) override { return writer.get(); } }; auto our_data = std::make_unique(); diff --git a/src/tox_av_voip_model.cpp b/src/tox_av_voip_model.cpp index 10d1504..faf2121 100644 --- a/src/tox_av_voip_model.cpp +++ b/src/tox_av_voip_model.cpp @@ -56,6 +56,16 @@ struct ToxAVCallAudioSink : public FrameStream2SinkI { } } + bool setBitrate(uint64_t rate) { + // TODO: prevent disable active? + auto err = _toxav.toxavAudioSetBitRate(_fid, rate); + if (err == TOXAV_ERR_BIT_RATE_SET_OK) { + _audio_bitrate = rate; + return true; + } + return false; + } + // sink std::shared_ptr> subscribe(void) override { if (_writer) { @@ -116,6 +126,16 @@ struct ToxAVCallVideoSink : public FrameStream2SinkI { } } + bool setBitrate(uint64_t rate) { + // TODO: prevent disable active? + auto err = _toxav.toxavVideoSetBitRate(_fid, rate); + if (err == TOXAV_ERR_BIT_RATE_SET_OK) { + _video_bitrate = rate; + return true; + } + return false; + } + // sink std::shared_ptr> subscribe(void) override { if (_writer) { @@ -187,6 +207,7 @@ void ToxAVVoIPModel::addAudioSink(ObjectHandle session, uint32_t friend_number) auto new_asink = std::make_unique(_av, friend_number); auto* new_asink_ptr = new_asink.get(); + outgoing_audio.emplace(new_asink_ptr->_audio_bitrate); outgoing_audio.emplace(new_asink_ptr); outgoing_audio.emplace>(std::move(new_asink)); outgoing_audio.emplace(Components::StreamSink::create("ToxAV Friend Call Outgoing Audio")); @@ -240,6 +261,7 @@ void ToxAVVoIPModel::addVideoSink(ObjectHandle session, uint32_t friend_number) auto new_vsink = std::make_unique(_av, friend_number); auto* new_vsink_ptr = new_vsink.get(); + outgoing_video.emplace(new_vsink_ptr->_video_bitrate); outgoing_video.emplace(new_vsink_ptr); outgoing_video.emplace>(std::move(new_vsink)); outgoing_video.emplace(Components::StreamSink::create("ToxAV Friend Call Outgoing Video")); @@ -523,7 +545,7 @@ void ToxAVVoIPModel::handleEvent(const Events::FriendCallState& e) { } ToxAVVoIPModel::ToxAVVoIPModel(ObjectStore2& os, ToxAVI& av, ContactStore4I& cs, ToxContactModel2& tcm) : - _os(os), _av(av), _av_sr(_av.newSubRef(this)), _cs(cs), _cs_sr(_cs.newSubRef(this)), _tcm(tcm) + _os(os), _os_sr(_os.newSubRef(this)), _av(av), _av_sr(_av.newSubRef(this)), _cs(cs), _cs_sr(_cs.newSubRef(this)), _tcm(tcm) { _av_sr .subscribe(ToxAV_Event::friend_call) @@ -548,6 +570,10 @@ ToxAVVoIPModel::ToxAVVoIPModel(ObjectStore2& os, ToxAVI& av, ContactStore4I& cs, .subscribe(ContactStore4_Event::contact_construct) .subscribe(ContactStore4_Event::contact_update) ; + + _os_sr + .subscribe(ObjectStore_Event::object_update) + ; } ToxAVVoIPModel::~ToxAVVoIPModel(void) { @@ -873,3 +899,32 @@ bool ToxAVVoIPModel::onEvent(const ContactStore::Events::Contact4Destory&) { return false; } +bool ToxAVVoIPModel::onEvent(const ObjectStore::Events::ObjectConstruct&) { + return false; +} + +bool ToxAVVoIPModel::onEvent(const ObjectStore::Events::ObjectUpdate& e) { + if (e.e.all_of()) { + auto& rate = e.e.get_or_emplace().rate; + if (rate <= 0) { + rate = 0; + } + if (!e.e.get()->setBitrate(rate)) { + rate = e.e.get()->_video_bitrate; + } + } else if (e.e.all_of()) { + auto& rate = e.e.get_or_emplace().rate; + if (rate <= 0) { + rate = 0; + } + if (!e.e.get()->setBitrate(rate)) { + rate = e.e.get()->_audio_bitrate; + } + } + + return false; +} + +bool ToxAVVoIPModel::onEvent(const ObjectStore::Events::ObjectDestory&) { + return false; +} diff --git a/src/tox_av_voip_model.hpp b/src/tox_av_voip_model.hpp index 0797c83..0c671fc 100644 --- a/src/tox_av_voip_model.hpp +++ b/src/tox_av_voip_model.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include #include "./frame_streams/voip_model.hpp" @@ -16,8 +16,9 @@ struct ToxAVCallAudioSink; struct ToxAVCallVideoSink; -class ToxAVVoIPModel : protected ToxAVEventI, protected ContactStore4EventI, public VoIPModelI { +class ToxAVVoIPModel : protected ToxAVEventI, protected ContactStore4EventI, protected ObjectStoreEventI, public VoIPModelI { ObjectStore2& _os; + ObjectStore2::SubscriptionReference _os_sr; ToxAVI& _av; ToxAVI::SubscriptionReference _av_sr; ContactStore4I& _cs; @@ -93,5 +94,10 @@ class ToxAVVoIPModel : protected ToxAVEventI, protected ContactStore4EventI, pub bool onEvent(const ContactStore::Events::Contact4Construct&) override; bool onEvent(const ContactStore::Events::Contact4Update&) override; bool onEvent(const ContactStore::Events::Contact4Destory&) override; + + protected: // object events + bool onEvent(const ObjectStore::Events::ObjectConstruct&) override; + bool onEvent(const ObjectStore::Events::ObjectUpdate&) override; + bool onEvent(const ObjectStore::Events::ObjectDestory&) override; };