*****************************************************************************
* 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.
*****************************************************************************/
/***
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.
***/
# 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
#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>
#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." )
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,
* 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
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 );
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
* 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;
}
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 );
/*****************************************************************************
* 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;
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;
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:
{
/* 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;
/* 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++;
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 );
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.
*****************************************************************************/
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 )
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;
{
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;
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 )
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 );
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;
+}