#include "screencast/render/renderer.h" #include #include namespace sc { namespace { // DecodedFrame pixels are AV_PIX_FMT_RGBA: memory order R, G, B, A, which // matches SDL_PIXELFORMAT_RGBA8888. constexpr SDL_PixelFormat kSdlPixelFormat = SDL_PIXELFORMAT_RGBA8888; class SdlRenderer final : public Renderer { public: explicit SdlRenderer(const RendererConfig& config) : config_(config) {} ~SdlRenderer() override { shutdown(); } SdlRenderer(const SdlRenderer&) = delete; SdlRenderer& operator=(const SdlRenderer&) = delete; const std::string& last_error() const { return last_error_; } bool initialize() { if (!SDL_Init(SDL_INIT_VIDEO)) { last_error_ = std::string{"SDL_Init failed: "} + SDL_GetError(); return false; } sdl_inited_ = true; window_ = SDL_CreateWindow(config_.window_title.c_str(), config_.initial_width, config_.initial_height, 0); if (window_ == nullptr) { last_error_ = std::string{"SDL_CreateWindow failed: "} + SDL_GetError(); shutdown(); return false; } renderer_ = SDL_CreateRenderer(window_, nullptr); if (renderer_ == nullptr) { last_error_ = std::string{"SDL_CreateRenderer failed: "} + SDL_GetError(); shutdown(); return false; } return true; } bool present(const DecodedFrame& frame) override { if (frame.width <= 0 || frame.height <= 0) { return false; } if (frame.width != texture_width_ || frame.height != texture_height_) { if (!recreate_texture(frame.width, frame.height)) { return false; } } const int pitch = frame.width * 4; if (!SDL_UpdateTexture(texture_, nullptr, frame.rgba_pixels.data(), pitch)) { return false; } if (!SDL_RenderTexture(renderer_, texture_, nullptr, nullptr)) { return false; } SDL_RenderPresent(renderer_); return true; } bool poll_events() override { if (closed_) { return false; } SDL_Event event{}; while (SDL_PollEvent(&event)) { if (event.type == SDL_EVENT_QUIT || (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window_))) { closed_ = true; return false; } } return true; } void shutdown() override { if (texture_ != nullptr) { SDL_DestroyTexture(texture_); texture_ = nullptr; } if (renderer_ != nullptr) { SDL_DestroyRenderer(renderer_); renderer_ = nullptr; } if (window_ != nullptr) { SDL_DestroyWindow(window_); window_ = nullptr; } if (sdl_inited_) { SDL_Quit(); sdl_inited_ = false; } } private: bool recreate_texture(int width, int height) { if (texture_ != nullptr) { SDL_DestroyTexture(texture_); texture_ = nullptr; } texture_ = SDL_CreateTexture(renderer_, kSdlPixelFormat, SDL_TEXTUREACCESS_STREAMING, width, height); if (texture_ == nullptr) { last_error_ = std::string{"SDL_CreateTexture failed: "} + SDL_GetError(); texture_width_ = 0; texture_height_ = 0; return false; } texture_width_ = width; texture_height_ = height; return true; } RendererConfig config_; std::string last_error_; bool sdl_inited_ = false; bool closed_ = false; SDL_Window* window_ = nullptr; SDL_Renderer* renderer_ = nullptr; SDL_Texture* texture_ = nullptr; int texture_width_ = 0; int texture_height_ = 0; }; } // namespace RendererResult> RendererFactory::create(const RendererConfig& config) { auto renderer = std::make_unique(config); if (!renderer->initialize()) { return RendererError{renderer->last_error()}; } return std::unique_ptr(std::move(renderer)); } } // namespace sc