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