diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/QuickMedia.cpp | 84 | ||||
-rw-r--r-- | src/VideoPlayer.cpp | 121 |
2 files changed, 143 insertions, 62 deletions
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index ba9abf7..e80fde6 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -391,43 +391,13 @@ namespace QuickMedia { video_player_ui_window->setVerticalSyncEnabled(true); }; - // This variable is needed because calling play_video is not possible in onPlaybackEndedCallback - bool play_next_video = false; bool ui_resize = true; + bool seekable = false; std::unique_ptr<VideoPlayer> video_player; - auto play_video = [this, &video_player, &play_next_video, &on_window_create, &video_player_ui_window, &ui_resize]() { + auto load_video_error_check = [this, &video_player]() { watched_videos.insert(content_url); - video_player = std::make_unique<VideoPlayer>([this, &play_next_video, &video_player_ui_window, &ui_resize](const char *event_name) { - if(strcmp(event_name, "end-file") == 0) { - video_player_ui_window = nullptr; - ui_resize = true; - - std::string new_video_url; - BodyItems related_media = current_plugin->get_related_media(content_url); - // Find video that hasn't been played before in this video session - for(auto it = related_media.begin(), end = related_media.end(); it != end; ++it) { - if(watched_videos.find((*it)->url) == watched_videos.end()) { - new_video_url = (*it)->url; - break; - } - } - - // If there are no videos to play, then dont play any... - if(new_video_url.empty()) { - show_notification("Video player", "No more related videos to play"); - current_page = Page::SEARCH_SUGGESTION; - return; - } - - content_url = std::move(new_video_url); - play_next_video = true; - // TODO: This doesn't seem to work correctly right now, it causes video to become black when changing video (context reset bug). - //video_player->load_file(video_url); - } - }, on_window_create); - VideoPlayer::Error err = video_player->load_video(content_url.c_str(), window.getSystemHandle()); if(err != VideoPlayer::Error::OK) { std::string err_msg = "Failed to play url: "; @@ -436,7 +406,41 @@ namespace QuickMedia { current_page = Page::SEARCH_SUGGESTION; } }; - play_video(); + + video_player = std::make_unique<VideoPlayer>([this, &video_player, &seekable, &load_video_error_check](const char *event_name) { + bool end_of_file = false; + if(strcmp(event_name, "pause") == 0) { + double time_remaining = 0.0; + if(video_player->get_time_remaining(&time_remaining) == VideoPlayer::Error::OK && time_remaining < 0.1) + end_of_file = true; + } else if(strcmp(event_name, "playback-restart") == 0) { + video_player->set_paused(false); + video_player->is_seekable(&seekable); + } + + if(end_of_file) { + std::string new_video_url; + BodyItems related_media = current_plugin->get_related_media(content_url); + // Find video that hasn't been played before in this video session + for(auto it = related_media.begin(), end = related_media.end(); it != end; ++it) { + if(watched_videos.find((*it)->url) == watched_videos.end()) { + new_video_url = (*it)->url; + break; + } + } + + // If there are no videos to play, then dont play any... + if(new_video_url.empty()) { + show_notification("Video player", "No more related videos to play"); + current_page = Page::SEARCH_SUGGESTION; + return; + } + + content_url = std::move(new_video_url); + load_video_error_check(); + } + }, on_window_create); + load_video_error_check(); auto on_doubleclick = []() { // TODO: Toggle fullscreen of video here @@ -444,7 +448,7 @@ namespace QuickMedia { sf::Clock ui_hide_timer; bool ui_visible = true; - const int UI_HIDE_TIMEOUT = 3000; + const int UI_HIDE_TIMEOUT = 4500; sf::Clock time_since_last_left_click; int left_click_counter; @@ -461,11 +465,6 @@ namespace QuickMedia { window.display(); while (current_page == Page::VIDEO_CONTENT) { - if(play_next_video) { - play_next_video = false; - play_video(); - } - while (window.pollEvent(event)) { base_event_handler(event, Page::SEARCH_SUGGESTION); if(event.type == sf::Event::Resized) { @@ -517,7 +516,7 @@ namespace QuickMedia { //window.clear(); //window.display(); - if(get_progress_timer.getElapsedTime().asMilliseconds() >= 500) { + if(video_player->is_connected() && get_progress_timer.getElapsedTime().asMilliseconds() >= 500) { get_progress_timer.restart(); video_player->get_progress(&progress); } @@ -549,7 +548,10 @@ namespace QuickMedia { if(sf::Mouse::isButtonPressed(sf::Mouse::Left)) { auto mouse_pos = sf::Mouse::getPosition(window); if(mouse_pos.y >= window_size.y - ui_height) { - video_player->set_progress((double)mouse_pos.x / (double)window_size.x); + if(seekable) + video_player->set_progress((double)mouse_pos.x / (double)window_size.x); + else + fprintf(stderr, "Video is not seekable!\n"); // TODO: Show this to the user } } } else { diff --git a/src/VideoPlayer.cpp b/src/VideoPlayer.cpp index 96b9ee2..79322b6 100644 --- a/src/VideoPlayer.cpp +++ b/src/VideoPlayer.cpp @@ -2,6 +2,7 @@ #include "../include/Program.h" #include <string> #include <json/reader.h> +#include <json/writer.h> #include <memory> #include <assert.h> @@ -30,7 +31,8 @@ namespace QuickMedia { display(nullptr), request_id(1), expected_request_id(0), - request_response_data(Json::nullValue) + request_response_data(Json::nullValue), + response_data_status(ResponseDataStatus::NONE) { display = XOpenDisplay(NULL); if (!display) @@ -62,9 +64,11 @@ namespace QuickMedia { } const std::string parent_window_str = std::to_string(parent_window); - const char *args[] = { "mpv", /*"--keep-open=yes", "--keep-open-pause=no",*/ "--input-ipc-server", ipc_server_path, + const char *args[] = { "mpv", "--keep-open=yes", /*"--keep-open-pause=no",*/ "--input-ipc-server", ipc_server_path, "--no-config", "--no-input-default-bindings", "--input-vo-keyboard=no", "--no-input-cursor", "--cache-secs=120", "--demuxer-max-bytes=40M", "--demuxer-max-back-bytes=20M", + "--no-input-terminal", + "--no-osc", /*"--vo=gpu", "--hwdec=auto",*/ "--wid", parent_window_str.c_str(), "--", path, nullptr }; if(exec_program_async(args, &video_process_id) != 0) @@ -93,9 +97,17 @@ namespace QuickMedia { if(video_process_id == -1) return launch_video_process(path, _parent_window); - std::string cmd = "loadfile "; - cmd += path; - return send_command(cmd.c_str(), cmd.size()); + Json::Value command_data(Json::arrayValue); + command_data.append("loadfile"); + command_data.append(path); + Json::Value command(Json::objectValue); + command["command"] = command_data; + + Json::StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = ""; + const std::string cmd_str = Json::writeString(builder, command) + "\n"; + return send_command(cmd_str.c_str(), cmd_str.size()); } static std::vector<Window> get_child_window(Display *display, Window window) { @@ -187,9 +199,16 @@ namespace QuickMedia { if(json_reader->parse(buffer + start, buffer + i, &json_root, &json_errors)) { const Json::Value &event = json_root["event"]; const Json::Value &request_id_json = json_root["request_id"]; - if(event.isString() && event_callback) - event_callback(event.asCString()); - else if(expected_request_id != 0 && request_id_json.isNumeric() && request_id_json.asUInt() == expected_request_id) { + if(event.isString()) { + if(event_callback) + event_callback(event.asCString()); + } + + if(expected_request_id != 0 && request_id_json.isNumeric() && request_id_json.asUInt() == expected_request_id) { + if(json_root["error"].isNull()) + response_data_status = ResponseDataStatus::ERROR; + else + response_data_status = ResponseDataStatus::OK; request_response_data = json_root["data"]; } } else { @@ -208,15 +227,60 @@ namespace QuickMedia { } VideoPlayer::Error VideoPlayer::get_progress(double *result) { + Json::Value percent_pos_json; + Error err = get_property("percent-pos", &percent_pos_json, Json::realValue); + if(err != Error::OK) + return err; + + *result = percent_pos_json.asDouble() * 0.01; + return err; + } + + VideoPlayer::Error VideoPlayer::get_time_remaining(double *result) { + Json::Value time_remaining_json; + Error err = get_property("time-remaining", &time_remaining_json, Json::realValue); + if(err != Error::OK) + return err; + + *result = time_remaining_json.asDouble(); + return err; + } + + VideoPlayer::Error VideoPlayer::set_property(const std::string &property_name, const Json::Value &value) { + Json::Value command_data(Json::arrayValue); + command_data.append("set_property"); + command_data.append(property_name); + command_data.append(value); + Json::Value command(Json::objectValue); + command["command"] = command_data; + + Json::StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = ""; + const std::string cmd_str = Json::writeString(builder, command) + "\n"; + return send_command(cmd_str.c_str(), cmd_str.size()); + } + + VideoPlayer::Error VideoPlayer::get_property(const std::string &property_name, Json::Value *result, Json::ValueType result_type) { unsigned int cmd_request_id = request_id; ++request_id; // Overflow check. 0 is defined as no request, 1 is the first valid one if(request_id == 0) request_id = 1; - std::string cmd = "{ \"command\": [\"get_property\", \"percent-pos\"], \"request_id\": "; - cmd += std::to_string(cmd_request_id) + " }\n"; - Error err = send_command(cmd.c_str(), cmd.size()); + Json::Value command_data(Json::arrayValue); + command_data.append("get_property"); + command_data.append(property_name); + Json::Value command(Json::objectValue); + command["command"] = command_data; + command["request_id"] = cmd_request_id; + + Json::StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = ""; + const std::string cmd_str = Json::writeString(builder, command) + "\n"; + + Error err = send_command(cmd_str.c_str(), cmd_str.size()); if(err != Error::OK) return err; @@ -227,35 +291,50 @@ namespace QuickMedia { if(err != Error::OK) goto cleanup; - if(!request_response_data.isNull()) + if(response_data_status != ResponseDataStatus::NONE) break; } while(read_timer.getElapsedTime().asMilliseconds() < READ_TIMEOUT_MS); - if(request_response_data.isNull()) { - err = Error::READ_TIMEOUT; + if(response_data_status == ResponseDataStatus::OK) { + if(request_response_data.type() == result_type) + *result = request_response_data; + else + err = Error::READ_INCORRECT_TYPE; + } else if(response_data_status == ResponseDataStatus::ERROR) { + err = Error::READ_RESPONSE_ERROR; goto cleanup; - } - - if(!request_response_data.isDouble()) { - fprintf(stderr, "Read: expected to receive data of type double for request id %u, was type %s\n", cmd_request_id, "TODO: Fill type here"); - err = Error::READ_INCORRECT_TYPE; + } else { + err = Error::READ_TIMEOUT; goto cleanup; } - *result = request_response_data.asDouble() * 0.01; - cleanup: expected_request_id = 0; + response_data_status = ResponseDataStatus::NONE; request_response_data = Json::Value(Json::nullValue); return err; } + VideoPlayer::Error VideoPlayer::set_paused(bool paused) { + return set_property("pause", paused); + } + VideoPlayer::Error VideoPlayer::set_progress(double progress) { std::string cmd = "{ \"command\": [\"set_property\", \"percent-pos\", "; cmd += std::to_string(progress * 100.0) + "] }"; return send_command(cmd.c_str(), cmd.size()); } + VideoPlayer::Error VideoPlayer::is_seekable(bool *result) { + Json::Value seekable_json; + Error err = get_property("seekable", &seekable_json, Json::booleanValue); + if(err != Error::OK) + return err; + + *result = seekable_json.asBool(); + return err; + } + VideoPlayer::Error VideoPlayer::send_command(const char *cmd, size_t size) { if(!connected_to_ipc) return Error::FAIL_NOT_CONNECTED; |