cli: Use memory-mapped input frames for yuv and y4m
[x264/x264-sandbox.git] / input / raw.c
1 /*****************************************************************************
2  * raw.c: raw input
3  *****************************************************************************
4  * Copyright (C) 2003-2015 x264 project
5  *
6  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7  *          Loren Merritt <lorenm@u.washington.edu>
8  *          Steven Walters <kemuri9@gmail.com>
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., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
23  *
24  * This program is also available under a commercial proprietary license.
25  * For more information, contact us at licensing@x264.com.
26  *****************************************************************************/
27
28 #include "input.h"
29 #define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "raw", __VA_ARGS__ )
30
31 typedef struct
32 {
33     FILE *fh;
34     int next_frame;
35     uint64_t plane_size[4];
36     uint64_t frame_size;
37     int bit_depth;
38     cli_mmap_t mmap;
39     int use_mmap;
40 } raw_hnd_t;
41
42 static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
43 {
44     raw_hnd_t *h = calloc( 1, sizeof(raw_hnd_t) );
45     if( !h )
46         return -1;
47
48     if( !opt->resolution )
49     {
50         /* try to parse the file name */
51         for( char *p = psz_filename; *p; p++ )
52             if( *p >= '0' && *p <= '9' && sscanf( p, "%dx%d", &info->width, &info->height ) == 2 )
53                 break;
54     }
55     else
56         sscanf( opt->resolution, "%dx%d", &info->width, &info->height );
57     FAIL_IF_ERROR( !info->width || !info->height, "raw input requires a resolution.\n" )
58     if( opt->colorspace )
59     {
60         for( info->csp = X264_CSP_CLI_MAX-1; info->csp > X264_CSP_NONE; info->csp-- )
61         {
62             if( x264_cli_csps[info->csp].name && !strcasecmp( x264_cli_csps[info->csp].name, opt->colorspace ) )
63                 break;
64         }
65         FAIL_IF_ERROR( info->csp == X264_CSP_NONE, "unsupported colorspace `%s'\n", opt->colorspace );
66     }
67     else /* default */
68         info->csp = X264_CSP_I420;
69
70     h->bit_depth = opt->bit_depth;
71     FAIL_IF_ERROR( h->bit_depth < 8 || h->bit_depth > 16, "unsupported bit depth `%d'\n", h->bit_depth );
72     if( h->bit_depth > 8 )
73         info->csp |= X264_CSP_HIGH_DEPTH;
74
75     if( !strcmp( psz_filename, "-" ) )
76         h->fh = stdin;
77     else
78         h->fh = x264_fopen( psz_filename, "rb" );
79     if( h->fh == NULL )
80         return -1;
81
82     info->thread_safe = 1;
83     info->num_frames  = 0;
84     info->vfr         = 0;
85
86     const x264_cli_csp_t *csp = x264_cli_get_csp( info->csp );
87     for( int i = 0; i < csp->planes; i++ )
88     {
89         h->plane_size[i] = x264_cli_pic_plane_size( info->csp, info->width, info->height, i );
90         h->frame_size += h->plane_size[i];
91         /* x264_cli_pic_plane_size returns the size in bytes, we need the value in pixels from here on */
92         h->plane_size[i] /= x264_cli_csp_depth_factor( info->csp );
93     }
94
95     if( x264_is_regular_file( h->fh ) )
96     {
97         fseek( h->fh, 0, SEEK_END );
98         uint64_t size = ftell( h->fh );
99         fseek( h->fh, 0, SEEK_SET );
100         info->num_frames = size / h->frame_size;
101
102         /* Attempt to use memory-mapped input frames if possible */
103         if( !(h->bit_depth & 7) )
104             h->use_mmap = !x264_cli_mmap_init( &h->mmap, h->fh );
105     }
106
107     *p_handle = h;
108     return 0;
109 }
110
111 static int read_frame_internal( cli_pic_t *pic, raw_hnd_t *h, int bit_depth_uc )
112 {
113     int pixel_depth = x264_cli_csp_depth_factor( pic->img.csp );
114
115     for( int i = 0; i < pic->img.planes; i++ )
116     {
117         if( h->use_mmap )
118         {
119             if( i )
120                 pic->img.plane[i] = pic->img.plane[i-1] + pixel_depth * h->plane_size[i-1];
121         }
122         else if( fread( pic->img.plane[i], pixel_depth, h->plane_size[i], h->fh ) != h->plane_size[i] )
123             return -1;
124
125         if( bit_depth_uc )
126         {
127             /* upconvert non 16bit high depth planes to 16bit using the same
128              * algorithm as used in the depth filter. */
129             uint16_t *plane = (uint16_t*)pic->img.plane[i];
130             uint64_t pixel_count = h->plane_size[i];
131             int lshift = 16 - h->bit_depth;
132             for( uint64_t j = 0; j < pixel_count; j++ )
133                 plane[j] = plane[j] << lshift;
134         }
135     }
136     return 0;
137 }
138
139 static int read_frame( cli_pic_t *pic, hnd_t handle, int i_frame )
140 {
141     raw_hnd_t *h = handle;
142
143     if( h->use_mmap )
144     {
145         pic->img.plane[0] = x264_cli_mmap( &h->mmap, i_frame * h->frame_size, h->frame_size );
146         if( !pic->img.plane[0] )
147             return -1;
148     }
149     else if( i_frame > h->next_frame )
150     {
151         if( x264_is_regular_file( h->fh ) )
152             fseek( h->fh, i_frame * h->frame_size, SEEK_SET );
153         else
154             while( i_frame > h->next_frame )
155             {
156                 if( read_frame_internal( pic, h, 0 ) )
157                     return -1;
158                 h->next_frame++;
159             }
160     }
161
162     if( read_frame_internal( pic, h, h->bit_depth & 7 ) )
163         return -1;
164
165     h->next_frame = i_frame+1;
166     return 0;
167 }
168
169 static int release_frame( cli_pic_t *pic, hnd_t handle )
170 {
171     raw_hnd_t *h = handle;
172     if( h->use_mmap )
173         return x264_cli_munmap( &h->mmap, pic->img.plane[0], h->frame_size );
174     return 0;
175 }
176
177 static int picture_alloc( cli_pic_t *pic, hnd_t handle, int csp, int width, int height )
178 {
179     raw_hnd_t *h = handle;
180     return (h->use_mmap ? x264_cli_pic_init_noalloc : x264_cli_pic_alloc)( pic, csp, width, height );
181 }
182
183 static void picture_clean( cli_pic_t *pic, hnd_t handle )
184 {
185     raw_hnd_t *h = handle;
186     if( h->use_mmap )
187         memset( pic, 0, sizeof(cli_pic_t) );
188     else
189         x264_cli_pic_clean( pic );
190 }
191
192 static int close_file( hnd_t handle )
193 {
194     raw_hnd_t *h = handle;
195     if( !h || !h->fh )
196         return 0;
197     if( h->use_mmap )
198         x264_cli_mmap_close( &h->mmap );
199     fclose( h->fh );
200     free( h );
201     return 0;
202 }
203
204 const cli_input_t raw_input = { open_file, picture_alloc, read_frame, release_frame, picture_clean, close_file };