diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1 index e81a442d0a..55279b8192 100644 --- a/DOCS/man/en/mplayer.1 +++ b/DOCS/man/en/mplayer.1 @@ -2481,6 +2481,12 @@ used for the OSD and clear it (default: ^[[A\\r^[[K). Tells MPlayer to handle the subtitle file as unicode. . .TP +.B \-unrarexec (does not support MingW currently.) +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. . diff --git a/Makefile b/Makefile index 796071de60..9f79e1991b 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,7 @@ SRCS_COMMON = asxparser.c \ vobsub.c \ SRCS_COMMON-$(UNRARLIB) += unrarlib.c +SRCS_COMMON-$(UNRAR_EXEC) += unrar_exec.c SRCS_MPLAYER = mplayer.c \ m_property.c \ diff --git a/cfg-mplayer.h b/cfg-mplayer.h index cc5caa8c09..e968dd6d34 100644 --- a/cfg-mplayer.h +++ b/cfg-mplayer.h @@ -35,6 +35,8 @@ extern char *menu_fribidi_charset; 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 @@ const m_option_t mplayer_opts[]={ // 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}, diff --git a/configure b/configure index 4fb13fb26f..c00dc7d3a0 100755 --- a/configure +++ b/configure @@ -256,6 +256,7 @@ Optional features: --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 @@ _sunaudio=auto _alsa=auto _fastmemcpy=yes _unrarlib=yes +_unrar_exec=auto _win32dll=auto _select=yes _radio=no @@ -1097,6 +1099,8 @@ for ac_option do --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 ;; @@ -6715,6 +6719,18 @@ else fi echores "$_unrarlib" +echocheck "UnRAR executable" +if test "$_unrar_exec" = auto ; then + _unrar_exec="yes" + mingw32 && _unrar_exec="no" +fi +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' @@ -7611,6 +7627,7 @@ SPEEX = $_speex MUSEPACK = $_musepack UNRARLIB = $_unrarlib +UNRAR_EXEC = $_unrar_exec PNG = $_png JPEG = $_jpeg GIF = $_gif @@ -8165,6 +8182,9 @@ $_def_fastmemcpy /* 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 diff --git a/unrar_exec.c b/unrar_exec.c new file mode 100644 index 0000000000..29a531fb61 --- /dev/null +++ b/unrar_exec.c @@ -0,0 +1,236 @@ +/* + * 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 +#include "unrar_exec.h" + +#include "mp_msg.h" + +#define UNRAR_LIST 1 +#define UNRAR_EXTRACT 2 + +char* unrar_executable = NULL; + +static FILE* launch_pipe(pid_t *apid, const char *executable, int action, + const char *archive, const char *filename) +{ + if (!executable || access(executable, R_OK | X_OK)) return NULL; + if (access(archive, R_OK)) return NULL; + { + 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. */ + 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"); + } +} + +#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; + } + } + fclose(rar_pipe); + pid = waitpid(pid, &status, 0); + 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; + } + fclose(rar_pipe); + pid = waitpid(pid, &status, 0); + 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; + } +} + diff --git a/unrar_exec.h b/unrar_exec.h new file mode 100644 index 0000000000..f34cac4f10 --- /dev/null +++ b/unrar_exec.h @@ -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 */ diff --git a/vobsub.c b/vobsub.c index ce804c3b77..3a075bb021 100644 --- a/vobsub.c +++ b/vobsub.c @@ -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 @@ static int vobsubid = -2; * 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 @@ rar_open(const char *const filename, const char *const mode) 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 @@ rar_open(const char *const filename, const char *const mode) } 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 @@ rar_open(const char *const filename, const char *const mode) 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);