#pragma once #include #include #include #include #include #include #include struct MpvProperty { std::string key; std::string value; }; struct Args { long wid_num = 0; long ipc_fd_num = 0; const char *ipc_fd = nullptr; const char *audio_file = nullptr; const char *file_to_play = nullptr; std::vector mpv_properties; }; static void usage() { fprintf(stderr, "usage: quickmedia-video-player [--ipc-fd ] [--audio-file ] [--key=value...] \n"); fprintf(stderr, " --ipc-fd A bi-directional (socketpair) file descriptor to receive commands from. Optional\n"); fprintf(stderr, " --audio-file Load the given audio file. Optional\n"); fprintf(stderr, " --key=value Additional options in the format --key=value are passed directly to mpv as string properties. Optional\n"); fprintf(stderr, "examples:\n"); fprintf(stderr, " quickmedia-video-player video.mp4\n"); fprintf(stderr, " quickmedia-video-player --wid 30481231 -- video.mp4\n"); exit(1); } static bool string_to_long(const char *str, long &result) { errno = 0; char *endptr = NULL; result = strtol(str, &endptr, 0); return endptr != str && errno == 0; } static bool string_to_double(const char *str, double &result) { errno = 0; char *endptr = NULL; result = strtod(str, &endptr); return endptr != str && errno == 0; } static bool fd_is_valid(int fd) { errno = 0; return fcntl(fd, F_GETFD) != -1 && errno != EBADF; } static Args parse_args(int argc, char **argv) { Args args; for(int i = 1; i < argc; ++i) { const char *arg = argv[i]; if(strcmp(arg, "--audio-file") == 0) { if(args.audio_file) { fprintf(stderr, "Error: option --audio-file was specified multiple times\n"); usage(); } if(i + 1 == argc) { fprintf(stderr, "Error: missing audio file after option --audio-file\n"); usage(); } args.audio_file = argv[i + 1]; ++i; } else if(strcmp(arg, "--ipc-fd") == 0) { if(args.ipc_fd) { fprintf(stderr, "Error: option --ipc-fd was specified multiple times\n"); usage(); } if(i + 1 == argc) { fprintf(stderr, "Error: missing fd after option --ipc-fd\n"); usage(); } args.ipc_fd = argv[i + 1]; ++i; } else if(strcmp(arg, "--") == 0) { if(i + 1 == argc) { fprintf(stderr, "Error: missing file option after --\n"); usage(); } else if(i + 1 != argc - 1) { fprintf(stderr, "Error: more than one option was specified after --\n"); usage(); } args.file_to_play = argv[i + 1]; ++i; } else if(strncmp(arg, "--", 2) == 0) { const char *equal_p = strchr(arg, '='); if(!equal_p) { fprintf(stderr, "Error: mpv option %s is missing \"=\"\n", arg); usage(); } args.mpv_properties.push_back({ std::string(arg + 2, equal_p - (arg + 2)), equal_p + 1 }); } else { if(args.file_to_play) { fprintf(stderr, "Error: file option was specified multiple times\n"); usage(); } args.file_to_play = arg; } } if(!args.file_to_play) { fprintf(stderr, "Error: missing file option\n"); usage(); } if(args.ipc_fd) { if(!string_to_long(args.ipc_fd, args.ipc_fd_num)) { fprintf(stderr, "Error: invalid number %s was specified for option --ipc-fd\n", args.ipc_fd); usage(); } if(!fd_is_valid(args.ipc_fd_num)) { fprintf(stderr, "Error: invalid fd %s was specified for option --ipc-fd\n", args.ipc_fd); usage(); } } return args; }