/*****************************************************************************
* y4m.c: y4m input
*****************************************************************************
- * Copyright (C) 2003-2015 x264 project
+ * Copyright (C) 2003-2016 x264 project
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Loren Merritt <lorenm@u.washington.edu>
uint64_t frame_size;
uint64_t plane_size[3];
int bit_depth;
+ cli_mmap_t mmap;
+ int use_mmap;
} y4m_hnd_t;
#define Y4M_MAGIC "YUV4MPEG2"
break;
}
}
- if( i == MAX_YUV4_HEADER || strncmp( header, Y4M_MAGIC, sizeof(Y4M_MAGIC)-1 ) )
- return -1;
+ FAIL_IF_ERROR( strncmp( header, Y4M_MAGIC, sizeof(Y4M_MAGIC)-1 ), "bad sequence header magic\n" )
+ FAIL_IF_ERROR( i == MAX_YUV4_HEADER, "bad sequence header length\n" )
/* Scan properties */
header_end = &header[i+1]; /* Include space */
uint64_t i_size = ftell( h->fh );
fseek( h->fh, init_pos, SEEK_SET );
info->num_frames = (i_size - h->seq_header_len) / h->frame_size;
+
+ /* Attempt to use memory-mapped input frames if possible */
+ if( !(h->bit_depth & 7) )
+ h->use_mmap = !x264_cli_mmap_init( &h->mmap, h->fh );
}
*p_handle = h;
{
static const size_t slen = sizeof(Y4M_FRAME_MAGIC)-1;
int pixel_depth = x264_cli_csp_depth_factor( pic->img.csp );
- int i = 0;
- char header[16];
-
- /* Read frame header - without terminating '\n' */
- if( fread( header, 1, slen, h->fh ) != slen )
- return -1;
+ int i = sizeof(Y4M_FRAME_MAGIC);
+ char header_buf[16];
+ char *header;
- header[slen] = 0;
- FAIL_IF_ERROR( strncmp( header, Y4M_FRAME_MAGIC, slen ), "bad header magic (%"PRIx32" <=> %s)\n",
- M32(header), header )
-
- /* Skip most of it */
- while( i < MAX_FRAME_HEADER && fgetc( h->fh ) != '\n' )
- i++;
- FAIL_IF_ERROR( i == MAX_FRAME_HEADER, "bad frame header!\n" )
+ /* Verify that the frame header is valid */
+ if( h->use_mmap )
+ {
+ header = (char*)pic->img.plane[0];
+ pic->img.plane[0] += h->frame_header_len;
+
+ /* If the header length has changed between frames the size of the mapping will be invalid.
+ * It might be possible to work around it, but I'm not aware of any tool beside fuzzers that
+ * produces y4m files with variable-length frame headers so just error out if that happens. */
+ while( i <= h->frame_header_len && header[i-1] != '\n' )
+ i++;
+ FAIL_IF_ERROR( i != h->frame_header_len, "bad frame header length\n" )
+ }
+ else
+ {
+ header = header_buf;
+ if( fread( header, 1, slen, h->fh ) != slen )
+ return -1;
+ while( i <= MAX_FRAME_HEADER && fgetc( h->fh ) != '\n' )
+ i++;
+ FAIL_IF_ERROR( i > MAX_FRAME_HEADER, "bad frame header length\n" )
+ }
+ FAIL_IF_ERROR( memcmp( header, Y4M_FRAME_MAGIC, slen ), "bad frame header magic\n" )
- int error = 0;
- for( i = 0; i < pic->img.planes && !error; i++ )
+ for( i = 0; i < pic->img.planes; i++ )
{
- error |= fread( pic->img.plane[i], pixel_depth, h->plane_size[i], h->fh ) != h->plane_size[i];
+ if( h->use_mmap )
+ {
+ if( i )
+ pic->img.plane[i] = pic->img.plane[i-1] + pixel_depth * h->plane_size[i-1];
+ }
+ else if( fread( pic->img.plane[i], pixel_depth, h->plane_size[i], h->fh ) != h->plane_size[i] )
+ return -1;
+
if( bit_depth_uc )
{
/* upconvert non 16bit high depth planes to 16bit using the same
plane[j] = plane[j] << lshift;
}
}
- return error;
+ return 0;
}
static int read_frame( cli_pic_t *pic, hnd_t handle, int i_frame )
{
y4m_hnd_t *h = handle;
- if( i_frame > h->next_frame )
+ if( h->use_mmap )
+ {
+ pic->img.plane[0] = x264_cli_mmap( &h->mmap, h->frame_size * i_frame + h->seq_header_len, h->frame_size );
+ if( !pic->img.plane[0] )
+ return -1;
+ }
+ else if( i_frame > h->next_frame )
{
if( x264_is_regular_file( h->fh ) )
fseek( h->fh, h->frame_size * i_frame + h->seq_header_len, SEEK_SET );
return 0;
}
+static int release_frame( cli_pic_t *pic, hnd_t handle )
+{
+ y4m_hnd_t *h = handle;
+ if( h->use_mmap )
+ return x264_cli_munmap( &h->mmap, pic->img.plane[0] - h->frame_header_len, h->frame_size );
+ return 0;
+}
+
+static int picture_alloc( cli_pic_t *pic, hnd_t handle, int csp, int width, int height )
+{
+ y4m_hnd_t *h = handle;
+ return (h->use_mmap ? x264_cli_pic_init_noalloc : x264_cli_pic_alloc)( pic, csp, width, height );
+}
+
+static void picture_clean( cli_pic_t *pic, hnd_t handle )
+{
+ y4m_hnd_t *h = handle;
+ if( h->use_mmap )
+ memset( pic, 0, sizeof(cli_pic_t) );
+ else
+ x264_cli_pic_clean( pic );
+}
+
static int close_file( hnd_t handle )
{
y4m_hnd_t *h = handle;
if( !h || !h->fh )
return 0;
+ if( h->use_mmap )
+ x264_cli_mmap_close( &h->mmap );
fclose( h->fh );
free( h );
return 0;
}
-const cli_input_t y4m_input = { open_file, x264_cli_pic_alloc, read_frame, NULL, x264_cli_pic_clean, close_file };
+const cli_input_t y4m_input = { open_file, picture_alloc, read_frame, release_frame, picture_clean, close_file };