subtitles.c
上传用户:riyaled888
上传日期:2009-03-27
资源大小:7338k
文件大小:13k
源码类别:

多媒体

开发平台:

MultiPlatform

  1. /*****************************************************************************
  2.  * subtitles.c
  3.  *****************************************************************************
  4.  * Copyright (C) 2003-2004 VideoLAN
  5.  * $Id: subtitles.c 8947 2004-10-07 20:52:24Z hartman $
  6.  *
  7.  * Authors: Derk-Jan Hartman <hartman at videolan.org>
  8.  * This is adapted code from the GPL'ed MPlayer (http://mplayerhq.hu)
  9.  *
  10.  * This program is free software; you can redistribute it and/or modify
  11.  * it under the terms of the GNU General Public License as published by
  12.  * the Free Software Foundation; either version 2 of the License, or
  13.  * (at your option) any later version.
  14.  *
  15.  * This program is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program; if not, write to the Free Software
  22.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
  23.  *****************************************************************************/
  24. /**
  25.  *  file
  26.  *  This file contains functions to dectect subtitle files.
  27.  */
  28. #include <stdlib.h>
  29. #include <vlc/vlc.h>
  30. #include <vlc/input.h>
  31. #ifdef HAVE_DIRENT_H
  32. #   include <dirent.h>
  33. #else
  34. #   include "../extras/dirent.h"
  35. #endif
  36. #include <ctype.h>
  37. /**
  38.  * What's between a directory and a filename?
  39.  */
  40. #if defined( WIN32 )
  41.     #define DIRECTORY_SEPARATOR '\'
  42. #else
  43.     #define DIRECTORY_SEPARATOR '/'
  44. #endif
  45. /**
  46.  * We are not going to autodetect more subtitle files than this.
  47.  */
  48. #define MAX_SUBTITLE_FILES 128
  49. /**
  50.  * The possible extentions for subtitle files we support
  51.  */
  52. static const char * sub_exts[] = {  "utf", "utf8", "utf-8", "sub", "srt", "smi", "txt", "ssa", "idx", NULL};
  53. /* extensions from unsupported types */
  54. /* rt, aqt, jss, js, ass */
  55. static void strcpy_trim( char *d, char *s )
  56. {
  57.     /* skip leading whitespace */
  58.     while( *s && !isalnum(*s) )
  59.     {
  60.         s++;
  61.     }
  62.     for(;;)
  63.     {
  64.         /* copy word */
  65.         while( *s && isalnum(*s) )
  66.         {
  67.             *d = tolower(*s);
  68.             s++; d++;
  69.         }
  70.         if (*s == 0) break;
  71.         /* trim excess whitespace */
  72.         while( *s && !isalnum(*s) )
  73.         {
  74.             s++;
  75.         }
  76.         if( *s == 0 ) break;
  77.         *d++ = ' ';
  78.     }
  79.     *d = 0;
  80. }
  81. static void strcpy_strip_ext( char *d, char *s )
  82. {
  83.     char *tmp = strrchr(s, '.');
  84.     if( !tmp ) {
  85.         strcpy(d, s);
  86.         return;
  87.     }
  88.     else
  89.     {
  90.         strncpy(d, s, tmp - s);
  91.         d[tmp - s] = 0;
  92.     }
  93.     while( *d )
  94.     {
  95.         *d = tolower(*d);
  96.         d++;
  97.     }
  98. }
  99. static void strcpy_get_ext( char *d, char *s )
  100. {
  101.     char *tmp = strrchr(s, '.');
  102.     if( !tmp )
  103.     {
  104.         strcpy(d, "");
  105.         return;
  106.     } else strcpy( d, tmp + 1 );
  107. }
  108. static int whiteonly( char *s )
  109. {
  110.   while ( *s )
  111.   {
  112.         if( isalnum( *s ) ) return 0;
  113.         s++;
  114.   }
  115.   return 1;
  116. }
  117. typedef struct _subfn
  118. {
  119.     int priority;
  120.     char *psz_fname;
  121.     char *psz_ext;
  122. } subfn;
  123. static int compare_sub_priority( const void *a, const void *b )
  124. {
  125.     if (((subfn*)a)->priority > ((subfn*)b)->priority)
  126.     {
  127.         return -1;
  128.     }
  129.     if (((subfn*)a)->priority < ((subfn*)b)->priority)
  130.     {
  131.         return 1;
  132.     }
  133. #ifndef UNDER_CE
  134.     return strcoll(((subfn*)a)->psz_fname, ((subfn*)b)->psz_fname);
  135. #else
  136.     return strcmp(((subfn*)a)->psz_fname, ((subfn*)b)->psz_fname);
  137. #endif
  138. }
  139. /**
  140.  * Convert a list of paths separated by ',' to a char**
  141.  */
  142. static char **paths_to_list( char *psz_dir, char *psz_path )
  143. {
  144.     unsigned int i, k, i_nb_subdirs;
  145.     char **subdirs; /* list of subdirectories to look in */
  146.     
  147.     i_nb_subdirs = 1;
  148.     for( k = 0; k < strlen( psz_path ); k++ )
  149.     {
  150.         if( psz_path[k] == ',' )
  151.         {
  152.             i_nb_subdirs++;
  153.         }
  154.     }
  155.                                                                                                                             
  156.     if( i_nb_subdirs > 0 )
  157.     {
  158.         char *psz_parser, *psz_temp;
  159.                                                                                                                             
  160.         subdirs = (char**)malloc( sizeof(char*) * ( i_nb_subdirs + 1 ) );
  161.         memset( subdirs, 0, sizeof(char*) * ( i_nb_subdirs + 1 ) );
  162.         i = 0;
  163.         psz_parser = psz_path;
  164.         while( psz_parser && *psz_parser )
  165.         {
  166.             char *psz_subdir;
  167.             psz_subdir = psz_parser;
  168.             psz_parser = strchr( psz_subdir, ',' );
  169.             if( psz_parser )
  170.             {
  171.                 *psz_parser = '';
  172.                 psz_parser++;
  173.                 while( *psz_parser == ' ' )
  174.                 {
  175.                     psz_parser++;
  176.                 }
  177.             }
  178.             if( strlen( psz_subdir ) > 0 )
  179.             {
  180.                 psz_temp = (char *)malloc( strlen(psz_dir)
  181.                                            + strlen(psz_subdir) + 2 );
  182.                 if( psz_temp )
  183.                 {
  184.                     sprintf( psz_temp, "%s%s%c", 
  185.                              psz_subdir[0] == '.' ? psz_dir : "", 
  186.                              psz_subdir,
  187.                              psz_subdir[strlen(psz_subdir) - 1] == 
  188.                               DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR );
  189.                     subdirs[i] = psz_temp;
  190.                     i++;
  191.                 }
  192.             }
  193.         }
  194.         subdirs[i] = NULL;
  195.     }
  196.     else
  197.     {
  198.         subdirs = NULL;
  199.     }
  200.     return subdirs;
  201. }
  202. /**
  203.  * Detect subtitle files.
  204.  *
  205.  * When called this function will split up the psz_fname string into a
  206.  * directory, filename and extension. It then opens the directory
  207.  * in which the file resides and tries to find possible matches of
  208.  * subtitles files.
  209.  *
  210.  * ingroup Demux
  211.  * param p_this the calling ref input_thread_t
  212.  * param psz_path a list of subdirectories (separated by a ',') to look in.
  213.  * param psz_fname the complete filename to base the search on.
  214.  * return a NULL terminated array of filenames with detected possible subtitles.
  215.  * The array contains max MAX_SUBTITLE_FILES items and you need to free it after use.
  216.  */
  217. char **subtitles_Detect( input_thread_t *p_this, char *psz_path,
  218.                          char *psz_fname )
  219. {
  220.     /* variables to be used for derivatives of psz_fname */
  221.     char *f_dir, *f_fname, *f_fname_noext, *f_fname_trim, *tmp;
  222.     /* variables to be used for derivatives FILE *f */
  223.     char *tmp_fname_noext, *tmp_fname_trim, *tmp_fname_ext, *tmpresult;
  224.     vlc_value_t fuzzy;
  225.     int len, i, j, i_sub_count, i_result2;
  226.     subfn *result; /* unsorted results */
  227.     char **result2; /* sorted results */
  228.     char **tmp_subdirs, **subdirs; /* list of subdirectories to look in */
  229.     FILE *f;
  230.     DIR *d;
  231.     struct dirent *de;
  232.     if( !strncmp( psz_fname, "file://", 7 ) )
  233.     {
  234.         psz_fname += 7;
  235.     }
  236.     i_sub_count = 0;
  237.     len = strlen( psz_fname ) > 256 ? strlen( psz_fname ) : 256;
  238.     f_dir = (char*)malloc(len);
  239.     f_fname = (char*)malloc(len);
  240.     f_fname_noext = (char*)malloc(len);
  241.     f_fname_trim = (char*)malloc(len);
  242.     tmp_fname_noext = (char*)malloc(len);
  243.     tmp_fname_trim = (char*)malloc(len);
  244.     tmp_fname_ext = (char*)malloc(len);
  245.     tmpresult = (char*)malloc(len);
  246.     result = (subfn*)malloc( sizeof(subfn) * MAX_SUBTITLE_FILES );
  247.     memset( result, 0, sizeof(subfn) * MAX_SUBTITLE_FILES );
  248.     /* extract filename & dirname from psz_fname */
  249.     tmp = strrchr( psz_fname, DIRECTORY_SEPARATOR );
  250.     if( tmp )
  251.     {
  252.         int pos;
  253.         strncpy( f_fname, tmp + 1, len - 1 );
  254.         f_fname[len - 1] = 0;
  255.         pos = tmp - psz_fname + 1;
  256.         strncpy( f_dir, psz_fname, __MIN(pos,len-1) );
  257.         f_dir[__MIN(pos,len-1)] = 0;
  258.     }
  259.     else
  260.     {
  261.         strncpy( f_fname, psz_fname, len - 1 );
  262.         f_fname[len - 1] = 0;
  263.         strcpy( f_dir, "" );
  264.     }
  265.     strcpy_strip_ext( f_fname_noext, f_fname );
  266.     strcpy_trim( f_fname_trim, f_fname_noext );
  267.     var_Get( p_this, "sub-autodetect-fuzzy", &fuzzy );
  268.     tmp_subdirs = paths_to_list( f_dir, psz_path );
  269.     subdirs = tmp_subdirs;
  270.     for( j = -1; j == -1 || ( j >= 0 && subdirs != NULL && *subdirs != NULL );
  271.          j++)
  272.     {
  273.         d = opendir( j < 0 ? f_dir : *subdirs );
  274.         if( d )
  275.         {
  276.             int b_found;
  277.             msg_Dbg( p_this, "looking for a subtitle file in %s", 
  278.                      j < 0 ? f_dir : *subdirs );
  279.             while( ( de = readdir( d ) ) )
  280.             {
  281.                 /* retrieve various parts of the filename */
  282.                 strcpy_strip_ext( tmp_fname_noext, de->d_name );
  283.                 strcpy_get_ext( tmp_fname_ext, de->d_name );
  284.                 strcpy_trim( tmp_fname_trim, tmp_fname_noext );
  285.                 /* does it end with a subtitle extension? */
  286.                 b_found = 0;
  287.                 for( i = 0; sub_exts[i]; i++ )
  288.                 {
  289.                     if( strcmp( sub_exts[i], tmp_fname_ext ) == 0 )
  290.                     {
  291.                         b_found = 1;
  292.                         msg_Dbg( p_this, "found a possible subtitle: %s",
  293.                                  de->d_name );
  294.                         break;
  295.                     }
  296.                 }
  297.                 /* we have a (likely) subtitle file */
  298.                 if( b_found )
  299.                 {
  300.                     int i_prio = 0;
  301.                     if( !i_prio && !strcmp( tmp_fname_trim, f_fname_trim ) )
  302.                     {
  303.                         /* matches the movie name exactly */
  304.                         i_prio = 4;
  305.                     }
  306.                     if( !i_prio &&
  307.                         ( tmp = strstr( tmp_fname_trim, f_fname_trim ) ) )
  308.                     {
  309.                         /* contains the movie name */
  310.                         tmp += strlen( f_fname_trim );
  311.                         if( whiteonly( tmp ) )
  312.                         {
  313.                             /* chars in front of the movie name */
  314.                             i_prio = 2;
  315.                         }
  316.                         else
  317.                         {
  318.                             /* chars after (and possibly in front of)
  319.                              * the movie name */
  320.                             i_prio = 3;
  321.                         }
  322.                     }
  323.                     if( !i_prio )
  324.                     {
  325.                         /* doesn't contain the movie name */
  326.                         if( j == 0 ) i_prio = 1;
  327.                     }
  328.                     if( i_prio >= fuzzy.i_int )
  329.                     {
  330.                         sprintf( tmpresult, "%s%s", j == -1 ? f_dir : *subdirs,
  331.                                  de->d_name );
  332.                         msg_Dbg( p_this, "autodetected subtitle: %s with "
  333.                                  "priority %d", de->d_name, i_prio );
  334.                         if( ( f = fopen( tmpresult, "rt" ) ) )
  335.                         {
  336.                             fclose( f );
  337.                             result[i_sub_count].priority = i_prio;
  338.                             result[i_sub_count].psz_fname = strdup(tmpresult);
  339.                             result[i_sub_count].psz_ext = strdup(tmp_fname_ext);
  340.                             i_sub_count++;
  341.                         }
  342.                     }
  343.                 }
  344.                 if( i_sub_count >= MAX_SUBTITLE_FILES ) break;
  345.             }
  346.             closedir( d );
  347.         }
  348.         if( j >= 0 ) free( *subdirs++ );
  349.     }
  350.     if( tmp_subdirs ) free( tmp_subdirs );
  351.     free( f_dir );
  352.     free( f_fname );
  353.     free( f_fname_noext );
  354.     free( f_fname_trim );
  355.     free( tmp_fname_noext );
  356.     free( tmp_fname_trim );
  357.     free( tmp_fname_ext );
  358.     free( tmpresult );
  359.     qsort( result, i_sub_count, sizeof( subfn ), compare_sub_priority );
  360.     result2 = (char**)malloc( sizeof(char*) * ( i_sub_count + 1 ) );
  361.     memset( result2, 0, sizeof(char*) * ( i_sub_count + 1 ) );
  362.     i_result2 = 0;
  363.     for( i = 0; i < i_sub_count; i++ )
  364.     {
  365.         if( result[i].psz_ext && !strcasecmp( result[i].psz_ext, "sub" ) )
  366.         {
  367.             int j;
  368.             for( j = 0; j < i_sub_count; j++ )
  369.             {
  370.                 if( result[j].psz_fname && result[i].psz_fname &&
  371.                     !strncasecmp( result[i].psz_fname, result[j].psz_fname, sizeof( result[i].psz_fname) - 4 ) && 
  372.                     !strcasecmp( result[j].psz_ext, "idx" ) )
  373.                     break;
  374.                 
  375.             }
  376.             if( j >= i_sub_count )
  377.             {
  378.                 result2[i_result2] = result[i].psz_fname;
  379.                 i_result2++;
  380.             }
  381.         }
  382.         else
  383.         {
  384.             result2[i_result2] = result[i].psz_fname;
  385.             i_result2++;
  386.         }
  387.     }
  388.     free( result );
  389.     return result2;
  390. }