]> git.sesse.net Git - vlc/blobdiff - modules/access/vdr.c
Add Vorbis and VP8 codecs to live555.
[vlc] / modules / access / vdr.c
index 7d578b3b473a73c39435e19d7193433bc39aa40b..56e4eb0f04a2ef0b665d45c3367d14fffa6af13e 100644 (file)
@@ -3,19 +3,19 @@
  *****************************************************************************
  * Copyright (C) 2010 Tobias Güntner
  *
- * This program 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
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
  * (at your option) any later version.
  *
  * This program 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.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
 /***
@@ -31,8 +31,8 @@ VDR recordings have either of two directory layouts:
             001.vdr, 002.vdr, 003.vdr, ...
             index.vdr, info.vdr, marks.vdr, ...
     2) TS format:
-        /path/to/0000-00-00.00.00.0.0.rec/
-            001.ts, 002.ts, 003.ts, ...
+        /path/to/0000-00-00.00.00.0-0.rec/
+            00001.ts, 00002.ts, 00003.ts, ...
             index, info, marks, ...
 See http://www.vdr-wiki.de/ and http://www.tvdr.de/ for more information.
 ***/
@@ -45,18 +45,12 @@ See http://www.vdr-wiki.de/ and http://www.tvdr.de/ for more information.
 # include "config.h"
 #endif
 
-#ifdef HAVE_SYS_TYPES_H
-#   include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-#   include <sys/stat.h>
-#endif
-#ifdef HAVE_FCNTL_H
-#   include <fcntl.h>
-#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #ifdef HAVE_UNISTD_H
 #   include <unistd.h>
-#elif defined( WIN32 ) && !defined( UNDER_CE )
+#elif defined( WIN32 )
 #   include <io.h>
 #endif
 
@@ -64,11 +58,6 @@ See http://www.vdr-wiki.de/ and http://www.tvdr.de/ for more information.
 #include <time.h>
 #include <errno.h>
 
-#if defined( WIN32 ) && !defined( UNDER_CE )
-#   undef lseek
-#   define lseek _lseeki64
-#endif
-
 #include <vlc_common.h>
 #include <vlc_plugin.h>
 #include <vlc_access.h>
@@ -86,10 +75,6 @@ static void Close( vlc_object_t * );
 
 #define HELP_TEXT N_("Support for VDR recordings (http://www.tvdr.de/).")
 
-#define CACHING_TEXT N_("Caching value in ms")
-#define CACHING_LONGTEXT N_( \
-    "Caching value for files. This value should be set in milliseconds." )
-
 #define CHAPTER_OFFSET_TEXT N_("Chapter offset in ms")
 #define CHAPTER_OFFSET_LONGTEXT N_( \
     "Move all chapters. This value should be set in milliseconds." )
@@ -104,8 +89,6 @@ vlc_module_begin ()
     set_help( HELP_TEXT )
     set_subcategory( SUBCAT_INPUT_ACCESS )
     set_description( N_("VDR recordings") )
-    add_integer( "vdr-caching", 5 * DEFAULT_PTS_DELAY / 1000,
-        CACHING_TEXT, CACHING_LONGTEXT, true )
     add_integer( "vdr-chapter-offset", 0,
         CHAPTER_OFFSET_TEXT, CHAPTER_OFFSET_LONGTEXT, true )
     add_float_with_range( "vdr-fps", 25, 1, 1000,
@@ -122,6 +105,9 @@ vlc_module_end ()
  * Local prototypes, constants, structures
  *****************************************************************************/
 
+/* minimum chapter size in seconds */
+#define MIN_CHAPTER_SIZE 5
+
 TYPEDEF_ARRAY( uint64_t, size_array_t );
 
 struct access_sys_t
@@ -152,14 +138,12 @@ static int Control( access_t *, int, va_list );
 static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len );
 static int Seek( access_t *p_access, uint64_t i_pos);
 static void FindSeekpoint( access_t *p_access );
-static bool ScanDirectory( access_t *p_access, bool b_strict );
+static bool ScanDirectory( access_t *p_access );
 static char *GetFilePath( access_t *p_access, unsigned i_file );
 static bool ImportNextFile( access_t *p_access );
 static bool SwitchFile( access_t *p_access, unsigned i_file );
 static void OptimizeForRead( int fd );
 static void UpdateFileSize( access_t *p_access );
-static int StatRelativeFile( access_t *p_access, const char *psz_file,
-                            struct stat *p_stat );
 static FILE *OpenRelativeFile( access_t *p_access, const char *psz_file );
 static bool ReadLine( char **ppsz_line, size_t *pi_size, FILE *p_file );
 static void ImportMeta( access_t *p_access );
@@ -167,6 +151,7 @@ static void ImportMarks( access_t *p_access );
 static bool ReadIndexRecord( FILE *p_file, bool b_ts, int64_t i_frame,
                             uint64_t *pi_offset, uint16_t *pi_file_num );
 static int64_t ParseFrameNumber( const char *psz_line, float fps );
+static const char *BaseName( const char *psz_path );
 
 /*****************************************************************************
  * Open a directory
@@ -183,13 +168,17 @@ static int Open( vlc_object_t *p_this )
      * and we can avoid false positives in the general case. */
     bool b_strict = strcmp( p_access->psz_access, "vdr" );
 
-    /* Do a quick test based on the directory extension to see if this
+    /* Do a quick test based on the directory name to see if this
      * directory might contain a VDR recording. We can be reasonably
      * sure if ScanDirectory() actually finds files. */
     if( b_strict )
     {
-        const char *psz_ext = strrchr( p_access->psz_filepath, '.' );
-        if( !psz_ext || strcasecmp( psz_ext, ".rec" ) )
+        char psz_extension[4];
+        int i_length = 0;
+        const char *psz_name = BaseName( p_access->psz_filepath );
+        if( sscanf( psz_name, "%*u-%*u-%*u.%*u.%*u.%*u%*[-.]%*u.%3s%n",
+            psz_extension, &i_length ) != 1 || strcasecmp( psz_extension, "rec" ) ||
+            ( psz_name[i_length] != DIR_SEP_CHAR && psz_name[i_length] != '\0' ) )
             return VLC_EGENERIC;
     }
 
@@ -206,7 +195,7 @@ static int Open( vlc_object_t *p_this )
     ARRAY_INIT( p_sys->file_sizes );
 
     /* Import all files and prepare playback. */
-    if( !ScanDirectory( p_access, b_strict ) ||
+    if( !ScanDirectory( p_access ) ||
         !SwitchFile( p_access, 0 ) )
     {
         Close( p_this );
@@ -238,7 +227,7 @@ static void Close( vlc_object_t * p_this )
 /*****************************************************************************
  * Determine format and import files
  *****************************************************************************/
-static bool ScanDirectory( access_t *p_access, bool b_strict )
+static bool ScanDirectory( access_t *p_access )
 {
     access_sys_t *p_sys = p_access->p_sys;
 
@@ -251,15 +240,6 @@ static bool ScanDirectory( access_t *p_access, bool b_strict )
             return false;
     }
 
-    /* meta data and index should exist */
-    if( b_strict )
-    {
-        struct stat st;
-        if( StatRelativeFile( p_access, "info", &st ) ||
-            StatRelativeFile( p_access, "index", &st ) )
-            return false;
-    }
-
     /* get all remaining parts */
     while( ImportNextFile( p_access ) )
         continue;
@@ -287,19 +267,16 @@ static int Control( access_t *p_access, int i_query, va_list args )
     switch( i_query )
     {
         case ACCESS_CAN_SEEK:
+        case ACCESS_CAN_FASTSEEK:
         case ACCESS_CAN_PAUSE:
         case ACCESS_CAN_CONTROL_PACE:
             *va_arg( args, bool* ) = true;
             break;
 
-        case ACCESS_CAN_FASTSEEK:
-            /* Seek() can open files, so it might be "too slow" */
-            *va_arg( args, bool* ) = false;
-            break;
-
         case ACCESS_GET_PTS_DELAY:
             pi64 = va_arg( args, int64_t * );
-            *pi64 = var_InheritInteger( p_access, "vdr-caching" ) * INT64_C(1000);
+            *pi64 = INT64_C(1000)
+                  * var_InheritInteger( p_access, "file-caching" );
             break;
 
         case ACCESS_SET_PAUSE_STATE:
@@ -387,7 +364,7 @@ static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
     {
         /* abort on read error */
         msg_Err( p_access, "failed to read (%m)" );
-        dialog_Fatal( p_access, _("File reading failed"), "%s",
+        dialog_Fatal( p_access, _("File reading failed"), "%s (%m)",
                       _("VLC could not read the file.") );
         SwitchFile( p_access, -1 );
         return 0;
@@ -412,8 +389,8 @@ static int Seek( access_t *p_access, uint64_t i_pos )
 
     /* find correct file */
     unsigned i_file = 0;
-    while( i_pos >= FILE_SIZE( i_file ) &&
-        i_file < FILE_COUNT - 1 )
+    while( i_file < FILE_COUNT - 1 &&
+           i_pos >= FILE_SIZE( i_file ) )
     {
         i_pos -= FILE_SIZE( i_file );
         i_file++;
@@ -558,7 +535,7 @@ static bool SwitchFile( access_t *p_access, unsigned i_file )
 
 error:
     dialog_Fatal (p_access, _("File reading failed"), _("VLC could not"
-        " open the file \"%s\"."), psz_path);
+        " open the file \"%s\". (%m)"), psz_path);
     if( p_sys->fd != -1 )
     {
         close( p_sys->fd );
@@ -612,27 +589,6 @@ static void UpdateFileSize( access_t *p_access )
     p_access->info.i_update |= INPUT_UPDATE_SIZE;
 }
 
-/*****************************************************************************
- * Stat file relative to base directory
- *****************************************************************************/
-static int StatRelativeFile( access_t *p_access, const char *psz_file,
-                              struct stat *p_stat )
-{
-    /* build path and add extension */
-    char *psz_path;
-    if( asprintf( &psz_path, "%s" DIR_SEP "%s%s",
-        p_access->psz_filepath, psz_file,
-        p_access->p_sys->b_ts_format ? "" : ".vdr" ) == -1 )
-        return -1;
-
-    int ret = vlc_stat( psz_path, p_stat );
-    if( ret )
-        msg_Dbg( p_access, "could not stat %s: %m", psz_path );
-    free( psz_path );
-
-    return ret;
-}
-
 /*****************************************************************************
  * Open file relative to base directory for reading.
  *****************************************************************************/
@@ -836,6 +792,16 @@ static void ImportMarks( access_t *p_access )
         return;
     }
 
+    /* get the length of this recording (index stores 8 bytes per frame) */
+    struct stat st;
+    if( fstat( fileno( indexfile ), &st ) )
+    {
+        fclose( marksfile );
+        fclose( indexfile );
+        return;
+    }
+    int64_t i_frame_count = st.st_size / 8;
+
     /* Put all cut marks in a "dummy" title */
     input_title_t *p_marks = vlc_input_title_New();
     if( !p_marks )
@@ -845,11 +811,20 @@ static void ImportMarks( access_t *p_access )
         return;
     }
     p_marks->psz_name = strdup( _("VDR Cut Marks") );
+    p_marks->i_length = i_frame_count * (int64_t)( CLOCK_FREQ / p_sys->fps );
+    p_marks->i_size = p_access->info.i_size;
 
     /* offset for chapter positions */
     int i_chapter_offset = p_sys->fps / 1000 *
         var_InheritInteger( p_access, "vdr-chapter-offset" );
 
+    /* minimum chapter size in frames */
+    int i_min_chapter_size = p_sys->fps * MIN_CHAPTER_SIZE;
+
+    /* the last chapter started at this frame (init to 0 so
+     * we skip useless chapters near the beginning as well) */
+    int64_t i_prev_chapter = 0;
+
     /* parse lines of the form "0:00:00.00 foobar" */
     char *line = NULL;
     size_t line_len;
@@ -857,6 +832,12 @@ static void ImportMarks( access_t *p_access )
     {
         int64_t i_frame = ParseFrameNumber( line, p_sys->fps );
 
+        /* skip chapters which are near the end or too close to each other */
+        if( i_frame - i_prev_chapter < i_min_chapter_size ||
+            i_frame >= i_frame_count - i_min_chapter_size )
+            continue;
+        i_prev_chapter = i_frame;
+
         /* move chapters (simple workaround for inaccurate cut marks) */
         if( i_frame > -i_chapter_offset )
             i_frame += i_chapter_offset;
@@ -884,7 +865,8 @@ static void ImportMarks( access_t *p_access )
         TAB_APPEND( p_marks->i_seekpoint, p_marks->seekpoint, sp );
     }
 
-    if( p_marks->i_seekpoint > 0 )
+    /* add a chapter at the beginning if missing */
+    if( p_marks->i_seekpoint > 0 && p_marks->seekpoint[0]->i_byte_offset > 0 )
     {
         seekpoint_t *sp = vlc_seekpoint_New();
         if( sp )
@@ -894,12 +876,12 @@ static void ImportMarks( access_t *p_access )
             sp->psz_name = strdup( _("Start") );
             TAB_INSERT( p_marks->i_seekpoint, p_marks->seekpoint, sp, 0 );
         }
-        p_sys->p_marks = p_marks;
     }
+
+    if( p_marks->i_seekpoint > 0 )
+        p_sys->p_marks = p_marks;
     else
-    {
         vlc_input_title_Delete( p_marks );
-    }
 
     fclose( marksfile );
     fclose( indexfile );
@@ -955,3 +937,21 @@ static int64_t ParseFrameNumber( const char *psz_line, float fps )
     int64_t i_frame = strtoll( psz_line, NULL, 10 );
     return __MAX(1, i_frame) - 1;
 }
+
+/*****************************************************************************
+ * Return the last path component (including trailing separators)
+ *****************************************************************************/
+static const char *BaseName( const char *psz_path )
+{
+    const char *psz_name = psz_path + strlen( psz_path );
+
+    /* skip superfluous separators at the end */
+    while( psz_name > psz_path && psz_name[-1] == DIR_SEP_CHAR )
+        --psz_name;
+
+    /* skip last component */
+    while( psz_name > psz_path && psz_name[-1] != DIR_SEP_CHAR )
+        --psz_name;
+
+    return psz_name;
+}