diff options
| author | 2023-11-06 11:54:08 -0500 | |
|---|---|---|
| committer | 2023-11-06 11:54:08 -0500 | |
| commit | a48a68cfb04b2020737c0adfb0e7667451a22b5c (patch) | |
| tree | a92121897f84298c3798a4f2ba56de45cd100eab /src/fruits/cmv | |
| download | camu-a48a68cfb04b2020737c0adfb0e7667451a22b5c.tar.gz camu-a48a68cfb04b2020737c0adfb0e7667451a22b5c.tar.bz2 camu-a48a68cfb04b2020737c0adfb0e7667451a22b5c.zip | |
Add camu
- Subprojects temporarily omitted
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/fruits/cmv')
| -rw-r--r-- | src/fruits/cmv/cmv2.c | 331 | ||||
| -rw-r--r-- | src/fruits/cmv/meson.build | 26 | ||||
| -rw-r--r-- | src/fruits/cmv/tui.c | 118 | ||||
| -rw-r--r-- | src/fruits/cmv/tui.h | 21 |
4 files changed, 496 insertions, 0 deletions
diff --git a/src/fruits/cmv/cmv2.c b/src/fruits/cmv/cmv2.c new file mode 100644 index 0000000..a7b688c --- /dev/null +++ b/src/fruits/cmv/cmv2.c @@ -0,0 +1,331 @@ +#define CMV_USE_TUI 1 + +#include <al/log.h> +#include <aki/line_processor.h> + +#include "../../codec/libav/common.h" + +#include "../../mixer/audio_miniaudio.h" + +#include "../../screen/screen.h" +#include "../../render/renderer_libplacebo.h" +//#include "../../render/renderer_tiger.h" + +#include "../../libsink/sink.h" + +#include "../../shoki/src/search.h" + +#include "../cap/cap.h" + +#if CMV_USE_TUI +#include "tui.h" +#endif + +struct cmv { + s32 quit; + struct camu_screen scr; + struct camu_renderer *renderer; + struct camu_mixer mixer; + struct aki_event_loop loop; + struct camu_sink sink; + struct aki_socket socket; + struct aki_line_processor pro; + struct cap_runner cap; +#if CMV_USE_TUI + struct cmv_tui tui; + struct aki_timer timer; +#endif +}; + +static struct cmv c; +static str *default_list = al_str_c("default"); + +static u8 sink_callback(void *userdata, u8 op, u8 type, void *opaque) +{ + struct cmv *c = (struct cmv *)userdata; + switch (op) { + case CAMU_SINK_ADD_BUFFER: + switch (type) { + case CAMU_SINK_AUDIO: { + struct camu_audio_buffer *buf = (struct camu_audio_buffer *)opaque; + camu_mixer_add_buffer(&c->mixer, buf); + al_log_debug("cmv", "Audio buffer added."); + break; + } + case CAMU_SINK_VIDEO: { + struct camu_video_buffer *buf = (struct camu_video_buffer *)opaque; + camu_screen_add_buffer(&c->scr, buf); + al_log_debug("cmv", "Video buffer added."); + break; + } + } + break; + case CAMU_SINK_REMOVE_BUFFER: + switch (type) { + case CAMU_SINK_AUDIO: { + struct camu_audio_buffer *buf = (struct camu_audio_buffer *)opaque; + camu_mixer_remove_buffer(&c->mixer, buf); + al_log_debug("cmv", "Audio buffer removed."); + break; + } + case CAMU_SINK_VIDEO: { + struct camu_video_buffer *buf = (struct camu_video_buffer *)opaque; + camu_screen_remove_buffer(&c->scr, buf); + al_log_debug("cmv", "Video buffer removed."); + break; + } + } + break; + case CAMU_SINK_SWAP_BUFFER: + switch (type) { + case CAMU_SINK_AUDIO: { + struct camu_audio_buffer *buf = (struct camu_audio_buffer *)opaque; + camu_mixer_remove_buffer(&c->mixer, buf); + if (cap_list_set_completed(&c->cap, default_list)) { + al_log_debug("cmv", "Audio buffers swapped (gapless)."); + return CAMU_SINK_BUFFERS_SWAPPED; + } else { + al_log_debug("cmv", "Audio buffer removed."); + } + break; + } + } + break; + case CAMU_SINK_START: + switch (type) { + case CAMU_SINK_AUDIO: { + camu_mixer_resume(&c->mixer); + al_log_debug("cmv", "Audio started."); + break; + } + case CAMU_SINK_VIDEO: { + camu_screen_set_state(&c->scr, CAMU_SCREEN_PLAYING); + camu_screen_wake(&c->scr); + al_log_debug("cmv", "Video started."); + break; + } + } + break; + case CAMU_SINK_STOP: + switch (type) { + case CAMU_SINK_AUDIO: { + camu_mixer_pause(&c->mixer); + al_log_debug("cmv", "Audio stopped."); + break; + } + case CAMU_SINK_VIDEO: { + camu_screen_set_state(&c->scr, CAMU_SCREEN_PAUSED); + al_log_debug("cmv", "Video stopped."); + break; + } + } + break; + case CAMU_SINK_EXIT: + aki_event_loop_break(&c->loop); + camu_sink_close(&c->sink); + break; + } + return CAMU_SINK_OK; +} + +static void screen_callback(void *userdata, u8 op, f64 float0) +{ + struct cmv *c = (struct cmv *)userdata; + switch (op) { + case CAMU_SCREEN_CLOSE: + c->quit = 1; + break; + case CAMU_SCREEN_NEXT: + cap_list_skip(&c->cap, default_list, 1); + break; + case CAMU_SCREEN_PREVIOUS: + cap_list_skip(&c->cap, default_list, -1); + break; + case CAMU_SCREEN_TOGGLE_PAUSE: + camu_sink_toggle_pause(&c->sink); + break; + case CAMU_SCREEN_SEEK: + camu_sink_seek(&c->sink, float0); + break; + default: + break; + } +} + +static aki_thread_result AKI_THREADCALL event_loop_thread(void *userdata) +{ + struct cmv *c = (struct cmv *)userdata; + aki_event_loop_run(&c->loop); + aki_event_loop_destroy(&c->loop); + return 0; +} + +static bool cap_callback(void *userdata, u8 op, str *name, str *unique_id, void *opaque) +{ + struct cmv *c = (struct cmv *)userdata; + (void)name; + switch (op) { + case CAP_BUFFER: + return camu_sink_local_buffer(&c->sink, unique_id, (struct cch_entry *)opaque); + case CAP_SWAP: + camu_sink_local_swap(&c->sink, unique_id); + break; + case CAP_SET: + camu_sink_local_set(&c->sink, unique_id); + break; + case CAP_UNLOAD: + camu_sink_local_unload(&c->sink, unique_id); + break; + } + return true; +} + +#ifndef _WIN32 +static u8 line_callback(void *userdata, str *line) +{ + struct cmv *c = (struct cmv *)userdata; + if (al_str_eq(line, al_str_c(";NEXT"))) { + cap_list_skip(&c->cap, default_list, 1); + } else if (al_str_eq(line, al_str_c(";PREV"))) { + cap_list_skip(&c->cap, default_list, -1); + } else { + cap_list_add(&c->cap, default_list, line); + } + return AKI_LINE_PROCESSOR_CONTINUE; +} +#endif + +#if CMV_USE_TUI +static void timer_callback(void *userdata, struct aki_timer *timer) +{ + struct cmv *c = (struct cmv *)userdata; + struct camu_sink_entry *entry = camu_sink_get_current(&c->sink); + if (entry) { + tui_draw(&c->tui, &entry->clock, &entry->runner); + } else { + tui_draw(&c->tui, NULL, NULL); + } + aki_timer_again(timer); + camu_sink_return_current(&c->sink); +} + +static s32 log_callback(void *userdata, char *s) +{ + struct cmv *c = (struct cmv *)userdata; + tui_push_log_msg(&c->tui, s); + return 0; +} + +#ifdef HAVE_FFMPEG +static void lav_log_callback(void *userdata, s32 level, const char *fmt, va_list args) +{ + (void)userdata; + if (level < AV_LOG_VERBOSE) al_logv(warn, "lav_internal", (char *)fmt, args); +} +#endif +#endif + +s32 main(s32 argc, char *argv[]) +{ + aki_common_init(); + +#if CMV_USE_TUI + if (!tui_init(&c.tui)) { + aki_common_close(); + return EXIT_FAILURE; + } + al_set_print(&c, log_callback); +#ifdef HAVE_FFMPEG + camu_lav_set_log_callback(lav_log_callback); +#endif +#endif + + c.scr.callback = screen_callback; + c.scr.userdata = &c; + if (!camu_screen_init(&c.scr)) { + aki_common_close(); + return EXIT_FAILURE; + } + + if (!camu_screen_create_window(&c.scr, "cmv")) { + aki_common_close(); + return EXIT_FAILURE; + } + c.renderer = camu_renderer_lp_create(); + //c.renderer = camu_renderer_tiger_create(); + camu_screen_create_renderer(&c.scr, c.renderer); + +#if CAP_USE_PYTHON + bool py_init = sho_python_init(); +#endif + + camu_mixer_init(&c.mixer, (struct camu_audio *)&audio_plugin_miniaudio); + c.mixer.audio->configure_stream(c.mixer.audio, NULL); + + aki_event_loop_init(&c.loop); + + c.sink.callback = sink_callback; + c.sink.userdata = &c; + camu_sink_init(&c.sink, &c.loop, &c.mixer, c.renderer); + + cap_init(&c.cap, cap_callback, &c); + cap_make_list(&c.cap, default_list); + for (s32 i = 1; i < argc; i++) { + cap_list_add(&c.cap, default_list, al_str_c(argv[i])); + } + +#ifndef _WIN32 + c.socket.type = AKI_SOCKET_UNIX; + aki_socket_init(&c.socket); + aki_socket_set_blocking(&c.socket, false); + aki_socket_set_no_delay(&c.socket, 1); + c.pro.callback = line_callback; + c.pro.userdata = &c; + aki_line_processor_init(&c.pro, al_str_c("\n")); + aki_line_processor_open_socket(&c.pro, &c.socket); + if (aki_socket_listen(&c.socket, al_str_c("/tmp/cmv_sock"), 0)) { + aki_line_processor_run(&c.pro, &c.loop); + } +#endif + +#if CMV_USE_TUI + aki_timer_init(&c.timer, &c.loop, timer_callback, &c); + aki_timer_set_repeat(&c.timer, 0.15); + aki_timer_again(&c.timer); +#endif + + struct aki_thread thread0; + aki_thread_create(&thread0, event_loop_thread, &c); + + c.quit = 0; + + while (!c.quit) { + if (camu_screen_tick(&c.scr)) { + c.renderer->render(c.renderer, &c.scr); + } + } + + camu_screen_set_state(&c.scr, CAMU_SCREEN_CLOSED); + + camu_sink_stop(&c.sink); + + aki_thread_join(&thread0); + + camu_mixer_close(&c.mixer); + camu_screen_close(&c.scr); + c.renderer->free(&c.renderer); + + cap_close(&c.cap); + +#if CAP_USE_PYTHON + if (py_init) sho_python_close(); +#endif + +#if CMV_USE_TUI + tui_close(&c.tui); +#endif + + aki_common_close(); + + return EXIT_SUCCESS; +} diff --git a/src/fruits/cmv/meson.build b/src/fruits/cmv/meson.build new file mode 100644 index 0000000..b604104 --- /dev/null +++ b/src/fruits/cmv/meson.build @@ -0,0 +1,26 @@ +cmv2_src = ['cmv2.c', 'tui.c'] +#cmv2_src = ['cmv2.c'] +cmv_deps = [common_deps, buffer, cache, av, screen, render, mixer, bimu, libsink, cap] + +miniaudio_args = [ + '-DMA_NO_JACK', + '-DMA_NO_ALSA', + '-DMA_NO_DECODING', + '-DMA_NO_ENCODING', + '-DMA_NO_GENERATION', + '-DMA_NO_RESOURCE_MANAGER', + '-DMA_NO_NODE_GRAPH', + '-DMA_NO_ENGINE', + '-DMA_NO_RUNTIME_LINKING' +] + +sekihi_args = [ + '-DSEKIHI_WINDOW_WAYLAND', +# '-DSEKIHI_WINDOW_GLFW', + '-DSEKIHI_API_OPENGL', +# '-DSEKIHI_API_VULKAN', + '-DSEKIHI_POLL_INLINE', + '-DSEKIHI_PAUSE' +] + +executable('cmv2', cmv2_src, dependencies: cmv_deps, c_args: [miniaudio_args, sekihi_args]) diff --git a/src/fruits/cmv/tui.c b/src/fruits/cmv/tui.c new file mode 100644 index 0000000..675d5f4 --- /dev/null +++ b/src/fruits/cmv/tui.c @@ -0,0 +1,118 @@ +#include <aki/file.h> +#include <al/log.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <termios.h> + +#include "tui.h" + +static struct cmv_tui *tui_global; + +static void update_term_size(struct cmv_tui *tui) +{ + struct winsize w; + ioctl(tui->fd, TIOCGWINSZ, &w); + tui->width = w.ws_col; + tui->height = w.ws_row; +} + +static void handle_winch(s32 sig) +{ + (void)sig; + struct cmv_tui *tui = tui_global; + aki_mutex_lock(&tui->mutex); + tui->update_size = true; + aki_mutex_unlock(&tui->mutex); +} + +bool tui_init(struct cmv_tui *tui) +{ + tui->update_size = true; + tui->width = 0; + tui->height = 0; + //tui->prev_lines = 0; + + tui->fd = STDIN_FILENO; + tui->out = fdopen(tui->fd, "w"); + if (!tui->out) return false; + setbuf(tui->out, NULL); + + tui_global = tui; + aki_mutex_init(&tui->mutex); + signal(SIGWINCH, handle_winch); + + al_array_init(tui->log_buffer); + tui->last_pts = 0.0; + + return true; +} + +void tui_push_log_msg(struct cmv_tui *tui, char *msg) +{ + aki_mutex_lock(&tui->mutex); + al_array_push(tui->log_buffer, msg); + aki_mutex_unlock(&tui->mutex); +} + +static char buffer[128]; + +static void draw_now_playing(struct cmv_tui *tui, struct camu_clock *clock, struct bmu_local *runner) +{ + f64 pts = camu_clock_get_pts(clock); + if (pts == -1) pts = tui->last_pts; + else tui->last_pts = pts; + s32 parts = 0; + f64 duration = bmu_local_get_duration(runner); + if (pts > 0.0 && duration != 0.0) { + if (pts > duration) pts = duration; + f64 percent = pts / duration; + parts = tui->width * percent; + } else if (duration == 0.0) { + parts = tui->width; + } + al_memset(buffer, '-', parts); + buffer[parts] = '\0'; + fprintf(tui->out, "%s", buffer); +} + +void tui_draw(struct cmv_tui *tui, struct camu_clock *clock, struct bmu_local *runner) +{ + aki_mutex_lock(&tui->mutex); + + if (tui->update_size) { + update_term_size(tui); + tui->update_size = false; + } + + fprintf(tui->out, "\r\033[K"); + //for (u32 i = 1; i < tui->prev_lines; i++) { + // fprintf(tui->out, "\033[A\r\033[K"); + //} + + char *msg; + for (u32 i = 0; i < tui->log_buffer.size; i++) { + al_array_pop_at(tui->log_buffer, 0, msg); + fprintf(tui->out, "%s\n", msg); + } + + //tui->prev_lines = 0; + + if (runner) { + draw_now_playing(tui, clock, runner); + } + + aki_mutex_unlock(&tui->mutex); +} + +void tui_close(struct cmv_tui *tui) +{ + char *msg; + al_array_foreach(tui->log_buffer, i, msg) { + al_free(msg); + } + al_array_free(tui->log_buffer); + aki_mutex_destroy(&tui->mutex); + fprintf(tui->out, "\n"); + fflush(tui->out); + fclose(tui->out); +} diff --git a/src/fruits/cmv/tui.h b/src/fruits/cmv/tui.h new file mode 100644 index 0000000..566a5e5 --- /dev/null +++ b/src/fruits/cmv/tui.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../../buffer/clock.h" +#include "../../bimu/local.h" + +struct cmv_tui { + s32 fd; + FILE *out; + bool update_size; + u32 width; + u32 height; + //u32 prev_lines; + array(char *) log_buffer; + f64 last_pts; + struct aki_mutex mutex; +}; + +bool tui_init(struct cmv_tui *tui); +void tui_push_log_msg(struct cmv_tui *tui, char *msg); +void tui_draw(struct cmv_tui *tui, struct camu_clock *clock, struct bmu_local *runner); +void tui_close(struct cmv_tui *tui); |