VideoPlayerWidget.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /*
  2. * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGUI/Action.h>
  7. #include <LibGUI/BoxLayout.h>
  8. #include <LibGUI/FilePicker.h>
  9. #include <LibGUI/ImageWidget.h>
  10. #include <LibGUI/Label.h>
  11. #include <LibGUI/MessageBox.h>
  12. #include <LibGUI/SeparatorWidget.h>
  13. #include <LibGUI/Slider.h>
  14. #include <LibGUI/Toolbar.h>
  15. #include <LibGUI/ToolbarContainer.h>
  16. #include <LibGUI/Window.h>
  17. #include "VideoPlayerWidget.h"
  18. namespace VideoPlayer {
  19. VideoPlayerWidget::VideoPlayerWidget(GUI::Window& window)
  20. : m_window(window)
  21. {
  22. set_fill_with_background_color(true);
  23. set_layout<GUI::VerticalBoxLayout>();
  24. m_video_display = add<VideoFrameWidget>();
  25. m_video_display->set_auto_resize(false);
  26. m_video_display->on_click = [&]() { toggle_pause(); };
  27. auto& player_controls_widget = add<GUI::Widget>();
  28. player_controls_widget.set_layout<GUI::VerticalBoxLayout>();
  29. player_controls_widget.set_max_height(50);
  30. m_seek_slider = player_controls_widget.add<GUI::HorizontalSlider>();
  31. m_seek_slider->set_fixed_height(20);
  32. m_seek_slider->set_enabled(false);
  33. auto& toolbar_container = player_controls_widget.add<GUI::ToolbarContainer>();
  34. m_toolbar = toolbar_container.add<GUI::Toolbar>();
  35. m_play_icon = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/play.png"sv).release_value_but_fixme_should_propagate_errors();
  36. m_pause_icon = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/pause.png"sv).release_value_but_fixme_should_propagate_errors();
  37. m_play_pause_action = GUI::Action::create("Play", { Key_Space }, m_play_icon, [&](auto&) {
  38. toggle_pause();
  39. });
  40. m_cycle_sizing_modes_action = GUI::Action::create("Sizing", [&](auto&) {
  41. cycle_sizing_modes();
  42. });
  43. m_toolbar->add_action(*m_play_pause_action);
  44. m_toolbar->add<GUI::VerticalSeparator>();
  45. m_timestamp_label = m_toolbar->add<GUI::Label>();
  46. m_timestamp_label->set_fixed_width(50);
  47. m_toolbar->add<GUI::Widget>(); // Filler widget
  48. m_toolbar->add_action(*m_cycle_sizing_modes_action);
  49. m_toolbar->add<GUI::VerticalSeparator>();
  50. m_volume_slider = m_toolbar->add<GUI::HorizontalSlider>();
  51. m_volume_slider->set_min(0);
  52. m_volume_slider->set_max(100);
  53. m_volume_slider->set_fixed_width(100);
  54. }
  55. void VideoPlayerWidget::open_file(StringView filename)
  56. {
  57. auto load_file_result = Video::PlaybackManager::from_file(this, filename);
  58. if (load_file_result.is_error()) {
  59. on_decoding_error(load_file_result.release_error());
  60. return;
  61. }
  62. m_path = filename;
  63. update_title();
  64. m_playback_manager = load_file_result.release_value();
  65. resume_playback();
  66. }
  67. void VideoPlayerWidget::update_play_pause_icon()
  68. {
  69. if (!m_playback_manager) {
  70. m_play_pause_action->set_enabled(false);
  71. m_play_pause_action->set_icon(m_play_icon);
  72. return;
  73. }
  74. m_play_pause_action->set_enabled(true);
  75. if (m_playback_manager->is_playing() || m_playback_manager->is_buffering())
  76. m_play_pause_action->set_icon(m_pause_icon);
  77. else
  78. m_play_pause_action->set_icon(m_play_icon);
  79. }
  80. void VideoPlayerWidget::resume_playback()
  81. {
  82. if (!m_playback_manager)
  83. return;
  84. m_playback_manager->resume_playback();
  85. update_play_pause_icon();
  86. }
  87. void VideoPlayerWidget::pause_playback()
  88. {
  89. if (!m_playback_manager)
  90. return;
  91. m_playback_manager->pause_playback();
  92. update_play_pause_icon();
  93. }
  94. void VideoPlayerWidget::toggle_pause()
  95. {
  96. if (!m_playback_manager)
  97. return;
  98. if (m_playback_manager->is_playing() || m_playback_manager->is_buffering())
  99. pause_playback();
  100. else
  101. resume_playback();
  102. }
  103. void VideoPlayerWidget::on_decoding_error(Video::DecoderError const& error)
  104. {
  105. StringView text_format;
  106. switch (error.category()) {
  107. case Video::DecoderErrorCategory::IO:
  108. text_format = "Error while reading video:\n{}"sv;
  109. break;
  110. case Video::DecoderErrorCategory::Memory:
  111. text_format = "Ran out of memory:\n{}"sv;
  112. break;
  113. case Video::DecoderErrorCategory::Corrupted:
  114. text_format = "Video was corrupted:\n{}"sv;
  115. break;
  116. case Video::DecoderErrorCategory::Invalid:
  117. text_format = "Invalid call:\n{}"sv;
  118. break;
  119. case Video::DecoderErrorCategory::NotImplemented:
  120. text_format = "Video feature is not yet implemented:\n{}"sv;
  121. break;
  122. default:
  123. text_format = "Unexpected error:\n{}"sv;
  124. break;
  125. }
  126. GUI::MessageBox::show(&m_window, String::formatted(text_format, error.string_literal()), "Video Player encountered an error"sv);
  127. }
  128. void VideoPlayerWidget::event(Core::Event& event)
  129. {
  130. if (event.type() == Video::EventType::DecoderErrorOccurred) {
  131. auto& error_event = static_cast<Video::DecoderErrorEvent&>(event);
  132. on_decoding_error(error_event.error());
  133. error_event.accept();
  134. } else if (event.type() == Video::EventType::VideoFramePresent) {
  135. auto& frame_event = static_cast<Video::VideoFramePresentEvent&>(event);
  136. m_video_display->set_bitmap(frame_event.frame());
  137. m_video_display->repaint();
  138. m_seek_slider->set_max(m_playback_manager->duration().to_milliseconds());
  139. m_seek_slider->set_value(m_playback_manager->current_playback_time().to_milliseconds());
  140. m_seek_slider->set_enabled(true);
  141. frame_event.accept();
  142. } else if (event.type() == Video::EventType::PlaybackStatusChange) {
  143. update_play_pause_icon();
  144. event.accept();
  145. }
  146. Widget::event(event);
  147. }
  148. void VideoPlayerWidget::cycle_sizing_modes()
  149. {
  150. auto sizing_mode = m_video_display->sizing_mode();
  151. sizing_mode = static_cast<VideoSizingMode>((to_underlying(sizing_mode) + 1) % to_underlying(VideoSizingMode::Sentinel));
  152. m_video_display->set_sizing_mode(sizing_mode);
  153. m_video_display->update();
  154. }
  155. void VideoPlayerWidget::update_title()
  156. {
  157. StringBuilder string_builder;
  158. if (m_path.is_empty()) {
  159. string_builder.append("No video"sv);
  160. } else {
  161. string_builder.append(m_path.view());
  162. }
  163. string_builder.append("[*] - Video Player"sv);
  164. window()->set_title(string_builder.to_string());
  165. }
  166. void VideoPlayerWidget::initialize_menubar(GUI::Window& window)
  167. {
  168. auto& file_menu = window.add_menu("&File");
  169. file_menu.add_action(GUI::CommonActions::make_open_action([&](auto&) {
  170. Optional<String> path = GUI::FilePicker::get_open_filepath(&window, "Open video file...");
  171. if (path.has_value())
  172. open_file(path.value());
  173. }));
  174. file_menu.add_separator();
  175. file_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) {
  176. window.close();
  177. }));
  178. auto& help_menu = window.add_menu("&Help");
  179. help_menu.add_action(GUI::CommonActions::make_about_action("Video Player", GUI::Icon::default_icon("app-video-player"sv), &window));
  180. }
  181. }