]> git.sesse.net Git - x264/blob - input/y4m.c
c34f264e16bba68523e7f5c52f835c9bbd6f9f35
[x264] / input / y4m.c
1 /*****************************************************************************
2  * y4m.c: x264 y4m input module
3  *****************************************************************************
4  * Copyright (C) 2003-2009 x264 project
5  *
6  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7  *          Loren Merritt <lorenm@u.washington.edu>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 #include "muxers.h"
25
26 typedef struct
27 {
28     FILE *fh;
29     int width, height;
30     int next_frame;
31     int seq_header_len, frame_header_len;
32     int frame_size;
33 } y4m_hnd_t;
34
35 #define Y4M_MAGIC "YUV4MPEG2"
36 #define MAX_YUV4_HEADER 80
37 #define Y4M_FRAME_MAGIC "FRAME"
38 #define MAX_FRAME_HEADER 80
39
40 static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
41 {
42     y4m_hnd_t *h = malloc( sizeof(y4m_hnd_t) );
43     int  i, n, d;
44     char header[MAX_YUV4_HEADER+10];
45     char *tokend, *header_end;
46     int colorspace = X264_CSP_NONE;
47     int alt_colorspace = X264_CSP_NONE;
48     if( !h )
49         return -1;
50
51     h->next_frame = 0;
52     info->vfr = 0;
53
54     if( !strcmp( psz_filename, "-" ) )
55         h->fh = stdin;
56     else
57         h->fh = fopen(psz_filename, "rb");
58     if( h->fh == NULL )
59         return -1;
60
61     h->frame_header_len = strlen( Y4M_FRAME_MAGIC )+1;
62
63     /* Read header */
64     for( i = 0; i < MAX_YUV4_HEADER; i++ )
65     {
66         header[i] = fgetc( h->fh );
67         if( header[i] == '\n' )
68         {
69             /* Add a space after last option. Makes parsing "444" vs
70                "444alpha" easier. */
71             header[i+1] = 0x20;
72             header[i+2] = 0;
73             break;
74         }
75     }
76     if( i == MAX_YUV4_HEADER || strncmp( header, Y4M_MAGIC, strlen( Y4M_MAGIC ) ) )
77         return -1;
78
79     /* Scan properties */
80     header_end = &header[i+1]; /* Include space */
81     h->seq_header_len = i+1;
82     for( char *tokstart = &header[strlen( Y4M_MAGIC )+1]; tokstart < header_end; tokstart++ )
83     {
84         if( *tokstart == 0x20 )
85             continue;
86         switch( *tokstart++ )
87         {
88             case 'W': /* Width. Required. */
89                 h->width = info->width = strtol( tokstart, &tokend, 10 );
90                 tokstart=tokend;
91                 break;
92             case 'H': /* Height. Required. */
93                 h->height = info->height = strtol( tokstart, &tokend, 10 );
94                 tokstart=tokend;
95                 break;
96             case 'C': /* Color space */
97                 if( !strncmp( "420", tokstart, 3 ) )
98                     colorspace = X264_CSP_I420;
99                 else
100                     colorspace = X264_CSP_MAX;      ///< anything other than 420 since we don't handle it
101                 tokstart = strchr( tokstart, 0x20 );
102                 break;
103             case 'I': /* Interlace type */
104                 switch( *tokstart++ )
105                 {
106                     case 'p': break;
107                     case '?':
108                     case 't':
109                     case 'b':
110                     case 'm':
111                     default:
112                         info->interlaced = 1;
113                 }
114                 break;
115             case 'F': /* Frame rate - 0:0 if unknown */
116                 if( sscanf( tokstart, "%d:%d", &n, &d ) == 2 && n && d )
117                 {
118                     x264_reduce_fraction( &n, &d );
119                     info->fps_num = n;
120                     info->fps_den = d;
121                 }
122                 tokstart = strchr( tokstart, 0x20 );
123                 break;
124             case 'A': /* Pixel aspect - 0:0 if unknown */
125                 /* Don't override the aspect ratio if sar has been explicitly set on the commandline. */
126                 if( sscanf( tokstart, "%d:%d", &n, &d ) == 2 && n && d )
127                 {
128                     x264_reduce_fraction( &n, &d );
129                     info->sar_width  = n;
130                     info->sar_height = d;
131                 }
132                 tokstart = strchr( tokstart, 0x20 );
133                 break;
134             case 'X': /* Vendor extensions */
135                 if( !strncmp( "YSCSS=", tokstart, 6 ) )
136                 {
137                     /* Older nonstandard pixel format representation */
138                     tokstart += 6;
139                     if( !strncmp( "420",tokstart, 3 ) )
140                         alt_colorspace = X264_CSP_I420;
141                     else
142                         alt_colorspace = X264_CSP_MAX;
143                 }
144                 tokstart = strchr( tokstart, 0x20 );
145                 break;
146         }
147     }
148
149     if( colorspace == X264_CSP_NONE )
150         colorspace = alt_colorspace;
151
152     // default to 4:2:0 if nothing is specified
153     if( colorspace == X264_CSP_NONE )
154         colorspace = X264_CSP_I420;
155
156     if( colorspace != X264_CSP_I420 )
157     {
158         fprintf( stderr, "y4m [error]: colorspace unhandled\n" );
159         return -1;
160     }
161
162     *p_handle = h;
163     return 0;
164 }
165
166 /* Most common case: frame_header = "FRAME" */
167 static int get_frame_total( hnd_t handle )
168 {
169     y4m_hnd_t *h = handle;
170     int i_frame_total = 0;
171
172     if( x264_is_regular_file( h->fh ) )
173     {
174         uint64_t init_pos = ftell( h->fh );
175         fseek( h->fh, 0, SEEK_END );
176         uint64_t i_size = ftell( h->fh );
177         fseek( h->fh, init_pos, SEEK_SET );
178         i_frame_total = (int)((i_size - h->seq_header_len) /
179                               (3*(h->width*h->height)/2+h->frame_header_len));
180     }
181
182     return i_frame_total;
183 }
184
185 static int read_frame_internal( x264_picture_t *p_pic, y4m_hnd_t *h )
186 {
187     int slen = strlen( Y4M_FRAME_MAGIC );
188     int i = 0;
189     char header[16];
190
191     /* Read frame header - without terminating '\n' */
192     if( fread( header, 1, slen, h->fh ) != slen )
193         return -1;
194
195     header[slen] = 0;
196     if( strncmp( header, Y4M_FRAME_MAGIC, slen ) )
197     {
198         fprintf( stderr, "y4m [error]: bad header magic (%"PRIx32" <=> %s)\n",
199                  M32(header), header );
200         return -1;
201     }
202
203     /* Skip most of it */
204     while( i < MAX_FRAME_HEADER && fgetc( h->fh ) != '\n' )
205         i++;
206     if( i == MAX_FRAME_HEADER )
207     {
208         fprintf( stderr, "y4m [error]: bad frame header!\n" );
209         return -1;
210     }
211     h->frame_header_len = i+slen+1;
212
213     if( fread( p_pic->img.plane[0], h->width * h->height, 1, h->fh ) <= 0
214      || fread( p_pic->img.plane[1], h->width * h->height / 4, 1, h->fh ) <= 0
215      || fread( p_pic->img.plane[2], h->width * h->height / 4, 1, h->fh ) <= 0 )
216         return -1;
217
218     return 0;
219 }
220
221 static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
222 {
223     y4m_hnd_t *h = handle;
224
225     if( i_frame > h->next_frame )
226     {
227         if( x264_is_regular_file( h->fh ) )
228             fseek( h->fh, (uint64_t)i_frame*(3*(h->width*h->height)/2+h->frame_header_len)
229                  + h->seq_header_len, SEEK_SET );
230         else
231             while( i_frame > h->next_frame )
232             {
233                 if( read_frame_internal( p_pic, h ) )
234                     return -1;
235                 h->next_frame++;
236             }
237     }
238
239     if( read_frame_internal( p_pic, h ) )
240         return -1;
241
242     h->next_frame = i_frame+1;
243     return 0;
244 }
245
246 static int close_file( hnd_t handle )
247 {
248     y4m_hnd_t *h = handle;
249     if( !h || !h->fh )
250         return 0;
251     fclose( h->fh );
252     free( h );
253     return 0;
254 }
255
256 const cli_input_t y4m_input = { open_file, get_frame_total, x264_picture_alloc, read_frame, NULL, x264_picture_clean, close_file };