diff --git a/src/gui/render.cpp b/src/gui/render.cpp index 28a84a7..18dbaff 100644 --- a/src/gui/render.cpp +++ b/src/gui/render.cpp @@ -1737,13 +1737,19 @@ void Render::CleanupFactories() { void Render::CleanupPeer(std::shared_ptr props) { SDL_FlushEvent(STREAM_REFRESH_EVENT); - if (props->dst_buffer_) { - size_t buffer_size = props->dst_buffer_capacity_; - std::vector buffer_copy(buffer_size); - memcpy(buffer_copy.data(), props->dst_buffer_, buffer_size); + std::shared_ptr> frame_snapshot; + int video_width = 0; + int video_height = 0; + { + std::lock_guard lock(props->video_frame_mutex_); + frame_snapshot = props->front_frame_; + video_width = props->video_width_; + video_height = props->video_height_; + } - int video_width = props->video_width_; - int video_height = props->video_height_; + if (frame_snapshot && !frame_snapshot->empty() && video_width > 0 && + video_height > 0) { + std::vector buffer_copy(*frame_snapshot); std::string remote_id = props->remote_id_; std::string remote_host_name = props->remote_host_name_; std::string password = @@ -1824,9 +1830,15 @@ void Render::CleanSubStreamWindowProperties( props->stream_texture_ = nullptr; } - if (props->dst_buffer_) { - delete[] props->dst_buffer_; - props->dst_buffer_ = nullptr; + { + std::lock_guard lock(props->video_frame_mutex_); + props->front_frame_.reset(); + props->back_frame_.reset(); + props->video_width_ = 0; + props->video_height_ = 0; + props->video_size_ = 0; + props->render_rect_dirty_ = true; + props->stream_cleanup_pending_ = false; } } @@ -2112,10 +2124,22 @@ void Render::ProcessSdlEvent(const SDL_Event& event) { { // std::shared_lock lock(client_properties_mutex_); for (auto& [host_name, props] : client_properties_) { - thumbnail_->SaveToThumbnail( - (char*)props->dst_buffer_, props->video_width_, - props->video_height_, host_name, props->remote_host_name_, - props->remember_password_ ? props->remote_password_ : ""); + std::shared_ptr> frame_snapshot; + int video_width = 0; + int video_height = 0; + { + std::lock_guard lock(props->video_frame_mutex_); + frame_snapshot = props->front_frame_; + video_width = props->video_width_; + video_height = props->video_height_; + } + if (frame_snapshot && !frame_snapshot->empty() && video_width > 0 && + video_height > 0) { + thumbnail_->SaveToThumbnail( + (char*)frame_snapshot->data(), video_width, video_height, + host_name, props->remote_host_name_, + props->remember_password_ ? props->remote_password_ : ""); + } if (props->peer_) { std::string client_id = (host_name == client_id_) @@ -2209,18 +2233,52 @@ void Render::ProcessSdlEvent(const SDL_Event& event) { if (!props) { break; } - if (props->video_width_ <= 0 || props->video_height_ <= 0) { + std::shared_ptr> frame_snapshot; + int video_width = 0; + int video_height = 0; + bool render_rect_dirty = false; + bool cleanup_pending = false; + { + std::lock_guard lock(props->video_frame_mutex_); + cleanup_pending = props->stream_cleanup_pending_; + if (!cleanup_pending) { + frame_snapshot = props->front_frame_; + video_width = props->video_width_; + video_height = props->video_height_; + } + render_rect_dirty = props->render_rect_dirty_; + } + + if (cleanup_pending) { + if (props->stream_texture_) { + SDL_DestroyTexture(props->stream_texture_); + props->stream_texture_ = nullptr; + } + { + std::lock_guard lock(props->video_frame_mutex_); + props->stream_cleanup_pending_ = false; + } + + if (render_rect_dirty) { + UpdateRenderRect(); + std::lock_guard lock(props->video_frame_mutex_); + props->render_rect_dirty_ = false; + } break; } - if (!props->dst_buffer_) { + + if (video_width <= 0 || video_height <= 0) { + break; + } + if (!frame_snapshot || frame_snapshot->empty()) { break; } if (props->stream_texture_) { - if (props->video_width_ != props->texture_width_ || - props->video_height_ != props->texture_height_) { - props->texture_width_ = props->video_width_; - props->texture_height_ = props->video_height_; + if (video_width != props->texture_width_ || + video_height != props->texture_height_) { + props->texture_width_ = video_width; + props->texture_height_ = video_height; SDL_DestroyTexture(props->stream_texture_); // props->stream_texture_ = SDL_CreateTexture( @@ -2245,8 +2303,8 @@ void Render::ProcessSdlEvent(const SDL_Event& event) { SDL_DestroyProperties(nvProps); } } else { - props->texture_width_ = props->video_width_; - props->texture_height_ = props->video_height_; + props->texture_width_ = video_width; + props->texture_height_ = video_height; // props->stream_texture_ = SDL_CreateTexture( // stream_renderer_, stream_pixformat_, // SDL_TEXTUREACCESS_STREAMING, props->texture_width_, @@ -2267,8 +2325,14 @@ void Render::ProcessSdlEvent(const SDL_Event& event) { SDL_DestroyProperties(nvProps); } - SDL_UpdateTexture(props->stream_texture_, NULL, props->dst_buffer_, + SDL_UpdateTexture(props->stream_texture_, NULL, frame_snapshot->data(), props->texture_width_); + + if (render_rect_dirty) { + UpdateRenderRect(); + std::lock_guard lock(props->video_frame_mutex_); + props->render_rect_dirty_ = false; + } } break; } diff --git a/src/gui/render.h b/src/gui/render.h index 5773496..c138eea 100644 --- a/src/gui/render.h +++ b/src/gui/render.h @@ -123,8 +123,13 @@ class Render { float mouse_diff_control_bar_pos_y_ = 0; double control_bar_button_pressed_time_ = 0; double net_traffic_stats_button_pressed_time_ = 0; - unsigned char* dst_buffer_ = nullptr; - size_t dst_buffer_capacity_ = 0; + // Double-buffered NV12 frame storage. Written by decode callback thread, + // consumed by SDL main thread. + std::mutex video_frame_mutex_; + std::shared_ptr> front_frame_; + std::shared_ptr> back_frame_; + bool render_rect_dirty_ = false; + bool stream_cleanup_pending_ = false; float mouse_pos_x_ = 0; float mouse_pos_y_ = 0; float mouse_pos_x_last_ = 0; diff --git a/src/gui/render_callback.cpp b/src/gui/render_callback.cpp index 20d1bd2..268d3fc 100644 --- a/src/gui/render_callback.cpp +++ b/src/gui/render_callback.cpp @@ -238,31 +238,31 @@ void Render::OnReceiveVideoBufferCb(const XVideoFrame* video_frame, render->client_properties_.find(remote_id)->second.get(); if (props->connection_established_) { - if (!props->dst_buffer_) { - props->dst_buffer_capacity_ = video_frame->size; - props->dst_buffer_ = new unsigned char[video_frame->size]; - } + { + std::lock_guard lock(props->video_frame_mutex_); - if (props->dst_buffer_capacity_ < video_frame->size) { - delete props->dst_buffer_; - props->dst_buffer_capacity_ = video_frame->size; - props->dst_buffer_ = new unsigned char[video_frame->size]; - } + if (!props->back_frame_) { + props->back_frame_ = + std::make_shared>(video_frame->size); + } + if (props->back_frame_->size() != video_frame->size) { + props->back_frame_->resize(video_frame->size); + } - memcpy(props->dst_buffer_, video_frame->data, video_frame->size); - bool need_to_update_render_rect = false; - if (props->video_width_ != props->video_width_last_ || - props->video_height_ != props->video_height_last_) { - need_to_update_render_rect = true; - props->video_width_last_ = props->video_width_; - props->video_height_last_ = props->video_height_; - } - props->video_width_ = video_frame->width; - props->video_height_ = video_frame->height; - props->video_size_ = video_frame->size; + std::memcpy(props->back_frame_->data(), video_frame->data, + video_frame->size); - if (need_to_update_render_rect) { - render->UpdateRenderRect(); + const bool size_changed = (props->video_width_ != video_frame->width) || + (props->video_height_ != video_frame->height); + if (size_changed) { + props->render_rect_dirty_ = true; + } + + props->video_width_ = video_frame->width; + props->video_height_ = video_frame->height; + props->video_size_ = video_frame->size; + + props->front_frame_.swap(props->back_frame_); } SDL_Event event; @@ -440,9 +440,9 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size, const double bps = (static_cast(delta_bytes) * 8.0) / delta_seconds; if (bps > 0.0) { - const double capped = - (std::min)(bps, static_cast( - (std::numeric_limits::max)())); + const double capped = (std::min)( + bps, + static_cast((std::numeric_limits::max)())); estimated_rate_bps = static_cast(capped); } } @@ -720,12 +720,22 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id, case ConnectionStatus::Closed: { props->connection_established_ = false; props->mouse_control_button_pressed_ = false; - if (props->dst_buffer_ && props->stream_texture_) { - memset(props->dst_buffer_, 0, props->dst_buffer_capacity_); - SDL_UpdateTexture(props->stream_texture_, NULL, props->dst_buffer_, - props->texture_width_); + + { + std::lock_guard lock(props->video_frame_mutex_); + props->front_frame_.reset(); + props->back_frame_.reset(); + props->video_width_ = 0; + props->video_height_ = 0; + props->video_size_ = 0; + props->render_rect_dirty_ = true; + props->stream_cleanup_pending_ = true; } - render->CleanSubStreamWindowProperties(props); + + SDL_Event event; + event.type = render->STREAM_REFRESH_EVENT; + event.user.data1 = props; + SDL_PushEvent(&event); break; }