Index: DOCS/man/en/mplayer.1 =================================================================== --- DOCS/man/en/mplayer.1 (revision 25315) +++ DOCS/man/en/mplayer.1 (working copy) @@ -2473,6 +2473,12 @@ Tells MPlayer to handle the subtitle file as unicode. . .TP +.B \-unrarexec +Specify the path to the unrar executable so MPlayer can use it to access +rar-compressed vobsub files (default: not set, so the feature is off). +The path must include the executable's filename, i.e.\& /usr/local/bin/unrar. +. +.TP .B "\-utf8 \ \ " Tells MPlayer to handle the subtitle file as UTF-8. . Index: Makefile =================================================================== --- Makefile (revision 25315) +++ Makefile (working copy) @@ -35,6 +35,7 @@ vobsub.c \ SRCS_COMMON-$(UNRARLIB) += unrarlib.c +SRCS_COMMON-$(UNRAR_EXEC) += unrar_exec.c SRCS_MPLAYER = mplayer.c \ m_property.c \ Index: cfg-mplayer.h =================================================================== --- cfg-mplayer.h (revision 25315) +++ cfg-mplayer.h (working copy) @@ -35,6 +35,8 @@ extern int menu_flip_hebrew; extern int menu_fribidi_flip_commas; +extern char *unrar_executable; + extern int vo_zr_parseoption(const m_option_t* conf, char *opt, char * param); extern void vo_zr_revertoption(const m_option_t* opt,char* pram); @@ -267,6 +269,9 @@ // these should be moved to -common, and supported in MEncoder {"vobsub", &vobsub_name, CONF_TYPE_STRING, 0, 0, 0, NULL}, {"vobsubid", &vobsub_id, CONF_TYPE_INT, CONF_RANGE, 0, 31, NULL}, +#ifdef USE_UNRAR_EXEC + {"unrarexec", &unrar_executable, CONF_TYPE_STRING, 0, 0, 0, NULL}, +#endif {"sstep", &step_sec, CONF_TYPE_INT, CONF_MIN, 0, 0, NULL}, Index: configure =================================================================== --- configure (revision 25315) +++ configure (working copy) @@ -256,6 +256,7 @@ --disable-freetype disable FreeType 2 font rendering [autodetect] --disable-fontconfig disable fontconfig font lookup [autodetect] --disable-unrarlib disable Unique RAR File Library [enabled] + --disable-unrarexec disable using of UnRAR executable [enabled] --enable-menu enable OSD menu (not DVD menu) [disabled] --disable-sortsub disable subtitle sorting [enabled] --enable-fribidi enable the FriBiDi libs [autodetect] @@ -592,6 +593,7 @@ _alsa=auto _fastmemcpy=yes _unrarlib=yes +_unrar_exec=yes _win32dll=auto _select=yes _radio=no @@ -1097,6 +1099,8 @@ --disable-fontconfig) _fontconfig=no ;; --enable-unrarlib) _unrarlib=yes ;; --disable-unrarlib) _unrarlib=no ;; + --enable-unrarexec) _unrar_exec=yes ;; + --disable-unrarexec) _unrar_exec=no ;; --enable-ftp) _ftp=yes ;; --disable-ftp) _ftp=no ;; --enable-vstream) _vstream=yes ;; @@ -6704,6 +6709,14 @@ fi echores "$_unrarlib" +echocheck "UnRAR executable" +if test "$_unrar_exec" = yes ; then + _def_unrar_exec='#define USE_UNRAR_EXEC 1' +else + _def_unrar_exec='#undef USE_UNRAR_EXEC' +fi +echores "$_unrar_exec" + echocheck "TV interface" if test "$_tv" = yes ; then _def_tv='#define USE_TV 1' @@ -7600,6 +7614,7 @@ MUSEPACK = $_musepack UNRARLIB = $_unrarlib +UNRAR_EXEC = $_unrar_exec PNG = $_png JPEG = $_jpeg GIF = $_gif @@ -8154,6 +8169,9 @@ /* Use unrarlib for Vobsubs */ $_def_unrarlib +/* Use UnRAR executable for Vobsubs */ +$_def_unrar_exec + /* gui support, please do not edit this option */ $_def_gui $_def_gtk2_gui Index: unrar_exec.c =================================================================== --- unrar_exec.c (revision 0) +++ unrar_exec.c (revision 0) @@ -0,0 +1,324 @@ +/* + * List files and extract file from rars by using external executable unrar. + * + * Copyright (C) 2005 Jindrich Makovicka + * Copyright (C) 2007 Ulion + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "unrar_exec.h" + +#include "mp_msg.h" + +#define UNRAR_LIST 1 +#define UNRAR_EXTRACT 2 + +char* unrar_executable = NULL; + +#ifdef __MINGW32__ +static int cmd_escape_append(char *cmd, const char *parameter) +{ + char *p = cmd; + if (*parameter=='\0') + return 0; + while (*parameter) { + // ./executable_name is not recognized by cmd.exe without quotes. + // So we convert it to .\executable_name to fix this. + if (*parameter == '/') { + *p++ = '\\'; + ++parameter; + continue; + } + // Spaces need be quoted. + if (*parameter == ' ') { + // Need to count how many continual '\' before current position. + // If count '\' is N, then we write additional N '\'. + // Then we have 2N '\' followed by a quote, that will result + // N '\' and following quote works as it is not '\' before it. + const char *q = p - 1; + while (q >= cmd && *q-- == '\\') + *p++ = '\\'; + *p++ = '"'; + while (*parameter==' ') + *p++ = *parameter++; + *p++ = '"'; + continue; + } + // '"' has special escape char, it can only be escaped by '\' + if (*parameter == '"') { + // Need to count how many continual '\' before current position. + // If count '\' is N, then we write additional N+1 '\'. + // Then we have 2N + 1 '\' followed by a quote, that will result + // N '\' followed by a quote. + const char *q = p - 1; + while (q >= cmd && *q-- == '\\') + *p++ = '\\'; + *p++ = '\\'; + } + else if (strchr("<>&|^@()", *parameter)) + *p++ = '^'; + *p++ = *parameter++; + } + *p++ = ' '; + *p = '\0'; + return p - cmd; +} +#endif + +static FILE* launch_pipe(pid_t *apid, const char *executable, int action, + const char *archive, const char *filename) +{ + if (!executable || access(executable, X_OK)) return NULL; + if (access(archive, R_OK)) return NULL; + { +#ifdef __MINGW32__ + /* + * If not using popen, the only other way is to use CreateProcess. + * This need a cmdline as parameter also, so we use popen on win32 + * and escape the command line. + * On win32, popen equals 'cmd.exe /C cmdline'. + * Check the output of 'cmd.exe /?' to see how quotes on the command + * line are processed. + */ + int mlen = (strlen(executable)+strlen(archive)+filename?strlen(filename):0)*3+100; + char cmdline[mlen]; + cmdline[0] = '"'; + cmdline[1] = '\0'; + cmd_escape_append(cmdline + strlen(cmdline), executable); + if (action == UNRAR_LIST) + strcat(cmdline, "v "); + else if (action == UNRAR_EXTRACT) + strcat(cmdline, "p -inul -p- "); + else + return NULL; + cmd_escape_append(cmdline + strlen(cmdline), archive); + if (action == UNRAR_EXTRACT) + cmd_escape_append(cmdline + strlen(cmdline), filename); + strcat(cmdline, "\""); + mp_msg(MSGT_GLOBAL, MSGL_V, "UnRAR: call unrar with command line: %s\n", + cmdline); + *apid = 1; + return popen(cmdline, "rb"); +#else + int mypipe[2]; + pid_t pid; + + if (pipe(mypipe)) { + mp_msg(MSGT_GLOBAL, MSGL_ERR, "UnRAR: Cannot create pipe.\n"); + return NULL; + } + + pid = fork(); + if (pid == 0) { + /* This is the child process. Execute the unrar executable. */ + int fd; + close(mypipe[0]); + // Close MPlayer's stdin, stdout and stderr so the unrar binary + // can not mess them up. + // TODO: Close all other files except the pipe. + close(0); close(1); close(2); + // Assign new stdin, stdout and stderr and check they actually got the + // right descriptors. + if (open("/dev/null", O_RDONLY) != 0 || dup(mypipe[1]) != 1 + || open("/dev/null", O_WRONLY) != 2) + _exit(EXIT_FAILURE); + if (action == UNRAR_LIST) + execl(executable, executable, "v", archive, NULL); + else if (action == UNRAR_EXTRACT) + execl(executable, executable, "p", "-inul", "-p-", + archive,filename,NULL); + mp_msg(MSGT_GLOBAL, MSGL_ERR, "UnRAR: Cannot execute %s\n", executable); + _exit(EXIT_FAILURE); + } + if (pid < 0) { + /* The fork failed. Report failure. */ + mp_msg(MSGT_GLOBAL, MSGL_ERR, "UnRAR: Fork failed\n"); + return NULL; + } + /* This is the parent process. Prepare the pipe stream. */ + close(mypipe[1]); + *apid = pid; + if (action == UNRAR_LIST) + mp_msg(MSGT_GLOBAL, MSGL_V, + "UnRAR: call unrar with command line: %s v %s\n", + executable, archive); + else if (action == UNRAR_EXTRACT) + mp_msg(MSGT_GLOBAL, MSGL_V, + "UnRAR: call unrar with command line: %s p -inul -p- %s %s\n", + executable, archive, filename); + return fdopen(mypipe[0], "r"); +#endif + } +} + +#define ALLOC_INCR 1 * 1024 * 1024 +int unrar_exec_get(unsigned char **output, unsigned long *size, + const char *filename, const char *rarfile) +{ + int bufsize = ALLOC_INCR, bytesread; + pid_t pid; + int status = 0; + FILE *rar_pipe; + + rar_pipe=launch_pipe(&pid,unrar_executable,UNRAR_EXTRACT,rarfile,filename); + if (!rar_pipe) return 0; + + *size = 0; + + *output = malloc(bufsize); + + while (*output) { + bytesread=fread(*output+*size, 1, bufsize-*size, rar_pipe); + if (bytesread <= 0) + break; + *size += bytesread; + if (*size == bufsize) { + char *p; + bufsize += ALLOC_INCR; + p = realloc(*output, bufsize); + if (!p) + free(*output); + *output = p; + } + } +#ifdef __MINGW32__ + pclose(rar_pipe); +#else + fclose(rar_pipe); + pid = waitpid(pid, &status, 0); +#endif + if (!*output || !*size || (pid == -1 && errno != ECHILD) || + (pid > 0 && status)) { + free(*output); + *output = NULL; + *size = 0; + return 0; + } + if (bufsize > *size) { + char *p = realloc(*output, *size); + if (p) + *output = p; + } + mp_msg(MSGT_GLOBAL, MSGL_V, "UnRAR: got file %s len %lu\n", filename,*size); + return 1; +} + +#define PARSE_NAME 0 +#define PARSE_PROPS 1 + +int unrar_exec_list(const char *rarfile, ArchiveList_struct **list) +{ + char buf[1024], fname[1024]; + char *p; + pid_t pid; + int status = 0, file_num = -1, ignore_next_line = 0, state = PARSE_NAME; + FILE *rar_pipe; + ArchiveList_struct *alist = NULL, *current = NULL, *new; + + rar_pipe = launch_pipe(&pid, unrar_executable, UNRAR_LIST, rarfile, NULL); + if (!rar_pipe) return -1; + while (fgets(buf, sizeof(buf), rar_pipe)) { + int packsize, unpsize, ratio, day, month, year, hour, min; + int llen = strlen(buf); + // If read nothing, we got a file_num -1. + if (file_num == -1) + file_num = 0; + if (buf[llen-1] != '\n') + // The line is too long, ignore it. + ignore_next_line = 2; + if (ignore_next_line) { + --ignore_next_line; + state = PARSE_NAME; + continue; + } + // Trim the line. + while (llen > 0 && strchr(" \t\n\r\v\f", buf[llen-1])) + --llen; + buf[llen] = '\0'; + p = buf; + while (*p && strchr(" \t\n\r\v\f", *p)) + ++p; + if (!*p) { + state = PARSE_NAME; + continue; + } + + if (state == PARSE_PROPS && sscanf(p, "%d %d %d%% %d-%d-%d %d:%d", + &unpsize, &packsize, &ratio, &day, + &month, &year, &hour, &min) == 8) { + new = calloc(1, sizeof(ArchiveList_struct)); + if (!new) { + file_num = -1; + break; + } + if (!current) + alist = new; + else + current->next = new; + current = new; + current->item.Name = strdup(fname); + state = PARSE_NAME; + if (!current->item.Name) { + file_num = -1; + break; + } + current->item.PackSize = packsize; + current->item.UnpSize = unpsize; + ++file_num; + continue; + } + strcpy(fname, p); + state = PARSE_PROPS; + } +#ifdef __MINGW32__ + pclose(rar_pipe); +#else + fclose(rar_pipe); + pid = waitpid(pid, &status, 0); +#endif + if (file_num < 0 || (pid == -1 && errno != ECHILD) || + (pid > 0 && status)) { + unrar_exec_freelist(alist); + return -1; + } + if (!alist) + return -1; + *list = alist; + mp_msg(MSGT_GLOBAL, MSGL_V, "UnRAR: list got %d files\n", file_num); + return file_num; +} + +void unrar_exec_freelist(ArchiveList_struct *list) +{ + ArchiveList_struct* tmp; + + while (list) { + tmp = list->next; + free(list->item.Name); + free(list); + list = tmp; + } +} + Index: unrar_exec.h =================================================================== --- unrar_exec.h (revision 0) +++ unrar_exec.h (revision 0) @@ -0,0 +1,38 @@ +/* + * List files and extract file from rars by using external executable unrar. + * + * Copyright (C) 2005 Jindrich Makovicka + * Copyright (C) 2007 Ulion + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef UNRAR_EXEC_H +#define UNRAR_EXEC_H + +#include "unrarlib.h" + +extern char* unrar_executable; + +int unrar_exec_get(unsigned char **output, unsigned long *size, + const char *filename, const char *rarfile); + +int unrar_exec_list(const char *rarfile, ArchiveList_struct **list); + +void unrar_exec_freelist(ArchiveList_struct *list); + +#endif /* UNRAR_EXEC_H */ Index: vobsub.c =================================================================== --- vobsub.c (revision 25315) +++ vobsub.c (working copy) @@ -20,7 +20,9 @@ #include "vobsub.h" #include "spudec.h" #include "mp_msg.h" -#ifdef USE_UNRARLIB +#ifdef USE_UNRAR_EXEC +#include "unrar_exec.h" +#elif defined(USE_UNRARLIB) #include "unrarlib.h" #endif #include "libavutil/common.h" @@ -35,7 +37,7 @@ * The RAR file must have the same basename as the file to open * See **********************************************************************/ -#ifdef USE_UNRARLIB +#if defined(USE_UNRARLIB) || defined(USE_UNRAR_EXEC) typedef struct { FILE *file; unsigned char *data; @@ -59,7 +61,7 @@ if (stream->file == NULL) { char *rar_filename; const char *p; - int rc; + int rc = 0; /* Guess the RAR archive filename */ rar_filename = NULL; p = strrchr(filename, '.'); @@ -88,15 +90,27 @@ } else { p++; } +#ifdef USE_UNRAR_EXEC + rc = unrar_exec_get(&stream->data, &stream->size, p, rar_filename); +#endif +#ifdef USE_UNRARLIB + if (!rc) rc = urarlib_get(&stream->data, &stream->size, (char*) p, rar_filename, ""); +#endif if (!rc) { /* There is no matching filename in the archive. However, sometimes * the files we are looking for have been given arbitrary names in the archive. * Let's look for a file with an exact match in the extension only. */ - int i, num_files, name_len; + int i, num_files = -1, name_len; ArchiveList_struct *list, *lp; +#ifdef USE_UNRARLIB /* the cast in the next line is a hack to overcome a design flaw (IMHO) in unrarlib */ num_files = urarlib_list (rar_filename, (ArchiveList_struct *)&list); +#endif +#ifdef USE_UNRAR_EXEC + if (num_files <= 0) + num_files = unrar_exec_list(rar_filename, &list); +#endif if (num_files > 0) { char *demanded_ext; demanded_ext = strrchr (p, '.'); @@ -105,13 +119,24 @@ for (i=0, lp=list; inext) { name_len = strlen (lp->item.Name); if (name_len >= demanded_ext_len && !strcasecmp (lp->item.Name + name_len - demanded_ext_len, demanded_ext)) { - if ((rc = urarlib_get(&stream->data, &stream->size, lp->item.Name, rar_filename, ""))) { - break; - } +#ifdef USE_UNRAR_EXEC + rc = unrar_exec_get(&stream->data, &stream->size, + lp->item.Name, rar_filename); + if (rc) break; +#endif +#ifdef USE_UNRARLIB + rc = urarlib_get(&stream->data, &stream->size, + lp->item.Name, rar_filename, ""); + if (rc) break; +#endif } } } +#ifdef USE_UNRARLIB urarlib_freelist (list); +#else + unrar_exec_freelist(list); +#endif } if (!rc) { free(rar_filename);