#include "./debug_video_tap.hpp" #include #include #include #include #include "./content/sdl_video_frame_stream2.hpp" #include #include #include struct DebugVideoTapSink : public FrameStream2SinkI { std::shared_ptr> _writer; DebugVideoTapSink(void) {} ~DebugVideoTapSink(void) {} // sink std::shared_ptr> subscribe(void) override { if (_writer) { // max 1 (exclusive) return nullptr; } _writer = std::make_shared>(1, true); return _writer; } bool unsubscribe(const std::shared_ptr>& sub) override { if (!sub || !_writer) { // nah return false; } if (sub == _writer) { _writer = nullptr; return true; } // what return false; } }; DebugVideoTap::DebugVideoTap(ObjectStore2& os, StreamManager& sm, TextureUploaderI& tu) : _os(os), _sm(sm), _tu(tu) { // post self as video sink _tap = {_os.registry(), _os.registry().create()}; try { auto dvts = std::make_unique(); _tap.emplace(dvts.get()); // to get our data back _tap.emplace>( std::move(dvts) ); _tap.emplace("DebugVideoTap", std::string{entt::type_name::value()}); } catch (...) { _os.registry().destroy(_tap); } } DebugVideoTap::~DebugVideoTap(void) { if (static_cast(_tap)) { _os.registry().destroy(_tap); } } float DebugVideoTap::render(void) { if (ImGui::Begin("DebugVideoTap")) { // list sources dropdown to connect too std::string preview_label {"none"}; if (static_cast(_selected_src)) { preview_label = std::to_string(entt::to_integral(_selected_src.entity())) + " (" + _selected_src.get().name + ")"; } if (ImGui::BeginCombo("selected source", preview_label.c_str())) { if (ImGui::Selectable("none")) { switchTo({}); } for (const auto& [oc, ss] : _os.registry().view().each()) { if (ss.frame_type_name != entt::type_name::value()) { continue; } std::string label = std::to_string(entt::to_integral(oc)) + " (" + ss.name + ")"; if (ImGui::Selectable(label.c_str())) { switchTo({_os.registry(), oc}); } } ImGui::EndCombo(); } { // first pull the latest img from sink and update the texture assert(static_cast(_tap)); auto& dvtsw = _tap.get()->_writer; if (dvtsw) { while (true) { auto new_frame_opt = dvtsw->pop(); if (new_frame_opt.has_value()) { // timing if (_v_last_ts == 0) { _v_last_ts = new_frame_opt.value().timestampNS; } else { auto delta = int64_t(new_frame_opt.value().timestampNS) - int64_t(_v_last_ts); _v_last_ts = new_frame_opt.value().timestampNS; //delta = std::min(delta, 10*1000*1000); if (_v_interval_avg == 0) { _v_interval_avg = delta/1'000'000'000.f; } else { const float r = 0.2f; _v_interval_avg = _v_interval_avg * (1-r) + (delta/1'000'000'000.f) * r; } } 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 (_tex == 0 || (int)_tex_w != converted_surf->w || (int)_tex_h != converted_surf->h) { _tu.destroy(_tex); _tex = _tu.uploadRGBA( static_cast(converted_surf->pixels), converted_surf->w, converted_surf->h, TextureUploaderI::LINEAR, TextureUploaderI::STREAMING ); _tex_w = converted_surf->w; _tex_h = converted_surf->h; } else { _tu.updateRGBA(_tex, static_cast(converted_surf->pixels), converted_surf->w * converted_surf->h * 4); } SDL_UnlockSurface(converted_surf); if (new_frame_surf != converted_surf) { // clean up temp SDL_DestroySurface(converted_surf); } } else { break; } } } } // img here if (_tex != 0) { ImGui::Text("moving avg interval: %f", _v_interval_avg); const float img_w = ImGui::GetContentRegionAvail().x; ImGui::Image( reinterpret_cast(_tex), ImVec2{img_w, img_w * float(_tex_h)/_tex_w} ); } } ImGui::End(); if (_v_interval_avg != 0) { return _v_interval_avg; } else { return 2.f; } } void DebugVideoTap::switchTo(ObjectHandle o) { if (o == _selected_src) { std::cerr << "DVT: switch to same ...\n"; return; } _tu.destroy(_tex); _tex = 0; _v_last_ts = 0; _v_interval_avg = 0; if (static_cast(_selected_src)) { _sm.disconnect(_selected_src, _tap); } if (static_cast(o) && _sm.connect(o, _tap)) { _selected_src = o; } else { std::cerr << "DVT: cleared video source\n"; _selected_src = {}; } }