]> git.sesse.net Git - x264/blob - input/y4m.c
Various minor missing changes from previous commits
[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, x264_param_t *p_param )
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 *tokstart, *tokend, *header_end;
46     if( !h )
47         return -1;
48
49     h->next_frame = 0;
50
51     if( !strcmp( psz_filename, "-" ) )
52         h->fh = stdin;
53     else
54         h->fh = fopen(psz_filename, "rb");
55     if( h->fh == NULL )
56         return -1;
57
58     h->frame_header_len = strlen( Y4M_FRAME_MAGIC )+1;
59
60     /* Read header */
61     for( i = 0; i < MAX_YUV4_HEADER; i++ )
62     {
63         header[i] = fgetc( h->fh );
64         if( header[i] == '\n' )
65         {
66             /* Add a space after last option. Makes parsing "444" vs
67                "444alpha" easier. */
68             header[i+1] = 0x20;
69             header[i+2] = 0;
70             break;
71         }
72     }
73     if( i == MAX_YUV4_HEADER || strncmp( header, Y4M_MAGIC, strlen( Y4M_MAGIC ) ) )
74         return -1;
75
76     /* Scan properties */
77     header_end = &header[i+1]; /* Include space */
78     h->seq_header_len = i+1;
79     for( tokstart = &header[strlen( Y4M_MAGIC )+1]; tokstart < header_end; tokstart++ )
80     {
81         if( *tokstart == 0x20 )
82             continue;
83         switch( *tokstart++ )
84         {
85             case 'W': /* Width. Required. */
86                 h->width = p_param->i_width = strtol( tokstart, &tokend, 10 );
87                 tokstart=tokend;
88                 break;
89             case 'H': /* Height. Required. */
90                 h->height = p_param->i_height = strtol( tokstart, &tokend, 10 );
91                 tokstart=tokend;
92                 break;
93             case 'C': /* Color space */
94                 if( strncmp( "420", tokstart, 3 ) )
95                 {
96                     fprintf( stderr, "Colorspace unhandled\n" );
97                     return -1;
98                 }
99                 tokstart = strchr( tokstart, 0x20 );
100                 break;
101             case 'I': /* Interlace type */
102                 switch( *tokstart++ )
103                 {
104                     case 'p': break;
105                     case '?':
106                     case 't':
107                     case 'b':
108                     case 'm':
109                     default:
110                         fprintf( stderr, "Warning, this sequence might be interlaced\n" );
111                 }
112                 break;
113             case 'F': /* Frame rate - 0:0 if unknown */
114                 if( sscanf( tokstart, "%d:%d", &n, &d ) == 2 && n && d )
115                 {
116                     x264_reduce_fraction( &n, &d );
117                     p_param->i_fps_num = n;
118                     p_param->i_fps_den = d;
119                 }
120                 tokstart = strchr( tokstart, 0x20 );
121                 break;
122             case 'A': /* Pixel aspect - 0:0 if unknown */
123                 /* Don't override the aspect ratio if sar has been explicitly set on the commandline. */
124                 if( sscanf( tokstart, "%d:%d", &n, &d ) == 2 && n && d && !p_param->vui.i_sar_width && !p_param->vui.i_sar_height )
125                 {
126                     x264_reduce_fraction( &n, &d );
127                     p_param->vui.i_sar_width = n;
128                     p_param->vui.i_sar_height = d;
129                 }
130                 tokstart = strchr( tokstart, 0x20 );
131                 break;
132             case 'X': /* Vendor extensions */
133                 if( !strncmp( "YSCSS=", tokstart, 6 ) )
134                 {
135                     /* Older nonstandard pixel format representation */
136                     tokstart += 6;
137                     if( strncmp( "420JPEG",tokstart, 7 ) &&
138                         strncmp( "420MPEG2",tokstart, 8 ) &&
139                         strncmp( "420PALDV",tokstart, 8 ) )
140                     {
141                         fprintf( stderr, "Unsupported extended colorspace\n" );
142                         return -1;
143                     }
144                 }
145                 tokstart = strchr( tokstart, 0x20 );
146                 break;
147         }
148     }
149
150     fprintf( stderr, "yuv4mpeg: %ix%i@%i/%ifps, %i:%i\n",
151              h->width, h->height, p_param->i_fps_num, p_param->i_fps_den,
152              p_param->vui.i_sar_width, p_param->vui.i_sar_height );
153
154     *p_handle = h;
155     return 0;
156 }
157
158 /* Most common case: frame_header = "FRAME" */
159 static int get_frame_total( hnd_t handle )
160 {
161     y4m_hnd_t *h = handle;
162     int i_frame_total = 0;
163
164     if( x264_is_regular_file( h->fh ) )
165     {
166         uint64_t init_pos = ftell( h->fh );
167         fseek( h->fh, 0, SEEK_END );
168         uint64_t i_size = ftell( h->fh );
169         fseek( h->fh, init_pos, SEEK_SET );
170         i_frame_total = (int)((i_size - h->seq_header_len) /
171                               (3*(h->width*h->height)/2+h->frame_header_len));
172     }
173
174     return i_frame_total;
175 }
176
177 static int read_frame_internal( x264_picture_t *p_pic, y4m_hnd_t *h )
178 {
179     int slen = strlen( Y4M_FRAME_MAGIC );
180     int i = 0;
181     char header[16];
182
183     /* Read frame header - without terminating '\n' */
184     if( fread( header, 1, slen, h->fh ) != slen )
185         return -1;
186
187     header[slen] = 0;
188     if( strncmp( header, Y4M_FRAME_MAGIC, slen ) )
189     {
190         fprintf( stderr, "Bad header magic (%"PRIx32" <=> %s)\n",
191                  M32(header), header );
192         return -1;
193     }
194
195     /* Skip most of it */
196     while( i < MAX_FRAME_HEADER && fgetc( h->fh ) != '\n' )
197         i++;
198     if( i == MAX_FRAME_HEADER )
199     {
200         fprintf( stderr, "Bad frame header!\n" );
201         return -1;
202     }
203     h->frame_header_len = i+slen+1;
204
205     if( fread( p_pic->img.plane[0], h->width * h->height, 1, h->fh ) <= 0
206      || fread( p_pic->img.plane[1], h->width * h->height / 4, 1, h->fh ) <= 0
207      || fread( p_pic->img.plane[2], h->width * h->height / 4, 1, h->fh ) <= 0 )
208         return -1;
209
210     return 0;
211 }
212
213 static int read_frame( x264_picture_t *p_pic, hnd_t handle, int i_frame )
214 {
215     y4m_hnd_t *h = handle;
216
217     if( i_frame > h->next_frame )
218     {
219         if( x264_is_regular_file( h->fh ) )
220             fseek( h->fh, (uint64_t)i_frame*(3*(h->width*h->height)/2+h->frame_header_len)
221                  + h->seq_header_len, SEEK_SET );
222         else
223             while( i_frame > h->next_frame )
224             {
225                 if( read_frame_internal( p_pic, h ) )
226                     return -1;
227                 h->next_frame++;
228             }
229     }
230
231     if( read_frame_internal( p_pic, h ) )
232         return -1;
233
234     h->next_frame = i_frame+1;
235     return 0;
236 }
237
238 static int close_file( hnd_t handle )
239 {
240     y4m_hnd_t *h = handle;
241     if( !h || !h->fh )
242         return 0;
243     fclose( h->fh );
244     free( h );
245     return 0;
246 }
247
248 cli_input_t y4m_input = { open_file, get_frame_total, x264_picture_alloc, read_frame, NULL, x264_picture_clean, close_file };