aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-08-08 22:12:09 +0200
committerdec05eba <dec05eba@protonmail.com>2019-08-08 22:12:12 +0200
commitf26534ca8d7107b14fdd5a02cbadd56505d159de (patch)
treee8f91f264126665a175709046bd92c9b0973323a /src
parentc9c2621accb68634685a14703491cacdd7ed2bb1 (diff)
Switch from libmpv to mpv process with window embed
Diffstat (limited to 'src')
-rw-r--r--src/Program.c60
-rw-r--r--src/QuickMedia.cpp171
-rw-r--r--src/VideoPlayer.cpp357
-rw-r--r--src/plugins/Manganelo.cpp2
-rw-r--r--src/plugins/Plugin.cpp4
-rw-r--r--src/plugins/Youtube.cpp3
6 files changed, 306 insertions, 291 deletions
diff --git a/src/Program.c b/src/Program.c
index 731a20e..a863fcd 100644
--- a/src/Program.c
+++ b/src/Program.c
@@ -4,6 +4,7 @@
#include <errno.h>
#include <string.h>
#include <stdio.h>
+#include <assert.h>
#define READ_END 0
#define WRITE_END 1
@@ -85,3 +86,62 @@ int exec_program(const char **args, ProgramOutputCallback output_callback, void
return result;
}
}
+
+int exec_program_async(const char **args, pid_t *result_process_id) {
+ /* 1 arguments */
+ if(args[0] == NULL)
+ return -1;
+
+ pid_t pid = fork();
+ if(pid == -1) {
+ int err = errno;
+ perror("Failed to fork");
+ return -err;
+ } else if(pid == 0) { /* child */
+ execvp(args[0], args);
+ } else { /* parent */
+ if(result_process_id)
+ *result_process_id = pid;
+ }
+ return 0;
+}
+
+#if 0
+int program_pipe_write(ProgramPipe *self, const char *data, size_t size) {
+ ssize_t bytes_written = write(self->write_fd, data, size);
+ if(bytes_written == -1) {
+ int err = errno;
+ perror("Failed to write to pipe to program");
+ return -err;
+ }
+ return 0;
+}
+
+int program_pipe_read(ProgramPipe *self, ProgramOutputCallback output_callback, void *userdata) {
+ char buffer[2048];
+
+ for(;;) {
+ ssize_t bytes_read = read(self->read_fd, buffer, sizeof(buffer) - 1);
+ if(bytes_read == 0) {
+ break;
+ } else if(bytes_read == -1) {
+ int err = errno;
+ perror("Failed to read from pipe to program");
+ return -err;
+ }
+
+ buffer[bytes_read] = '\0';
+ if(output_callback && output_callback(buffer, bytes_read, userdata) != 0)
+ break;
+ }
+
+ return 0;
+}
+
+void program_pipe_close(ProgramPipe *self) {
+ close(self->read_fd);
+ close(self->write_fd);
+ self->read_fd = -1;
+ self->write_fd = -1;
+}
+#endif
diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp
index 0a34508..cb58a1d 100644
--- a/src/QuickMedia.cpp
+++ b/src/QuickMedia.cpp
@@ -1,12 +1,13 @@
#include "../include/QuickMedia.hpp"
#include "../plugins/Manganelo.hpp"
#include "../plugins/Youtube.hpp"
-#include "../include/VideoPlayer.hpp"
#include "../include/Scale.hpp"
#include "../include/Program.h"
+#include "../include/VideoPlayer.hpp"
#include <cppcodec/base64_rfc4648.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
+#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Text.hpp>
#include <SFML/Window/Event.hpp>
#include <json/reader.h>
@@ -14,11 +15,19 @@
#include <assert.h>
#include <cmath>
#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <signal.h>
const sf::Color front_color(43, 45, 47);
const sf::Color back_color(33, 35, 37);
const int DOUBLE_CLICK_TIME = 500;
+// Prevent writing to broken pipe from exiting the program
+static void sigpipe_handler(int unused) {
+
+}
+
namespace QuickMedia {
Program::Program() :
window(sf::VideoMode(800, 600), "QuickMedia"),
@@ -35,6 +44,12 @@ namespace QuickMedia {
}
body = new Body(font);
search_bar = std::make_unique<SearchBar>(font);
+
+ struct sigaction action;
+ action.sa_handler = sigpipe_handler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = 0;
+ sigaction(SIGPIPE, &action, NULL);
}
Program::~Program() {
@@ -316,64 +331,86 @@ namespace QuickMedia {
#endif
}
+ struct XDisplayScope {
+ XDisplayScope(Display *_display) : display(_display) {
+
+ }
+
+ ~XDisplayScope() {
+ if(display)
+ XCloseDisplay(display);
+ }
+
+ Display *display;
+ };
+
void Program::video_content_page() {
search_bar->onTextUpdateCallback = nullptr;
search_bar->onTextSubmitCallback = nullptr;
+ Display* disp = XOpenDisplay(NULL);
+ if (!disp)
+ throw std::runtime_error("Failed to open display to X11 server");
+ XDisplayScope display_scope(disp);
+
+ #if 0
+ sf::RenderWindow video_window(sf::VideoMode(300, 300), "QuickMedia Video Player");
+ video_window.setVerticalSyncEnabled(true);
+ XReparentWindow(disp, video_window.getSystemHandle(), window.getSystemHandle(), 0, 0);
+ XMapWindow(disp, video_window.getSystemHandle());
+ XSync(disp, False);
+ video_window.setSize(sf::Vector2u(400, 300));
+ #endif
+
// This variable is needed because calling play_video is not possible in onPlaybackEndedCallback
bool play_next_video = false;
- auto onPlaybackEndedCallback = [this, &play_next_video]() {
- std::string new_video_url;
- std::vector<std::unique_ptr<BodyItem>> 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;
- }
- }
+ std::unique_ptr<VideoPlayer> video_player;
- // 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");
- return;
- }
+ auto play_video = [this, &video_player, &play_next_video]() {
+ printf("Playing video: %s\n", content_url.c_str());
+ watched_videos.insert(content_url);
+ video_player = std::make_unique<VideoPlayer>([this, &play_next_video](const char *event_name) {
+ if(strcmp(event_name, "end-file") == 0) {
+ std::string new_video_url;
+ std::vector<std::unique_ptr<BodyItem>> 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;
+ }
+ }
- 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);
- };
+ // 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;
+ }
- std::unique_ptr<VideoPlayer> video_player = nullptr;
- auto play_video = [this, &video_player, &onPlaybackEndedCallback]() {
- watched_videos.insert(content_url);
- try {
- printf("Play video: %s\n", content_url.c_str());
- video_player.reset(new VideoPlayer(&window, window_size.x, window_size.y, content_url.c_str()));
- video_player->onPlaybackEndedCallback = onPlaybackEndedCallback;
- } catch(VideoInitializationException &e) {
- show_notification("Video player", "Failed to create video player", Urgency::CRITICAL);
- video_player = nullptr;
+ 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);
+ }
+ });
+
+ 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: ";
+ err_msg += content_url;
+ show_notification("Video player", err_msg.c_str(), Urgency::CRITICAL);
+ current_page = Page::SEARCH_SUGGESTION;
}
};
play_video();
sf::Clock time_since_last_left_click;
int left_click_counter;
- bool video_is_fullscreen = false;
sf::Event event;
- auto on_doubleclick = [this, &video_is_fullscreen]() {
- if(video_is_fullscreen) {
- window.create(sf::VideoMode::getDesktopMode(), "QuickMedia", sf::Style::Default);
- } else {
- window.create(sf::VideoMode::getDesktopMode(), "QuickMedia", sf::Style::Fullscreen);
- }
- window.setVerticalSyncEnabled(true);
- video_is_fullscreen = !video_is_fullscreen;
- };
+ sf::RectangleShape rect(sf::Vector2f(500, 500));
while (current_page == Page::VIDEO_CONTENT) {
if(play_next_video) {
@@ -383,33 +420,61 @@ namespace QuickMedia {
while (window.pollEvent(event)) {
base_event_handler(event, Page::SEARCH_SUGGESTION);
+ if(event.type == sf::Event::Resized) {
+ //video_window.setSize(sf::Vector2u(event.size.width, event.size.height));
+ } else if(event.key.code == sf::Keyboard::Space) {
+ if(video_player->toggle_pause() != VideoPlayer::Error::OK) {
+ fprintf(stderr, "Failed to toggle pause!\n");
+ }
+ }
+ }
+
+ #if 0
+ while(video_window.pollEvent(event)) {
+ if (event.type == sf::Event::Closed) {
+ current_page = Page::EXIT;
+ } else if(event.type == sf::Event::Resized) {
+ sf::FloatRect visible_area(0, 0, event.size.width, event.size.height);
+ video_window.setView(sf::View(visible_area));
+ } else if(event.type == sf::Event::KeyPressed) {
+ if(event.key.code == sf::Keyboard::Escape) {
+ current_page = Page::SEARCH_SUGGESTION;
+ return;
+ }
+
+ if(event.key.code == sf::Keyboard::Space) {
+ if(video_player.toggle_pause() != VideoPlayer::Error::OK) {
+ fprintf(stderr, "Failed to toggle pause!\n");
+ }
+ }
+ }
+
if(event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left) {
if(time_since_last_left_click.restart().asMilliseconds() <= DOUBLE_CLICK_TIME) {
if(++left_click_counter == 2) {
- on_doubleclick();
+ //on_doubleclick();
left_click_counter = 0;
}
} else {
left_click_counter = 1;
}
}
+ }
+ #endif
- if(video_player) {
- if(event.type == sf::Event::Resized)
- video_player->resize(window_size);
- video_player->handle_event(event);
- }
+ VideoPlayer::Error update_err = video_player->update();
+ if(update_err == VideoPlayer::Error::FAIL_TO_CONNECT_TIMEOUT) {
+ show_notification("Video player", "Failed to connect to mpv ipc after 5 seconds", Urgency::CRITICAL);
+ current_page = Page::SEARCH_SUGGESTION;
+ return;
}
window.clear();
- if(video_player)
- video_player->draw(window);
window.display();
- }
- if(video_is_fullscreen) {
- window.create(sf::VideoMode::getDesktopMode(), "QuickMedia", sf::Style::Default);
- window.setVerticalSyncEnabled(true);
+ // TODO: Show loading video animation
+ //video_window.clear(sf::Color::Red);
+ //video_window.display();
}
}
diff --git a/src/VideoPlayer.cpp b/src/VideoPlayer.cpp
index 4c19c61..e0e69df 100644
--- a/src/VideoPlayer.cpp
+++ b/src/VideoPlayer.cpp
@@ -1,275 +1,164 @@
#include "../include/VideoPlayer.hpp"
-#include "../include/Scale.hpp"
-#include <SFML/Window/Mouse.hpp>
-#include <mpv/client.h>
-#include <mpv/render_gl.h>
-#include <SFML/OpenGL.hpp>
-#include <clocale>
-#include <cmath>
+#include "../include/Program.h"
+#include <string>
+#include <json/reader.h>
+#include <assert.h>
-const int UI_VISIBLE_TIMEOUT_MS = 2500;
-const auto pause_key = sf::Keyboard::Space;
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
-namespace QuickMedia {
- static void* getProcAddressMpv(void *funcContext, const char *name) {
- return (void*)sf::Context::getFunction(name);
- }
-
- static void onMpvRedraw(void *ctx) {
- VideoPlayer *video_player = (VideoPlayer*)ctx;
- video_player->redraw = true;
- }
+const int RETRY_TIME_MS = 1000;
+const int MAX_RETRIES = 5;
- static void on_mpv_events(void *ctx) {
- VideoPlayer *video_player = (VideoPlayer*)ctx;
- video_player->event_update = true;
+namespace QuickMedia {
+ VideoPlayer::VideoPlayer(EventCallbackFunc _event_callback) :
+ video_process_id(-1),
+ ipc_socket(-1),
+ connected_to_ipc(false),
+ connect_tries(0),
+ event_callback(_event_callback),
+ alive(true)
+ {
+
}
- static void check_error(int status) {
- if(status < 0)
- fprintf(stderr, "mpv api error: %s\n", mpv_error_string(status));
- }
+ VideoPlayer::~VideoPlayer() {
+ if(video_process_id != -1)
+ kill(video_process_id, SIGTERM);
- static void mpv_set_option_bool(mpv_handle *mpv, const char *option, bool value) {
- int int_value = value;
- check_error(mpv_set_option(mpv, option, MPV_FORMAT_FLAG, &int_value));
- }
+ if(ipc_socket != -1)
+ close(ipc_socket);
+
+ if(video_process_id != -1)
+ remove(ipc_server_path);
- static void mpv_set_option_int64(mpv_handle *mpv, const char *option, int64_t value) {
- check_error(mpv_set_option(mpv, option, MPV_FORMAT_INT64, &value));
+ alive = false;
+ if(event_read_thread.joinable())
+ event_read_thread.join();
}
- class ContextScope {
- public:
- ContextScope(sf::Context *_context) : context(_context) {
- context->setActive(true);
- }
-
- ~ContextScope() {
- context->setActive(false);
+ VideoPlayer::Error VideoPlayer::launch_video_process(const char *path, sf::WindowHandle parent_window) {
+ if(!tmpnam(ipc_server_path)) {
+ perror("Failed to generate ipc file name");
+ return Error::FAIL_TO_GENERATE_IPC_FILENAME;
}
- sf::Context *context;
- };
-
- VideoPlayer::VideoPlayer(sf::RenderWindow *_window, unsigned int width, unsigned int height, const char *file, bool loop) :
- redraw(false),
- event_update(false),
- onPlaybackEndedCallback(nullptr),
- mpv(nullptr),
- mpvGl(nullptr),
- context(nullptr),
- textureBuffer(nullptr),
- desired_size(width, height)
- {
- //ContextScope context_scope(context.get());
- texture.setSmooth(true);
-
- // mpv_create requires LC_NUMERIC to be set to "C" for some reason, see mpv_create documentation
- std::setlocale(LC_NUMERIC, "C");
- mpv = mpv_create();
- if(!mpv)
- throw VideoInitializationException("Failed to create mpv handle");
-
- //check_error(mpv_set_option_string(mpv, "input-default-bindings", "yes"));
- //check_error(mpv_set_option_string(mpv, "input-vo-keyboard", "yes"));
- check_error(mpv_set_option_string(mpv, "cache-secs", "120"));
- check_error(mpv_set_option_string(mpv, "demuxer-max-bytes", "20M"));
- check_error(mpv_set_option_string(mpv, "demuxer-max-back-bytes", "10M"));
+ 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,
+ "--no-config", "--no-input-default-bindings", "--input-vo-keyboard=no", "--no-input-cursor",
+ "--cache-secs=120", "--demuxer-max-bytes=20M", "--demuxer-max-back-bytes=10M",
+ "--vo=gpu", "--hwdec=auto",
+ "--wid", parent_window_str.c_str(), "--", path, nullptr };
+ if(exec_program_async(args, &video_process_id) != 0)
+ return Error::FAIL_TO_LAUNCH_PROCESS;
- //mpv_set_option_bool(mpv, "osc", true);
- //mpv_set_option_int64(mpv, "wid", window.getSystemHandle());
-
- if(mpv_initialize(mpv) < 0)
- throw VideoInitializationException("Failed to initialize mpv");
+ printf("mpv input ipc server: %s\n", ipc_server_path);
- // TODO: Enabling vo=gpu will make mpv create its own window, or take over the QuickMedia window fully
- // if "wid" option is used. To take advantage of vo=gpu, QuickMedia should create a parent window
- // and make mpv use that and then embed that into the parent QuickMedia window.
- // This will also remove the need for rendering with sfml (no need for texture copy!).
- //check_error(mpv_set_option_string(mpv, "vo", "gpu"));
- //check_error(mpv_set_option_string(mpv, "hwdec", "auto"));
+ if((ipc_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ perror("Failed to create socket for video player");
+ return Error::FAIL_TO_CREATE_SOCKET;
+ }
- //check_error(mpv_set_option_string(mpv, "terminal", "yes"));
- //check_error(mpv_set_option_string(mpv, "msg-level", "all=v"));
+ ipc_addr.sun_family = AF_UNIX;
+ strcpy(ipc_addr.sun_path, ipc_server_path);
- if(loop)
- check_error(mpv_set_option_string(mpv, "loop", "inf"));
+ int flags = fcntl(ipc_socket, F_GETFL, 0);
+ fcntl(ipc_socket, F_SETFL, flags | O_NONBLOCK);
- //mpvGl = (mpv_opengl_cb_context*)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB);
- //if(!mpvGl)
- // throw VideoInitializationException("Failed to initialize mpv opengl render context");
+ return Error::OK;
+ }
- mpv_opengl_init_params opengl_init_params;
- opengl_init_params.get_proc_address = getProcAddressMpv;
- opengl_init_params.get_proc_address_ctx = nullptr;
- opengl_init_params.extra_exts = nullptr;
- mpv_render_param params[] = {
- { MPV_RENDER_PARAM_API_TYPE, (void*)MPV_RENDER_API_TYPE_OPENGL },
- { MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &opengl_init_params },
- { (mpv_render_param_type)0, nullptr }
- };
+ VideoPlayer::Error VideoPlayer::load_video(const char *path, sf::WindowHandle parent_window) {
+ if(video_process_id == -1)
+ return launch_video_process(path, parent_window);
- if (mpv_render_context_create(&mpvGl, mpv, params) < 0)
- throw VideoInitializationException("failed to initialize mpv GL context");
-
- mpv_render_context_set_update_callback(mpvGl, onMpvRedraw, this);
- mpv_set_wakeup_callback(mpv, on_mpv_events, this);
-
- seekbar.setFillColor(sf::Color(255, 20, 60, 200));
- seekbar_background.setFillColor(sf::Color(33, 33, 33, 200));
- load_file(file);
+ std::string cmd = "loadfile ";
+ cmd += path;
+ return send_command(cmd.c_str(), cmd.size());
}
-
- VideoPlayer::~VideoPlayer() {
- if(mpvGl) {
- //mpv_render_context_set_update_callback(mpvGl, nullptr, nullptr);
- mpv_render_context_free(mpvGl);
- }
- free(textureBuffer);
+ VideoPlayer::Error VideoPlayer::update() {
+ if(ipc_socket == -1)
+ return Error::INIT_FAILED;
- if(mpv) {
- //mpv_set_wakeup_callback(mpv, nullptr, nullptr);
- mpv_destroy(mpv);
- //mpv_terminate_destroy(mpv);
- }
- }
+ if(connect_tries == MAX_RETRIES)
+ return Error::FAIL_TO_CONNECT_TIMEOUT;
- void VideoPlayer::handle_event(sf::Event &event) {
- if(event.type == sf::Event::MouseMoved) {
- cursor_last_active_timer.restart();
- } else if(event.type == sf::Event::KeyPressed) {
- if(event.key.code == pause_key) {
- mpv_command_string(mpv, "cycle pause");
+ if(!connected_to_ipc && ipc_connect_retry_timer.getElapsedTime().asMilliseconds() >= RETRY_TIME_MS) {
+ if(connect(ipc_socket, (struct sockaddr*)&ipc_addr, sizeof(ipc_addr)) == -1) {
+ ++connect_tries;
+ if(connect_tries == MAX_RETRIES) {
+ fprintf(stderr, "Failed to connect to mpv ipc after 5 seconds, last error: %s\n", strerror(errno));
+ return Error::FAIL_TO_CONNECT_TIMEOUT;
+ }
+ } else {
+ connected_to_ipc = true;
+ if(event_callback)
+ event_read_thread = std::thread(&VideoPlayer::read_ipc_func, this);
}
}
- }
-
- void VideoPlayer::setPosition(float x, float y) {
- sprite.setPosition(x, y);
- }
- // TODO: Make this work in the future when video size and sprite texture size wont be the same
- void VideoPlayer::resize(const sf::Vector2f &size) {
- desired_size = sf::Vector2f(size.x, size.y);
- if(!textureBuffer)
- return;
-
- sf::Vector2f video_size_f(video_size.x, video_size.y);
- auto video_scale = get_ratio(video_size_f, wrap_to_size(video_size_f, desired_size));
- sprite.setScale(video_scale);
-
- auto image_size = video_size_f;
- image_size.x *= video_scale.x;
- image_size.y *= video_scale.y;
- sprite.setPosition(std::floor(desired_size.x * 0.5f - image_size.x * 0.5f), std::floor(desired_size.y * 0.5f - image_size.y * 0.5f));
+ return Error::OK;
}
- void VideoPlayer::handle_mpv_events() {
- while(true) {
- mpv_event *mpvEvent = mpv_wait_event(mpv, 0.0);
- if(mpvEvent->event_id == MPV_EVENT_NONE)
+ void VideoPlayer::read_ipc_func() {
+ assert(connected_to_ipc);
+ assert(event_callback);
+
+ Json::Value json_root;
+ Json::CharReaderBuilder json_builder;
+ std::unique_ptr<Json::CharReader> json_reader(json_builder.newCharReader());
+ std::string json_errors;
+
+ char buffer[2048];
+ while(alive) {
+ ssize_t bytes_read = read(ipc_socket, buffer, sizeof(buffer));
+ if(bytes_read == -1) {
+ int err = errno;
+ if(err == EAGAIN) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ continue;
+ }
+
+ fprintf(stderr, "Failed to read from ipc socket, error: %s\n", strerror(err));
break;
- else if(mpvEvent->event_id == MPV_EVENT_SHUTDOWN)
- return;
- else if(mpvEvent->event_id == MPV_EVENT_VIDEO_RECONFIG) {
- int64_t w, h;
- if (mpv_get_property(mpv, "dwidth", MPV_FORMAT_INT64, &w) >= 0 &&
- mpv_get_property(mpv, "dheight", MPV_FORMAT_INT64, &h) >= 0 &&
- w > 0 && h > 0 && (w != video_size.x || h != video_size.y))
- {
- video_size.x = w;
- video_size.y = h;
- context.reset(new sf::Context(sf::ContextSettings(), w, h));
- context->setActive(true);
- // TODO: Verify if it's valid to re-create the texture like this,
- // instead of using deconstructor
- if(texture.create(w, h)) {
- void *newTextureBuf = realloc(textureBuffer, w * h * 4);
- if(newTextureBuf)
- textureBuffer = (sf::Uint8*)newTextureBuf;
+ } else if(bytes_read > 0) {
+ int start = 0;
+ for(int i = 0; i < bytes_read; ++i) {
+ if(buffer[i] != '\n')
+ continue;
+
+ if(json_reader->parse(buffer + start, buffer + i, &json_root, &json_errors)) {
+ const Json::Value &event = json_root["event"];
+ if(event.isString())
+ event_callback(event.asCString());
+ } else {
+ fprintf(stderr, "Failed to parse json for ipc: |%.*s|, reason: %s\n", (int)bytes_read, buffer, json_errors.c_str());
}
- glViewport(0, 0, w, h);
- context->setActive(false);
- resize(desired_size);
+
+ start = i + 1;
}
- } else if(mpvEvent->event_id == MPV_EVENT_END_FILE) {
- if(onPlaybackEndedCallback)
- onPlaybackEndedCallback();
- } else {
- //printf("Mpv event: %s\n", mpv_event_name(mpvEvent->event_id));
}
}
}
-
- void VideoPlayer::draw(sf::RenderWindow &window) {
- if(event_update.exchange(false))
- handle_mpv_events();
-
- if(textureBuffer && redraw) {
- uint64_t update_flags = mpv_render_context_update(mpvGl);
- if((update_flags & MPV_RENDER_UPDATE_FRAME) && redraw.exchange(false)) {
- auto textureSize = texture.getSize();
-
- mpv_opengl_fbo opengl_fbo;
- opengl_fbo.fbo = 0;
- opengl_fbo.w = textureSize.x;
- opengl_fbo.h = textureSize.y;
- opengl_fbo.internal_format = 0;
- mpv_render_param params[] =
- {
- { MPV_RENDER_PARAM_OPENGL_FBO, &opengl_fbo },
- { (mpv_render_param_type)0, nullptr }
- };
-
- context->setActive(true);
- mpv_render_context_render(mpvGl, params);
- // TODO: Instead of copying video to cpu buffer and then to texture, copy directly from video buffer to texture buffer
- glReadPixels(0, 0, textureSize.x, textureSize.y, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer);
- context->setActive(false);
- texture.update(textureBuffer);
- sprite.setTexture(texture, true);
- mpv_render_context_report_swap(mpvGl);
- }
- }
- window.draw(sprite);
-
- if(cursor_last_active_timer.getElapsedTime().asMilliseconds() > UI_VISIBLE_TIMEOUT_MS)
- return;
- double pos = 0.0;
- mpv_get_property(mpv, "percent-pos", MPV_FORMAT_DOUBLE, &pos);
- pos *= 0.01;
-
- auto window_size = window.getSize();
-
- const float seekbar_height = std::floor(window_size.y * 0.02f);
- const float seekbar_max_size = window_size.x;
- sf::Vector2f seekbar_size(seekbar_max_size * pos, seekbar_height);
- seekbar.setPosition(0.0f, window_size.y - seekbar_height);
- seekbar.setSize(seekbar_size);
- window.draw(seekbar);
+ VideoPlayer::Error VideoPlayer::toggle_pause() {
+ const char cmd[] = "cycle pause\n";
+ return send_command(cmd, sizeof(cmd) - 1);
+ }
- seekbar_background.setPosition(seekbar.getPosition() + sf::Vector2f(seekbar_size.x, 0.0f));
- seekbar_background.setSize(sf::Vector2f(seekbar_max_size - seekbar_size.x, seekbar_height));
- window.draw(seekbar_background);
+ VideoPlayer::Error VideoPlayer::send_command(const char *cmd, size_t size) {
+ if(!connected_to_ipc)
+ return Error::FAIL_NOT_CONNECTED;
- if(sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
- auto mouse_pos = sf::Mouse::getPosition(window);
- auto seekbar_pos = seekbar.getPosition();
- float diff_x = mouse_pos.x - seekbar_pos.x;
- if(mouse_pos.x >= seekbar_pos.x && mouse_pos.x <= seekbar_pos.x + seekbar_max_size &&
- mouse_pos.y >= seekbar_pos.y && mouse_pos.y <= seekbar_pos.y + seekbar_height)
- {
- double new_pos = ((double)diff_x / seekbar_max_size) * 100.0;
- mpv_set_property(mpv, "percent-pos", MPV_FORMAT_DOUBLE, &new_pos);
- }
+ if(send(ipc_socket, cmd, size, 0) == -1) {
+ fprintf(stderr, "Failed to send to ipc socket, error: %s, command: %.*s\n", strerror(errno), (int)size, cmd);
+ return Error::FAIL_TO_SEND;
}
- }
- void VideoPlayer::load_file(const std::string &path) {
- const char *cmd[] = { "loadfile", path.c_str(), nullptr };
- mpv_command(mpv, cmd);
+ return Error::OK;
}
}
diff --git a/src/plugins/Manganelo.cpp b/src/plugins/Manganelo.cpp
index b1f02a3..e91baf0 100644
--- a/src/plugins/Manganelo.cpp
+++ b/src/plugins/Manganelo.cpp
@@ -67,7 +67,7 @@ namespace QuickMedia {
Json::CharReaderBuilder json_builder;
std::unique_ptr<Json::CharReader> json_reader(json_builder.newCharReader());
std::string json_errors;
- if(json_reader->parse(&server_response.front(), &server_response.back(), &json_root, &json_errors)) {
+ if(!json_reader->parse(&server_response[0], &server_response[server_response.size()], &json_root, &json_errors)) {
fprintf(stderr, "Manganelo suggestions json error: %s\n", json_errors.c_str());
return SuggestionResult::ERR;
}
diff --git a/src/plugins/Plugin.cpp b/src/plugins/Plugin.cpp
index 86f5d7d..5d81aad 100644
--- a/src/plugins/Plugin.cpp
+++ b/src/plugins/Plugin.cpp
@@ -28,11 +28,13 @@ namespace QuickMedia {
}
DownloadResult download_to_string(const std::string &url, std::string &result, const std::vector<CommandArg> &additional_args) {
- std::vector<const char*> args = { "curl", "-H", "Accept-Language: en-US,en;q=0.5", "--compressed", "-s", "-L", url.c_str() };
+ std::vector<const char*> args = { "curl", "-H", "Accept-Language: en-US,en;q=0.5", "--compressed", "-s", "-L" };
for(const CommandArg &arg : additional_args) {
args.push_back(arg.option.c_str());
args.push_back(arg.value.c_str());
}
+ args.push_back("--");
+ args.push_back(url.c_str());
args.push_back(nullptr);
if(exec_program(args.data(), accumulate_string, &result) != 0)
return DownloadResult::NET_ERR;
diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp
index a5670ec..2a3f011 100644
--- a/src/plugins/Youtube.cpp
+++ b/src/plugins/Youtube.cpp
@@ -48,13 +48,12 @@ namespace QuickMedia {
if(json_end == 0 || json_start >= json_end)
return SuggestionResult::ERR;
- --json_end;
Json::Value json_root;
Json::CharReaderBuilder json_builder;
std::unique_ptr<Json::CharReader> json_reader(json_builder.newCharReader());
std::string json_errors;
- if(json_reader->parse(&server_response[json_start], &server_response[json_end], &json_root, &json_errors)) {
+ if(!json_reader->parse(&server_response[json_start], &server_response[json_end], &json_root, &json_errors)) {
fprintf(stderr, "Youtube suggestions json error: %s\n", json_errors.c_str());
return SuggestionResult::ERR;
}