/* * Copyright (c) 2022, Gregory Bertilson * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include "VideoPlayerWidget.h" namespace VideoPlayer { VideoPlayerWidget::VideoPlayerWidget(GUI::Window& window) : m_window(window) { set_fill_with_background_color(true); set_layout(); m_video_display = add(); m_video_display->set_auto_resize(false); m_video_display->on_click = [&]() { toggle_pause(); }; auto& player_controls_widget = add(); player_controls_widget.set_layout(); player_controls_widget.set_max_height(50); m_seek_slider = player_controls_widget.add(); m_seek_slider->set_fixed_height(20); m_seek_slider->set_enabled(false); auto& toolbar_container = player_controls_widget.add(); m_toolbar = toolbar_container.add(); m_play_icon = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/play.png"sv).release_value_but_fixme_should_propagate_errors(); m_pause_icon = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/pause.png"sv).release_value_but_fixme_should_propagate_errors(); m_play_pause_action = GUI::Action::create("Play", { Key_Space }, m_play_icon, [&](auto&) { toggle_pause(); }); m_cycle_sizing_modes_action = GUI::Action::create("Sizing", [&](auto&) { cycle_sizing_modes(); }); m_toolbar->add_action(*m_play_pause_action); m_toolbar->add(); m_timestamp_label = m_toolbar->add(); m_timestamp_label->set_fixed_width(50); m_toolbar->add(); // Filler widget m_toolbar->add_action(*m_cycle_sizing_modes_action); m_toolbar->add(); m_volume_slider = m_toolbar->add(); m_volume_slider->set_min(0); m_volume_slider->set_max(100); m_volume_slider->set_fixed_width(100); } void VideoPlayerWidget::open_file(StringView filename) { auto load_file_result = Video::PlaybackManager::from_file(this, filename); if (load_file_result.is_error()) { on_decoding_error(load_file_result.release_error()); return; } m_playback_manager = load_file_result.release_value(); resume_playback(); } void VideoPlayerWidget::update_play_pause_icon() { if (!m_playback_manager) { m_play_pause_action->set_enabled(false); m_play_pause_action->set_icon(m_play_icon); return; } m_play_pause_action->set_enabled(true); if (m_playback_manager->is_playing() || m_playback_manager->is_buffering()) m_play_pause_action->set_icon(m_pause_icon); else m_play_pause_action->set_icon(m_play_icon); } void VideoPlayerWidget::resume_playback() { if (!m_playback_manager) return; m_playback_manager->resume_playback(); update_play_pause_icon(); } void VideoPlayerWidget::pause_playback() { if (!m_playback_manager) return; m_playback_manager->pause_playback(); update_play_pause_icon(); } void VideoPlayerWidget::toggle_pause() { if (!m_playback_manager) return; if (m_playback_manager->is_playing() || m_playback_manager->is_buffering()) pause_playback(); else resume_playback(); } void VideoPlayerWidget::on_decoding_error(Video::DecoderError error) { StringView text_format; switch (error.category()) { case Video::DecoderErrorCategory::IO: text_format = "Error while reading video:\n{}"sv; break; case Video::DecoderErrorCategory::Memory: text_format = "Ran out of memory:\n{}"sv; break; case Video::DecoderErrorCategory::Corrupted: text_format = "Video was corrupted:\n{}"sv; break; case Video::DecoderErrorCategory::Invalid: text_format = "Invalid call:\n{}"sv; break; case Video::DecoderErrorCategory::NotImplemented: text_format = "Video feature is not yet implemented:\n{}"sv; break; default: text_format = "Unexpected error:\n{}"sv; break; } GUI::MessageBox::show(&m_window, String::formatted(text_format, error.string_literal()), "Video Player encountered an error"sv); } void VideoPlayerWidget::event(Core::Event& event) { if (event.type() == Video::EventType::DecoderErrorOccurred) { auto& error_event = static_cast(event); on_decoding_error(error_event.error()); error_event.accept(); } else if (event.type() == Video::EventType::VideoFramePresent) { auto& frame_event = static_cast(event); m_video_display->set_bitmap(frame_event.frame()); m_video_display->repaint(); m_seek_slider->set_max(m_playback_manager->duration().to_milliseconds()); m_seek_slider->set_value(m_playback_manager->current_playback_time().to_milliseconds()); m_seek_slider->set_enabled(true); frame_event.accept(); } else if (event.type() == Video::EventType::PlaybackStatusChange) { update_play_pause_icon(); event.accept(); } Widget::event(event); } void VideoPlayerWidget::cycle_sizing_modes() { auto sizing_mode = m_video_display->sizing_mode(); sizing_mode = static_cast((to_underlying(sizing_mode) + 1) % to_underlying(VideoSizingMode::Sentinel)); m_video_display->set_sizing_mode(sizing_mode); m_video_display->update(); } }