make toxav respond to sink bitrate changes

and make debug taps disconnect on X
This commit is contained in:
Green Sky 2025-04-03 21:57:14 +02:00
parent fa7a77ca14
commit 553e883ee1
No known key found for this signature in database
GPG Key ID: DBE05085D874AB4A
5 changed files with 121 additions and 30 deletions

View File

@ -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<const uint8_t*>(converted_surf->pixels),
converted_surf->w,
converted_surf->h,
static_cast<const uint8_t*>(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<const uint8_t*>(converted_surf->pixels), converted_surf->w * converted_surf->h * 4);
_tu.update(view._tex, static_cast<const uint8_t*>(converted_surf->pixels), converted_surf->w * converted_surf->h * 3/2);
//_tu.updateRGBA(view._tex, static_cast<const uint8_t*>(converted_surf->pixels), converted_surf->w * converted_surf->h * 4);
_tu.update(view._tex, static_cast<const uint8_t*>(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;

View File

@ -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

View File

@ -52,6 +52,14 @@ namespace Components {
template<typename FrameType>
using FrameStream2Sink = std::unique_ptr<FrameStream2SinkI<FrameType>>;
// 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> data; // stores reader writer type erased
std::function<void(Connection&)> 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<typename FN>
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<typename FN>
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<Components::FrameStream2Sink<FrameType>>();
struct inlineData : public Connection::Data {
virtual ~inlineData(void) {}
~inlineData(void) {}
std::shared_ptr<FrameStream2I<FrameType>> reader;
std::shared_ptr<FrameStream2I<FrameType>> writer;
const void* getReaderStream(void) override { return reader.get(); }
const void* getWriterStream(void) override { return writer.get(); }
};
auto our_data = std::make_unique<inlineData>();

View File

@ -56,6 +56,16 @@ struct ToxAVCallAudioSink : public FrameStream2SinkI<AudioFrame2> {
}
}
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<FrameStream2I<AudioFrame2>> subscribe(void) override {
if (_writer) {
@ -116,6 +126,16 @@ struct ToxAVCallVideoSink : public FrameStream2SinkI<SDLVideoFrame> {
}
}
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<FrameStream2I<SDLVideoFrame>> subscribe(void) override {
if (_writer) {
@ -187,6 +207,7 @@ void ToxAVVoIPModel::addAudioSink(ObjectHandle session, uint32_t friend_number)
auto new_asink = std::make_unique<ToxAVCallAudioSink>(_av, friend_number);
auto* new_asink_ptr = new_asink.get();
outgoing_audio.emplace<Components::Bitrate>(new_asink_ptr->_audio_bitrate);
outgoing_audio.emplace<ToxAVCallAudioSink*>(new_asink_ptr);
outgoing_audio.emplace<Components::FrameStream2Sink<AudioFrame2>>(std::move(new_asink));
outgoing_audio.emplace<Components::StreamSink>(Components::StreamSink::create<AudioFrame2>("ToxAV Friend Call Outgoing Audio"));
@ -240,6 +261,7 @@ void ToxAVVoIPModel::addVideoSink(ObjectHandle session, uint32_t friend_number)
auto new_vsink = std::make_unique<ToxAVCallVideoSink>(_av, friend_number);
auto* new_vsink_ptr = new_vsink.get();
outgoing_video.emplace<Components::Bitrate>(new_vsink_ptr->_video_bitrate);
outgoing_video.emplace<ToxAVCallVideoSink*>(new_vsink_ptr);
outgoing_video.emplace<Components::FrameStream2Sink<SDLVideoFrame>>(std::move(new_vsink));
outgoing_video.emplace<Components::StreamSink>(Components::StreamSink::create<SDLVideoFrame>("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<ToxAVCallVideoSink*>()) {
auto& rate = e.e.get_or_emplace<Components::Bitrate>().rate;
if (rate <= 0) {
rate = 0;
}
if (!e.e.get<ToxAVCallVideoSink*>()->setBitrate(rate)) {
rate = e.e.get<ToxAVCallVideoSink*>()->_video_bitrate;
}
} else if (e.e.all_of<ToxAVCallAudioSink*>()) {
auto& rate = e.e.get_or_emplace<Components::Bitrate>().rate;
if (rate <= 0) {
rate = 0;
}
if (!e.e.get<ToxAVCallAudioSink*>()->setBitrate(rate)) {
rate = e.e.get<ToxAVCallAudioSink*>()->_audio_bitrate;
}
}
return false;
}
bool ToxAVVoIPModel::onEvent(const ObjectStore::Events::ObjectDestory&) {
return false;
}

View File

@ -1,6 +1,6 @@
#pragma once
#include <solanaceae/object_store/fwd.hpp>
#include <solanaceae/object_store/object_store.hpp>
#include <solanaceae/contact/contact_store_i.hpp>
#include <solanaceae/tox_contacts/tox_contact_model2.hpp>
#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;
};