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