1 /*
2  *  yuv4mpeg.c:  Functions for reading and writing "new" YUV4MPEG streams
3  *
4  *  Copyright (C) 2001 Matthew J. Marjanovic <maddog@mir.com>
5  *
6  *
7  *  This program is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU General Public License
9  *  as published by the Free Software Foundation; either version 2
10  *  of the License, or (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  */
22 #define ADM_LEGACY_PROGGY
23 #include "ADM_default.h"
24 #define INTERNAL_Y4M_LIBCODE_STUFF_QPX
25 #include "yuv4mpeg.h"
26 #include "yuv4mpeg_intern.h"
27 #include "mjpeg_logging.h"
28 
29 
30 static int _y4mparam_allow_unknown_tags = 1;  /* default is forgiveness */
31 static int _y4mparam_feature_level = 0;       /* default is ol YUV4MPEG2 */
32 
33 static void *(*_y4m_alloc)(size_t bytes) = malloc;
34 static void (*_y4m_free)(void *ptr) = free;
35 
36 
37 
y4m_allow_unknown_tags(int yn)38 int y4m_allow_unknown_tags(int yn)
39 {
40   int old = _y4mparam_allow_unknown_tags;
41   if (yn >= 0)
42     _y4mparam_allow_unknown_tags = (yn) ? 1 : 0;
43   return old;
44 }
45 
46 
y4m_accept_extensions(int level)47 int y4m_accept_extensions(int level)
48 {
49   int old = _y4mparam_feature_level;
50   if (level >= 0)
51     _y4mparam_feature_level = level;
52   return old;
53 }
54 
55 
56 
57 /*************************************************************************
58  *
59  * Convenience functions for fd read/write
60  *
61  *   - guaranteed to transfer entire payload (or fail)
62  *   - returns:
63  *               0 on complete success
64  *               +(# of remaining bytes) on eof (for y4m_read)
65  *               -(# of rem. bytes) on error (and ERRNO should be set)
66  *
67  *************************************************************************/
68 
69 
y4m_read(int fd,void * buf,size_t len)70 ssize_t y4m_read(int fd, void *buf, size_t len)
71 {
72    ssize_t n;
73    uint8_t *ptr = (uint8_t *)buf;
74 
75    while (len > 0) {
76      n = read(fd, ptr, len);
77      if (n <= 0) {
78        /* return amount left to read */
79        if (n == 0)
80 	 return len;  /* n == 0 --> eof */
81        else
82 	 return -len; /* n < 0 --> error */
83      }
84      ptr += n;
85      len -= n;
86    }
87    return 0;
88 }
89 
90 
y4m_write(int fd,const void * buf,size_t len)91 ssize_t y4m_write(int fd, const void *buf, size_t len)
92 {
93    ssize_t n;
94    const uint8_t *ptr = (const uint8_t *)buf;
95 
96    while (len > 0) {
97      n = write(fd, ptr, len);
98      if (n <= 0) return -len;  /* return amount left to write */
99      ptr += n;
100      len -= n;
101    }
102    return 0;
103 }
104 
105 
106 
107 
108 /*************************************************************************
109  *
110  * "Extra tags" handling
111  *
112  *************************************************************************/
113 
114 
y4m_new_xtag(void)115 static char *y4m_new_xtag(void)
116 {
117   return _y4m_alloc(Y4M_MAX_XTAG_SIZE * sizeof(char));
118 }
119 
120 
y4m_init_xtag_list(y4m_xtag_list_t * xtags)121 void y4m_init_xtag_list(y4m_xtag_list_t *xtags)
122 {
123   int i;
124   xtags->count = 0;
125   for (i = 0; i < Y4M_MAX_XTAGS; i++) {
126     xtags->tags[i] = NULL;
127   }
128 }
129 
130 
y4m_fini_xtag_list(y4m_xtag_list_t * xtags)131 void y4m_fini_xtag_list(y4m_xtag_list_t *xtags)
132 {
133   int i;
134   for (i = 0; i < Y4M_MAX_XTAGS; i++) {
135     if (xtags->tags[i] != NULL) {
136       _y4m_free(xtags->tags[i]);
137       xtags->tags[i] = NULL;
138     }
139   }
140   xtags->count = 0;
141 }
142 
143 
y4m_copy_xtag_list(y4m_xtag_list_t * dest,const y4m_xtag_list_t * src)144 void y4m_copy_xtag_list(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src)
145 {
146   int i;
147   for (i = 0; i < src->count; i++) {
148     if (dest->tags[i] == NULL)
149       dest->tags[i] = y4m_new_xtag();
150     strncpy(dest->tags[i], src->tags[i], Y4M_MAX_XTAG_SIZE);
151   }
152   dest->count = src->count;
153 }
154 
155 
156 
y4m_snprint_xtags(char * s,int maxn,const y4m_xtag_list_t * xtags)157 static int y4m_snprint_xtags(char *s, int maxn, const y4m_xtag_list_t *xtags)
158 {
159   int i, room;
160 
161   for (i = 0, room = maxn - 1; i < xtags->count; i++) {
162     int n = snprintf(s, room + 1, " %s", xtags->tags[i]);
163     if ((n < 0) || (n > room)) return Y4M_ERR_HEADER;
164     s += n;
165     room -= n;
166   }
167   s[0] = '\n';  /* finish off header with newline */
168   s[1] = '\0';  /* ...and end-of-string           */
169   return Y4M_OK;
170 }
171 
172 
y4m_xtag_count(const y4m_xtag_list_t * xtags)173 int y4m_xtag_count(const y4m_xtag_list_t *xtags)
174 {
175   return xtags->count;
176 }
177 
178 
y4m_xtag_get(const y4m_xtag_list_t * xtags,int n)179 const char *y4m_xtag_get(const y4m_xtag_list_t *xtags, int n)
180 {
181   if (n >= xtags->count)
182     return NULL;
183   else
184     return xtags->tags[n];
185 }
186 
187 
y4m_xtag_add(y4m_xtag_list_t * xtags,const char * tag)188 int y4m_xtag_add(y4m_xtag_list_t *xtags, const char *tag)
189 {
190   if (xtags->count >= Y4M_MAX_XTAGS) return Y4M_ERR_XXTAGS;
191   if (xtags->tags[xtags->count] == NULL)
192     xtags->tags[xtags->count] = y4m_new_xtag();
193   strncpy(xtags->tags[xtags->count], tag, Y4M_MAX_XTAG_SIZE);
194   (xtags->count)++;
195   return Y4M_OK;
196 }
197 
198 
y4m_xtag_remove(y4m_xtag_list_t * xtags,int n)199 int y4m_xtag_remove(y4m_xtag_list_t *xtags, int n)
200 {
201   int i;
202   char *q;
203 
204   if ((n < 0) || (n >= xtags->count)) return Y4M_ERR_RANGE;
205   q = xtags->tags[n];
206   for (i = n; i < (xtags->count - 1); i++)
207     xtags->tags[i] = xtags->tags[i+1];
208   xtags->tags[i] = q;
209   (xtags->count)--;
210   return Y4M_OK;
211 }
212 
213 
y4m_xtag_clearlist(y4m_xtag_list_t * xtags)214 int y4m_xtag_clearlist(y4m_xtag_list_t *xtags)
215 {
216   xtags->count = 0;
217   return Y4M_OK;
218 }
219 
220 
y4m_xtag_addlist(y4m_xtag_list_t * dest,const y4m_xtag_list_t * src)221 int y4m_xtag_addlist(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src)
222 {
223   int i, j;
224 
225   if ((dest->count + src->count) > Y4M_MAX_XTAGS) return Y4M_ERR_XXTAGS;
226   for (i = dest->count, j = 0;
227        j < src->count;
228        i++, j++) {
229     if (dest->tags[i] == NULL)
230       dest->tags[i] = y4m_new_xtag();
231     strncpy(dest->tags[i], src->tags[i], Y4M_MAX_XTAG_SIZE);
232   }
233   dest->count += src->count;
234   return Y4M_OK;
235 }
236 
237 
238 /*************************************************************************
239  *
240  * Creators/destructors for y4m_*_info_t structures
241  *
242  *************************************************************************/
243 
244 
y4m_init_stream_info(y4m_stream_info_t * info)245 void y4m_init_stream_info(y4m_stream_info_t *info)
246 {
247   if (info == NULL) return;
248   /* init substructures */
249   y4m_init_xtag_list(&(info->x_tags));
250   /* set defaults */
251   y4m_clear_stream_info(info);
252 }
253 
254 
y4m_clear_stream_info(y4m_stream_info_t * info)255 void y4m_clear_stream_info(y4m_stream_info_t *info)
256 {
257   if (info == NULL) return;
258   /* clear/initialize info */
259   info->width = Y4M_UNKNOWN;
260   info->height = Y4M_UNKNOWN;
261   info->interlace = Y4M_UNKNOWN;
262   info->framerate = y4m_fps_UNKNOWN;
263   info->sampleaspect = y4m_sar_UNKNOWN;
264   if (_y4mparam_feature_level < 1) {
265     info->chroma = Y4M_CHROMA_420JPEG;
266   } else {
267     info->chroma = Y4M_UNKNOWN;
268   }
269   y4m_xtag_clearlist(&(info->x_tags));
270 }
271 
272 
y4m_copy_stream_info(y4m_stream_info_t * dest,const y4m_stream_info_t * src)273 void y4m_copy_stream_info(y4m_stream_info_t *dest,
274 			  const y4m_stream_info_t *src)
275 {
276   if ((dest == NULL) || (src == NULL)) return;
277   /* copy info */
278   dest->width = src->width;
279   dest->height = src->height;
280   dest->interlace = src->interlace;
281   dest->framerate = src->framerate;
282   dest->sampleaspect = src->sampleaspect;
283   dest->chroma = src->chroma;
284   y4m_copy_xtag_list(&(dest->x_tags), &(src->x_tags));
285 }
286 
287 
y4m_fini_stream_info(y4m_stream_info_t * info)288 void y4m_fini_stream_info(y4m_stream_info_t *info)
289 {
290   if (info == NULL) return;
291   y4m_fini_xtag_list(&(info->x_tags));
292 }
293 
294 
y4m_si_set_width(y4m_stream_info_t * si,int width)295 void y4m_si_set_width(y4m_stream_info_t *si, int width)
296 {
297   si->width = width;
298 }
299 
y4m_si_get_width(const y4m_stream_info_t * si)300 int y4m_si_get_width(const y4m_stream_info_t *si)
301 { return si->width; }
302 
y4m_si_set_height(y4m_stream_info_t * si,int height)303 void y4m_si_set_height(y4m_stream_info_t *si, int height)
304 {
305   si->height = height;
306 }
307 
y4m_si_get_height(const y4m_stream_info_t * si)308 int y4m_si_get_height(const y4m_stream_info_t *si)
309 { return si->height; }
310 
y4m_si_set_interlace(y4m_stream_info_t * si,int interlace)311 void y4m_si_set_interlace(y4m_stream_info_t *si, int interlace)
312 { si->interlace = interlace; }
313 
y4m_si_get_interlace(const y4m_stream_info_t * si)314 int y4m_si_get_interlace(const y4m_stream_info_t *si)
315 { return si->interlace; }
316 
y4m_si_set_framerate(y4m_stream_info_t * si,y4m_ratio_t framerate)317 void y4m_si_set_framerate(y4m_stream_info_t *si, y4m_ratio_t framerate)
318 { si->framerate = framerate; }
319 
y4m_si_get_framerate(const y4m_stream_info_t * si)320 y4m_ratio_t y4m_si_get_framerate(const y4m_stream_info_t *si)
321 { return si->framerate; }
322 
y4m_si_set_sampleaspect(y4m_stream_info_t * si,y4m_ratio_t sar)323 void y4m_si_set_sampleaspect(y4m_stream_info_t *si, y4m_ratio_t sar)
324 { si->sampleaspect = sar; }
325 
y4m_si_get_sampleaspect(const y4m_stream_info_t * si)326 y4m_ratio_t y4m_si_get_sampleaspect(const y4m_stream_info_t *si)
327 { return si->sampleaspect; }
328 
y4m_si_set_chroma(y4m_stream_info_t * si,int chroma_mode)329 void y4m_si_set_chroma(y4m_stream_info_t *si, int chroma_mode)
330 { si->chroma = chroma_mode; }
331 
y4m_si_get_chroma(const y4m_stream_info_t * si)332 int y4m_si_get_chroma(const y4m_stream_info_t *si)
333 { return si->chroma; }
334 
335 
y4m_si_get_plane_count(const y4m_stream_info_t * si)336 int y4m_si_get_plane_count(const y4m_stream_info_t *si)
337 {
338   switch (si->chroma) {
339   case Y4M_CHROMA_420JPEG:
340   case Y4M_CHROMA_420MPEG2:
341   case Y4M_CHROMA_420PALDV:
342   case Y4M_CHROMA_444:
343   case Y4M_CHROMA_422:
344   case Y4M_CHROMA_411:
345     return 3;
346   case Y4M_CHROMA_MONO:
347     return 1;
348   case Y4M_CHROMA_444ALPHA:
349     return 4;
350   default:
351     return Y4M_UNKNOWN;
352   }
353 }
354 
y4m_si_get_plane_width(const y4m_stream_info_t * si,int plane)355 int y4m_si_get_plane_width(const y4m_stream_info_t *si, int plane)
356 {
357   switch (plane) {
358   case 0:
359     return (si->width);
360   case 1:
361   case 2:
362     switch (si->chroma) {
363     case Y4M_CHROMA_420JPEG:
364     case Y4M_CHROMA_420MPEG2:
365     case Y4M_CHROMA_420PALDV:
366       return (si->width) / 2;
367     case Y4M_CHROMA_444:
368     case Y4M_CHROMA_444ALPHA:
369       return (si->width);
370     case Y4M_CHROMA_422:
371       return (si->width) / 2;
372     case Y4M_CHROMA_411:
373       return (si->width) / 4;
374     default:
375       return Y4M_UNKNOWN;
376     }
377   case 3:
378     switch (si->chroma) {
379     case Y4M_CHROMA_444ALPHA:
380       return (si->width);
381     default:
382       return Y4M_UNKNOWN;
383     }
384   default:
385     return Y4M_UNKNOWN;
386   }
387 }
388 
y4m_si_get_plane_height(const y4m_stream_info_t * si,int plane)389 int y4m_si_get_plane_height(const y4m_stream_info_t *si, int plane)
390 {
391   switch (plane) {
392   case 0:
393     return (si->height);
394   case 1:
395   case 2:
396     switch (si->chroma) {
397     case Y4M_CHROMA_420JPEG:
398     case Y4M_CHROMA_420MPEG2:
399     case Y4M_CHROMA_420PALDV:
400       return (si->height) / 2;
401     case Y4M_CHROMA_444:
402     case Y4M_CHROMA_444ALPHA:
403     case Y4M_CHROMA_422:
404     case Y4M_CHROMA_411:
405       return (si->height);
406     default:
407       return Y4M_UNKNOWN;
408     }
409   case 3:
410     switch (si->chroma) {
411     case Y4M_CHROMA_444ALPHA:
412       return (si->height);
413     default:
414       return Y4M_UNKNOWN;
415     }
416   default:
417     return Y4M_UNKNOWN;
418   }
419 }
420 
y4m_si_get_plane_length(const y4m_stream_info_t * si,int plane)421 int y4m_si_get_plane_length(const y4m_stream_info_t *si, int plane)
422 {
423   int w = y4m_si_get_plane_width(si, plane);
424   int h = y4m_si_get_plane_height(si, plane);
425   if ((w != Y4M_UNKNOWN) && (h != Y4M_UNKNOWN))
426     return (w * h);
427   else
428     return Y4M_UNKNOWN;
429 }
430 
y4m_si_get_framelength(const y4m_stream_info_t * si)431 int y4m_si_get_framelength(const y4m_stream_info_t *si)
432 {
433   int total = 0;
434   int planes = y4m_si_get_plane_count(si);
435   int p;
436   for (p = 0; p < planes; p++) {
437     int plen = y4m_si_get_plane_length(si, p);
438     if (plen == Y4M_UNKNOWN) return Y4M_UNKNOWN;
439     total += plen;
440   }
441   return total;
442 }
443 
444 
y4m_si_xtags(y4m_stream_info_t * si)445 y4m_xtag_list_t *y4m_si_xtags(y4m_stream_info_t *si)
446 { return &(si->x_tags); }
447 
448 
449 
y4m_init_frame_info(y4m_frame_info_t * info)450 void y4m_init_frame_info(y4m_frame_info_t *info)
451 {
452   if (info == NULL) return;
453   /* init substructures */
454   y4m_init_xtag_list(&(info->x_tags));
455   /* set defaults */
456   y4m_clear_frame_info(info);
457 }
458 
459 
y4m_clear_frame_info(y4m_frame_info_t * info)460 void y4m_clear_frame_info(y4m_frame_info_t *info)
461 {
462   if (info == NULL) return;
463   /* clear/initialize info */
464   info->spatial = Y4M_UNKNOWN;
465   info->temporal = Y4M_UNKNOWN;
466   info->presentation = Y4M_UNKNOWN;
467   y4m_xtag_clearlist(&(info->x_tags));
468 }
469 
470 
y4m_copy_frame_info(y4m_frame_info_t * dest,const y4m_frame_info_t * src)471 void y4m_copy_frame_info(y4m_frame_info_t *dest, const y4m_frame_info_t *src)
472 {
473   if ((dest == NULL) || (src == NULL)) return;
474   /* copy info */
475   dest->spatial = src->spatial;
476   dest->temporal = src->temporal;
477   dest->presentation = src->presentation;
478   y4m_copy_xtag_list(&(dest->x_tags), &(src->x_tags));
479 }
480 
y4m_fini_frame_info(y4m_frame_info_t * info)481 void y4m_fini_frame_info(y4m_frame_info_t *info)
482 {
483   if (info == NULL) return;
484   y4m_fini_xtag_list(&(info->x_tags));
485 }
486 
487 
y4m_fi_set_presentation(y4m_frame_info_t * fi,int pres)488 void y4m_fi_set_presentation(y4m_frame_info_t *fi, int pres)
489 { fi->presentation = pres; }
490 
y4m_fi_get_presentation(const y4m_frame_info_t * fi)491 int y4m_fi_get_presentation(const y4m_frame_info_t *fi)
492 { return fi->presentation; }
493 
y4m_fi_set_temporal(y4m_frame_info_t * fi,int sampling)494 void y4m_fi_set_temporal(y4m_frame_info_t *fi, int sampling)
495 { fi->temporal = sampling; }
496 
y4m_fi_get_temporal(const y4m_frame_info_t * fi)497 int y4m_fi_get_temporal(const y4m_frame_info_t *fi)
498 { return fi->temporal; }
499 
y4m_fi_set_spatial(y4m_frame_info_t * fi,int sampling)500 void y4m_fi_set_spatial(y4m_frame_info_t *fi, int sampling)
501 { fi->spatial = sampling; }
502 
y4m_fi_get_spatial(const y4m_frame_info_t * fi)503 int y4m_fi_get_spatial(const y4m_frame_info_t *fi)
504 { return fi->spatial; }
505 
506 
507 
y4m_fi_xtags(y4m_frame_info_t * fi)508 y4m_xtag_list_t *y4m_fi_xtags(y4m_frame_info_t *fi)
509 { return &(fi->x_tags); }
510 
511 
512 /*************************************************************************
513  *
514  * Tag parsing
515  *
516  *************************************************************************/
517 
518 
519 /* Parse (the first) old, unofficial X-tag chroma specification,
520    and then remove that tag from the X-tag list. */
521 static int
handle_old_chroma_xtag(y4m_stream_info_t * si)522 handle_old_chroma_xtag(y4m_stream_info_t *si)
523 {
524   y4m_xtag_list_t *xtags = y4m_si_xtags(si);
525   const char *tag = NULL;
526   int n, chroma;
527 
528   for (n = y4m_xtag_count(xtags) - 1; n >= 0; n--) {
529     tag = y4m_xtag_get(xtags, n);
530     if (!strncmp("XYSCSS=", tag, 7)) break;
531   }
532   if ((tag == NULL) || (n < 0)) return Y4M_UNKNOWN;
533   mjpeg_warn("Deprecated X-tag for chroma found in a stream header...");
534   mjpeg_warn("...pester someone to upgrade the source's program!");
535   /* parse the tag */
536   tag += 7;
537   if (!strcmp("411", tag))           chroma = Y4M_CHROMA_411;
538   else if (!strcmp(tag, "420"))      chroma = Y4M_CHROMA_420JPEG;
539   else if (!strcmp(tag, "420MPEG2")) chroma = Y4M_CHROMA_420MPEG2;
540   else if (!strcmp(tag, "420PALDV")) chroma = Y4M_CHROMA_420PALDV;
541   else if (!strcmp(tag, "420JPEG"))  chroma = Y4M_CHROMA_420JPEG;
542   else if (!strcmp(tag, "444"))      chroma = Y4M_CHROMA_444;
543   else chroma = Y4M_UNKNOWN;
544   /* Remove the 'X' tag so that no one has to worry about it any more. */
545   y4m_xtag_remove(xtags, n);
546   /* Hmm... what if there are more XYSCSS tags?  Broken is as broken does;
547      thank goodness this is temporary code. */
548   return chroma;
549 }
550 
551 
552 
553 
y4m_parse_stream_tags(char * s,y4m_stream_info_t * i)554 int y4m_parse_stream_tags(char *s, y4m_stream_info_t *i)
555 {
556   char *token, *value;
557   char tag;
558   int err;
559 
560   /* parse fields */
561   for (token = strtok(s, Y4M_DELIM);
562        token != NULL;
563        token = strtok(NULL, Y4M_DELIM)) {
564     if (token[0] == '\0') continue;   /* skip empty strings */
565     tag = token[0];
566     value = token + 1;
567     switch (tag) {
568     case 'W':  /* width */
569       i->width = atoi(value);
570       if (i->width <= 0) return Y4M_ERR_RANGE;
571       break;
572     case 'H':  /* height */
573       i->height = atoi(value);
574       if (i->height <= 0) return Y4M_ERR_RANGE;
575       break;
576     case 'F':  /* frame rate (fps) */
577       if ((err = y4m_parse_ratio(&(i->framerate), value)) != Y4M_OK)
578 	return err;
579       if (i->framerate.n < 0) return Y4M_ERR_RANGE;
580       break;
581     case 'I':  /* interlacing */
582       switch (value[0]) {
583       case 'p':  i->interlace = Y4M_ILACE_NONE; break;
584       case 't':  i->interlace = Y4M_ILACE_TOP_FIRST; break;
585       case 'b':  i->interlace = Y4M_ILACE_BOTTOM_FIRST; break;
586       case 'm':  i->interlace = Y4M_ILACE_MIXED; break;
587       case '?':
588       default:
589 	i->interlace = Y4M_UNKNOWN; break;
590       }
591       break;
592     case 'A':  /* sample (pixel) aspect ratio */
593       if ((err = y4m_parse_ratio(&(i->sampleaspect), value)) != Y4M_OK)
594 	return err;
595       if (i->sampleaspect.n < 0) return Y4M_ERR_RANGE;
596       break;
597     case 'C':
598       i->chroma = y4m_chroma_parse_keyword(value);
599       if (i->chroma == Y4M_UNKNOWN)
600 	return Y4M_ERR_HEADER;
601       break;
602     case 'X':  /* 'X' meta-tag */
603       if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err;
604       break;
605     default:
606       /* possible error on unknown options */
607       if (_y4mparam_allow_unknown_tags) {
608 	/* unknown tags ok:  store in xtag list and warn... */
609 	if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err;
610 	mjpeg_warn("Unknown stream tag encountered:  '%s'", token);
611       } else {
612 	/* unknown tags are *not* ok */
613 	return Y4M_ERR_BADTAG;
614       }
615       break;
616     }
617   }
618 
619   /* If feature_level > 0, then handle and/or remove any old-style XYSCSS
620      chroma tags.  The new-style 'C' tag takes precedence, however. */
621   if (_y4mparam_feature_level > 0) {
622     int xt_chroma = handle_old_chroma_xtag(i);
623 
624     if (i->chroma == Y4M_UNKNOWN)
625       i->chroma = xt_chroma;
626     else if ((xt_chroma != Y4M_UNKNOWN) &&
627              (xt_chroma != i->chroma))
628       mjpeg_warn("Old chroma X-tag (ignored) does not match new chroma tag.");
629   }
630 
631   /* Without 'C' tag or any other chroma spec, default to 420jpeg */
632   if (i->chroma == Y4M_UNKNOWN)
633     i->chroma = Y4M_CHROMA_420JPEG;
634 
635   /* Error checking... */
636   /*      - Width and Height are required. */
637   if ((i->width == Y4M_UNKNOWN) || (i->height == Y4M_UNKNOWN))
638     return Y4M_ERR_HEADER;
639   /*      - Non-420 chroma and mixed interlace require level >= 1 */
640   if (_y4mparam_feature_level < 1) {
641     if ((i->chroma != Y4M_CHROMA_420JPEG) &&
642 	(i->chroma != Y4M_CHROMA_420MPEG2) &&
643 	(i->chroma != Y4M_CHROMA_420PALDV))
644       return Y4M_ERR_FEATURE;
645     if (i->interlace == Y4M_ILACE_MIXED)
646       return Y4M_ERR_FEATURE;
647   }
648 
649   /* ta da!  done. */
650   return Y4M_OK;
651 }
652 
653 
654 
y4m_parse_frame_tags(char * s,const y4m_stream_info_t * si,y4m_frame_info_t * fi)655 static int y4m_parse_frame_tags(char *s, const y4m_stream_info_t *si,
656 				y4m_frame_info_t *fi)
657 {
658   char *token, *value;
659   char tag;
660   int err;
661 
662   /* parse fields */
663   for (token = strtok(s, Y4M_DELIM);
664        token != NULL;
665        token = strtok(NULL, Y4M_DELIM)) {
666     if (token[0] == '\0') continue;   /* skip empty strings */
667     tag = token[0];
668     value = token + 1;
669     switch (tag) {
670     case 'I':
671       /* frame 'I' tag requires feature level >= 1 */
672       if (_y4mparam_feature_level < 1) return Y4M_ERR_FEATURE;
673       if (si->interlace != Y4M_ILACE_MIXED) return Y4M_ERR_BADTAG;
674       switch (value[0]) {
675       case 't':  fi->presentation = Y4M_PRESENT_TOP_FIRST;        break;
676       case 'T':  fi->presentation = Y4M_PRESENT_TOP_FIRST_RPT;    break;
677       case 'b':  fi->presentation = Y4M_PRESENT_BOTTOM_FIRST;     break;
678       case 'B':  fi->presentation = Y4M_PRESENT_BOTTOM_FIRST_RPT; break;
679       case '1':  fi->presentation = Y4M_PRESENT_PROG_SINGLE;      break;
680       case '2':  fi->presentation = Y4M_PRESENT_PROG_DOUBLE;      break;
681       case '3':  fi->presentation = Y4M_PRESENT_PROG_TRIPLE;      break;
682       default:
683 	return Y4M_ERR_BADTAG;
684       }
685       switch (value[1]) {
686       case 'p':  fi->temporal = Y4M_SAMPLING_PROGRESSIVE; break;
687       case 'i':  fi->temporal = Y4M_SAMPLING_INTERLACED;  break;
688       default:
689 	return Y4M_ERR_BADTAG;
690       }
691       switch (value[2]) {
692       case 'p':  fi->spatial = Y4M_SAMPLING_PROGRESSIVE; break;
693       case 'i':  fi->spatial = Y4M_SAMPLING_INTERLACED;  break;
694       case '?':  fi->spatial = Y4M_UNKNOWN;              break;
695       default:
696 	return Y4M_ERR_BADTAG;
697       }
698       break;
699     case 'X':  /* 'X' meta-tag */
700       if ((err = y4m_xtag_add(&(fi->x_tags), token)) != Y4M_OK) return err;
701       break;
702     default:
703       /* possible error on unknown options */
704       if (_y4mparam_allow_unknown_tags) {
705 	/* unknown tags ok:  store in xtag list and warn... */
706 	if ((err = y4m_xtag_add(&(fi->x_tags), token)) != Y4M_OK) return err;
707 	mjpeg_warn("Unknown frame tag encountered:  '%s'", token);
708       } else {
709 	/* unknown tags are *not* ok */
710 	return Y4M_ERR_BADTAG;
711       }
712       break;
713     }
714   }
715   /* error-checking and/or non-mixed defaults */
716   switch (si->interlace) {
717   case Y4M_ILACE_MIXED:
718     /* T and P are required if stream "Im" */
719     if ((fi->presentation == Y4M_UNKNOWN) || (fi->temporal == Y4M_UNKNOWN))
720       return Y4M_ERR_HEADER;
721     /* and S is required if stream is also 4:2:0 */
722     if ( ((si->chroma == Y4M_CHROMA_420JPEG) ||
723           (si->chroma == Y4M_CHROMA_420MPEG2) ||
724           (si->chroma == Y4M_CHROMA_420PALDV)) &&
725          (fi->spatial == Y4M_UNKNOWN) )
726       return Y4M_ERR_HEADER;
727     break;
728   case Y4M_ILACE_NONE:
729     /* stream "Ip" --> equivalent to frame "I1pp" */
730     fi->spatial = Y4M_SAMPLING_PROGRESSIVE;
731     fi->temporal = Y4M_SAMPLING_PROGRESSIVE;
732     fi->presentation = Y4M_PRESENT_PROG_SINGLE;
733     break;
734   case Y4M_ILACE_TOP_FIRST:
735     /* stream "It" --> equivalent to frame "Itii" */
736     fi->spatial = Y4M_SAMPLING_INTERLACED;
737     fi->temporal = Y4M_SAMPLING_INTERLACED;
738     fi->presentation = Y4M_PRESENT_TOP_FIRST;
739     break;
740   case Y4M_ILACE_BOTTOM_FIRST:
741     /* stream "Ib" --> equivalent to frame "Ibii" */
742     fi->spatial = Y4M_SAMPLING_INTERLACED;
743     fi->temporal = Y4M_SAMPLING_INTERLACED;
744     fi->presentation = Y4M_PRESENT_BOTTOM_FIRST;
745     break;
746   default:
747     /* stream unknown:  then, whatever */
748     break;
749   }
750   /* ta da!  done. */
751   return Y4M_OK;
752 }
753 
754 
755 
756 
757 
758 /*************************************************************************
759  *
760  * Read/Write stream header
761  *
762  *************************************************************************/
763 
764 
y4m_read_stream_header(int fd,y4m_stream_info_t * i)765 int y4m_read_stream_header(int fd, y4m_stream_info_t *i)
766 {
767    char line[Y4M_LINE_MAX];
768    char *p;
769    int n;
770    int err;
771 
772   /* start with a clean slate */
773   y4m_clear_stream_info(i);
774    /* read the header line */
775    for (n = 0, p = line; n < Y4M_LINE_MAX; n++, p++) {
776      if (read(fd, p, 1) < 1)
777        return Y4M_ERR_SYSTEM;
778      if (*p == '\n') {
779        *p = '\0';           /* Replace linefeed by end of string */
780        break;
781      }
782    }
783    if (n >= Y4M_LINE_MAX)
784       return Y4M_ERR_HEADER;
785    /* look for keyword in header */
786    if (strncmp(line, Y4M_MAGIC, strlen(Y4M_MAGIC)))
787     return Y4M_ERR_MAGIC;
788    if ((err = y4m_parse_stream_tags(line + strlen(Y4M_MAGIC), i)) != Y4M_OK)
789      return err;
790 
791    return Y4M_OK;
792 }
793 
794 
795 
y4m_write_stream_header(int fd,const y4m_stream_info_t * i)796 int y4m_write_stream_header(int fd, const y4m_stream_info_t *i)
797 {
798   char s[Y4M_LINE_MAX+1];
799   int n;
800   int err;
801   y4m_ratio_t rate = i->framerate;
802   y4m_ratio_t aspect = i->sampleaspect;
803   const char *chroma_keyword = y4m_chroma_keyword(i->chroma);
804 
805   if ((i->chroma == Y4M_UNKNOWN) || (chroma_keyword == NULL))
806     return Y4M_ERR_HEADER;
807   if (_y4mparam_feature_level < 1) {
808     if ((i->chroma != Y4M_CHROMA_420JPEG) &&
809 	(i->chroma != Y4M_CHROMA_420MPEG2) &&
810 	(i->chroma != Y4M_CHROMA_420PALDV))
811       return Y4M_ERR_FEATURE;
812     if (i->interlace == Y4M_ILACE_MIXED)
813       return Y4M_ERR_FEATURE;
814   }
815   y4m_ratio_reduce(&rate);
816   y4m_ratio_reduce(&aspect);
817   n = snprintf(s, sizeof(s), "%s W%d H%d F%d:%d I%s A%d:%d C%s",
818 	       Y4M_MAGIC,
819 	       i->width,
820 	       i->height,
821 	       rate.n, rate.d,
822 	       (i->interlace == Y4M_ILACE_NONE) ? "p" :
823 	       (i->interlace == Y4M_ILACE_TOP_FIRST) ? "t" :
824 	       (i->interlace == Y4M_ILACE_BOTTOM_FIRST) ? "b" :
825 	       (i->interlace == Y4M_ILACE_MIXED) ? "m" : "?",
826 	       aspect.n, aspect.d,
827 	       chroma_keyword
828 	       );
829   if ((n < 0) || (n > Y4M_LINE_MAX)) return Y4M_ERR_HEADER;
830   if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, &(i->x_tags)))
831       != Y4M_OK)
832     return err;
833   /* non-zero on error */
834   return (y4m_write(fd, s, strlen(s)) ? Y4M_ERR_SYSTEM : Y4M_OK);
835 }
836 
837 
838 
839 
840 
841 /*************************************************************************
842  *
843  * Read/Write frame header
844  *
845  *************************************************************************/
846 
y4m_read_frame_header(int fd,const y4m_stream_info_t * si,y4m_frame_info_t * fi)847 int y4m_read_frame_header(int fd,
848 			  const y4m_stream_info_t *si,
849 			  y4m_frame_info_t *fi)
850 {
851   char line[Y4M_LINE_MAX];
852   char *p;
853   int n;
854   ssize_t remain;
855 
856   /* start with a clean slate */
857   y4m_clear_frame_info(fi);
858   /* This is more clever than read_stream_header...
859      Try to read "FRAME\n" all at once, and don't try to parse
860      if nothing else is there...
861   */
862   remain = y4m_read(fd, line, sizeof(Y4M_FRAME_MAGIC)-1+1); /* -'\0', +'\n' */
863   if (remain < 0) return Y4M_ERR_SYSTEM;
864   if (remain > 0) {
865     /* A clean EOF should end exactly at a frame-boundary */
866     if (remain == sizeof(Y4M_FRAME_MAGIC))
867       return Y4M_ERR_EOF;
868     else
869       return Y4M_ERR_BADEOF;
870   }
871   if (strncmp(line, Y4M_FRAME_MAGIC, sizeof(Y4M_FRAME_MAGIC)-1))
872     return Y4M_ERR_MAGIC;
873   if (line[sizeof(Y4M_FRAME_MAGIC)-1] == '\n')
874     return Y4M_OK; /* done -- no tags:  that was the end-of-line. */
875 
876   if (line[sizeof(Y4M_FRAME_MAGIC)-1] != Y4M_DELIM[0]) {
877     return Y4M_ERR_MAGIC; /* wasn't a space -- what was it? */
878   }
879 
880   /* proceed to get the tags... (overwrite the magic) */
881   for (n = 0, p = line; n < Y4M_LINE_MAX; n++, p++) {
882     if (y4m_read(fd, p, 1))
883       return Y4M_ERR_SYSTEM;
884     if (*p == '\n') {
885       *p = '\0';           /* Replace linefeed by end of string */
886       break;
887     }
888   }
889   if (n >= Y4M_LINE_MAX) return Y4M_ERR_HEADER;
890   /* non-zero on error */
891   return y4m_parse_frame_tags(line, si, fi);
892 }
893 
894 
y4m_write_frame_header(int fd,const y4m_stream_info_t * si,const y4m_frame_info_t * fi)895 int y4m_write_frame_header(int fd,
896 			   const y4m_stream_info_t *si,
897 			   const y4m_frame_info_t *fi)
898 {
899   char s[Y4M_LINE_MAX+1];
900   int n, err;
901 
902   if (si->interlace == Y4M_ILACE_MIXED) {
903     if (_y4mparam_feature_level < 1) return Y4M_ERR_FEATURE;
904     n = snprintf(s, sizeof(s), "%s I%c%c%c", Y4M_FRAME_MAGIC,
905 		 (fi->presentation == Y4M_PRESENT_TOP_FIRST)        ? 't' :
906 		 (fi->presentation == Y4M_PRESENT_TOP_FIRST_RPT)    ? 'T' :
907 		 (fi->presentation == Y4M_PRESENT_BOTTOM_FIRST)     ? 'b' :
908 		 (fi->presentation == Y4M_PRESENT_BOTTOM_FIRST_RPT) ? 'B' :
909 		 (fi->presentation == Y4M_PRESENT_PROG_SINGLE) ? '1' :
910 		 (fi->presentation == Y4M_PRESENT_PROG_DOUBLE) ? '2' :
911 		 (fi->presentation == Y4M_PRESENT_PROG_TRIPLE) ? '3' :
912 		 '?',
913 		 (fi->temporal == Y4M_SAMPLING_PROGRESSIVE) ? 'p' :
914 		 (fi->temporal == Y4M_SAMPLING_INTERLACED)  ? 'i' :
915 		 '?',
916 		 (fi->spatial == Y4M_SAMPLING_PROGRESSIVE) ? 'p' :
917 		 (fi->spatial == Y4M_SAMPLING_INTERLACED)  ? 'i' :
918 		 '?'
919 		 );
920   } else {
921     n = snprintf(s, sizeof(s), "%s", Y4M_FRAME_MAGIC);
922   }
923 
924   if ((n < 0) || (n > Y4M_LINE_MAX)) return Y4M_ERR_HEADER;
925   if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, &(fi->x_tags)))
926       != Y4M_OK)
927     return err;
928   /* non-zero on error */
929   return (y4m_write(fd, s, strlen(s)) ? Y4M_ERR_SYSTEM : Y4M_OK);
930 }
931 
932 
933 
934 /*************************************************************************
935  *
936  * Read/Write entire frame
937  *
938  *************************************************************************/
939 
y4m_read_frame_data(int fd,const y4m_stream_info_t * si,y4m_frame_info_t * fi,uint8_t * const * frame)940 int y4m_read_frame_data(int fd, const y4m_stream_info_t *si,
941                         y4m_frame_info_t *fi, uint8_t * const *frame)
942 {
943   int planes = y4m_si_get_plane_count(si);
944   int p;
945 
946   /* Read each plane */
947   for (p = 0; p < planes; p++) {
948     int w = y4m_si_get_plane_width(si, p);
949     int h = y4m_si_get_plane_height(si, p);
950     if (y4m_read(fd, frame[p], w*h)) return Y4M_ERR_SYSTEM;
951   }
952   return Y4M_OK;
953 }
954 
955 
956 
y4m_read_frame(int fd,const y4m_stream_info_t * si,y4m_frame_info_t * fi,uint8_t * const * frame)957 int y4m_read_frame(int fd, const y4m_stream_info_t *si,
958 		   y4m_frame_info_t *fi, uint8_t * const *frame)
959 {
960   int err;
961 
962   /* Read frame header */
963   if ((err = y4m_read_frame_header(fd, si, fi)) != Y4M_OK) return err;
964   /* Read date */
965   return y4m_read_frame_data(fd, si, fi, frame);
966 }
967 
968 
969 
970 
y4m_write_frame(int fd,const y4m_stream_info_t * si,const y4m_frame_info_t * fi,uint8_t * const * frame)971 int y4m_write_frame(int fd, const y4m_stream_info_t *si,
972 		    const y4m_frame_info_t *fi, uint8_t * const *frame)
973 {
974   int planes = y4m_si_get_plane_count(si);
975   int err, p;
976 
977   /* Write frame header */
978   if ((err = y4m_write_frame_header(fd, si, fi)) != Y4M_OK) return err;
979   /* Write each plane */
980   for (p = 0; p < planes; p++) {
981     int w = y4m_si_get_plane_width(si, p);
982     int h = y4m_si_get_plane_height(si, p);
983     if (y4m_write(fd, frame[p], w*h)) return Y4M_ERR_SYSTEM;
984   }
985   return Y4M_OK;
986 }
987 
988 
989 
990 /*************************************************************************
991  *
992  * Read/Write entire frame, (de)interleaved (to)from two separate fields
993  *
994  *************************************************************************/
995 
996 
y4m_read_fields_data(int fd,const y4m_stream_info_t * si,y4m_frame_info_t * fi,uint8_t * const * upper_field,uint8_t * const * lower_field)997 int y4m_read_fields_data(int fd, const y4m_stream_info_t *si,
998                          y4m_frame_info_t *fi,
999                          uint8_t * const *upper_field,
1000                          uint8_t * const *lower_field)
1001 {
1002   int p;
1003   int planes = y4m_si_get_plane_count(si);
1004   const int maxrbuf=32*1024;
1005   uint8_t *rbuf=_y4m_alloc(maxrbuf);
1006   int rbufpos=0,rbuflen=0;
1007 
1008   /* Read each plane */
1009   for (p = 0; p < planes; p++) {
1010     uint8_t *dsttop = upper_field[p];
1011     uint8_t *dstbot = lower_field[p];
1012     int height = y4m_si_get_plane_height(si, p);
1013     int width = y4m_si_get_plane_width(si, p);
1014     int y;
1015     /* alternately read one line into each field */
1016     for (y = 0; y < height; y += 2) {
1017       if( width*2 >= maxrbuf ) {
1018         if (y4m_read(fd, dsttop, width)) goto y4merr;
1019         if (y4m_read(fd, dstbot, width)) goto y4merr;
1020       } else {
1021         if( rbufpos==rbuflen ) {
1022           rbuflen=(height-y)*width;
1023           if( rbuflen>maxrbuf )
1024             rbuflen=maxrbuf-maxrbuf%(2*width);
1025           if( y4m_read(fd,rbuf,rbuflen) )
1026             goto y4merr;
1027           rbufpos=0;
1028         }
1029 
1030         memcpy(dsttop,rbuf+rbufpos,width); rbufpos+=width;
1031         memcpy(dstbot,rbuf+rbufpos,width); rbufpos+=width;
1032       }
1033       dsttop+=width;
1034       dstbot+=width;
1035     }
1036   }
1037   _y4m_free(rbuf);
1038   return Y4M_OK;
1039 
1040  y4merr:
1041   _y4m_free(rbuf);
1042   return Y4M_ERR_SYSTEM;
1043 }
1044 
1045 
y4m_read_fields(int fd,const y4m_stream_info_t * si,y4m_frame_info_t * fi,uint8_t * const * upper_field,uint8_t * const * lower_field)1046 int y4m_read_fields(int fd, const y4m_stream_info_t *si, y4m_frame_info_t *fi,
1047                     uint8_t * const *upper_field,
1048                     uint8_t * const *lower_field)
1049 {
1050   int err;
1051   /* Read frame header */
1052   if ((err = y4m_read_frame_header(fd, si, fi)) != Y4M_OK) return err;
1053   /* Read data */
1054   return y4m_read_fields_data(fd, si, fi, upper_field, lower_field);
1055 }
1056 
1057 
1058 
y4m_write_fields(int fd,const y4m_stream_info_t * si,const y4m_frame_info_t * fi,uint8_t * const * upper_field,uint8_t * const * lower_field)1059 int y4m_write_fields(int fd, const y4m_stream_info_t *si,
1060 		     const y4m_frame_info_t *fi,
1061 		     uint8_t * const *upper_field,
1062 		     uint8_t * const *lower_field)
1063 {
1064   int p, err;
1065   int planes = y4m_si_get_plane_count(si);
1066   int numwbuf=0;
1067   const int maxwbuf=32*1024;
1068   uint8_t *wbuf;
1069 
1070   /* Write frame header */
1071   if ((err = y4m_write_frame_header(fd, si, fi)) != Y4M_OK) return err;
1072   /* Write each plane */
1073   wbuf=_y4m_alloc(maxwbuf);
1074   for (p = 0; p < planes; p++) {
1075     uint8_t *srctop = upper_field[p];
1076     uint8_t *srcbot = lower_field[p];
1077     int height = y4m_si_get_plane_height(si, p);
1078     int width = y4m_si_get_plane_width(si, p);
1079     int y;
1080     /* alternately write one line from each field */
1081     for (y = 0; y < height; y += 2) {
1082       if( width*2 >= maxwbuf ) {
1083         if (y4m_write(fd, srctop, width)) goto y4merr;
1084         if (y4m_write(fd, srcbot, width)) goto y4merr;
1085       } else {
1086         if (numwbuf + 2 * width > maxwbuf) {
1087           if(y4m_write(fd, wbuf, numwbuf)) goto y4merr;
1088           numwbuf=0;
1089         }
1090 
1091         memcpy(wbuf+numwbuf,srctop,width); numwbuf += width;
1092         memcpy(wbuf+numwbuf,srcbot,width); numwbuf += width;
1093       }
1094       srctop  += width;
1095       srcbot  += width;
1096     }
1097   }
1098   if( numwbuf )
1099     if( y4m_write(fd, wbuf, numwbuf) )
1100       goto y4merr;
1101   _y4m_free(wbuf);
1102   return Y4M_OK;
1103 
1104  y4merr:
1105   _y4m_free(wbuf);
1106   return Y4M_ERR_SYSTEM;
1107 }
1108 
1109 
1110 /*************************************************************************
1111  *
1112  * Handy logging of stream info
1113  *
1114  *************************************************************************/
1115 
y4m_log_stream_info(log_level_t level,const char * prefix,const y4m_stream_info_t * i)1116 void y4m_log_stream_info(log_level_t level, const char *prefix,
1117 			 const y4m_stream_info_t *i)
1118 {
1119   char s[256];
1120 
1121   snprintf(s, sizeof(s), "  frame size:  ");
1122   if (i->width == Y4M_UNKNOWN)
1123     snprintf(s+strlen(s), sizeof(s)-strlen(s), "(?)x");
1124   else
1125     snprintf(s+strlen(s), sizeof(s)-strlen(s), "%dx", i->width);
1126   if (i->height == Y4M_UNKNOWN)
1127     snprintf(s+strlen(s), sizeof(s)-strlen(s), "(?) pixels ");
1128   else
1129     snprintf(s+strlen(s), sizeof(s)-strlen(s), "%d pixels ", i->height);
1130   {
1131     int framelength = y4m_si_get_framelength(i);
1132     if (framelength == Y4M_UNKNOWN)
1133       snprintf(s+strlen(s), sizeof(s)-strlen(s), "(? bytes)");
1134     else
1135       snprintf(s+strlen(s), sizeof(s)-strlen(s), "(%d bytes)", framelength);
1136     mjpeg_log(level, "%s%s", prefix, s);
1137   }
1138   {
1139     const char *desc = y4m_chroma_description(i->chroma);
1140     if (desc == NULL) desc = "unknown!";
1141     mjpeg_log(level, "%s      chroma:  %s", prefix, desc);
1142   }
1143   if ((i->framerate.n == 0) && (i->framerate.d == 0))
1144     mjpeg_log(level, "%s  frame rate:  ??? fps", prefix);
1145   else
1146     mjpeg_log(level, "%s  frame rate:  %d/%d fps (~%f)", prefix,
1147 	      i->framerate.n, i->framerate.d,
1148 	      (double) i->framerate.n / (double) i->framerate.d);
1149   mjpeg_log(level, "%s   interlace:  %s", prefix,
1150 	  (i->interlace == Y4M_ILACE_NONE) ? "none/progressive" :
1151 	  (i->interlace == Y4M_ILACE_TOP_FIRST) ? "top-field-first" :
1152 	  (i->interlace == Y4M_ILACE_BOTTOM_FIRST) ? "bottom-field-first" :
1153 	  (i->interlace == Y4M_ILACE_MIXED) ? "mixed-mode" :
1154 	  "anyone's guess");
1155   if ((i->sampleaspect.n == 0) && (i->sampleaspect.d == 0))
1156     mjpeg_log(level, "%ssample aspect ratio:  ?:?", prefix);
1157   else
1158     mjpeg_log(level, "%ssample aspect ratio:  %d:%d", prefix,
1159 	      i->sampleaspect.n, i->sampleaspect.d);
1160 }
1161 
1162 
1163 /*************************************************************************
1164  *
1165  * Convert error code to string
1166  *
1167  *************************************************************************/
1168 
y4m_strerr(int err)1169 const char *y4m_strerr(int err)
1170 {
1171   switch (err) {
1172   case Y4M_OK:          return "no error";
1173   case Y4M_ERR_RANGE:   return "parameter out of range";
1174   case Y4M_ERR_SYSTEM:  return "system error (failed read/write)";
1175   case Y4M_ERR_HEADER:  return "bad stream or frame header";
1176   case Y4M_ERR_BADTAG:  return "unknown header tag";
1177   case Y4M_ERR_MAGIC:   return "bad header magic";
1178   case Y4M_ERR_XXTAGS:  return "too many xtags";
1179   case Y4M_ERR_EOF:     return "end-of-file";
1180   case Y4M_ERR_BADEOF:  return "stream ended unexpectedly (EOF)";
1181   case Y4M_ERR_FEATURE: return "stream requires unsupported features";
1182   default:
1183     return "unknown error code";
1184   }
1185 }
1186 
1187 
1188 /*************************************************************************
1189  *
1190  * Chroma subsampling stuff
1191  *
1192  *************************************************************************/
1193 
y4m_chroma_ss_x_ratio(int chroma_mode)1194 y4m_ratio_t y4m_chroma_ss_x_ratio(int chroma_mode)
1195 {
1196   y4m_ratio_t r;
1197   switch (chroma_mode) {
1198   case Y4M_CHROMA_444ALPHA:
1199   case Y4M_CHROMA_444:
1200   case Y4M_CHROMA_MONO:
1201     r.n = 1; r.d = 1; break;
1202   case Y4M_CHROMA_420JPEG:
1203   case Y4M_CHROMA_420MPEG2:
1204   case Y4M_CHROMA_420PALDV:
1205   case Y4M_CHROMA_422:
1206     r.n = 1; r.d = 2; break;
1207   case Y4M_CHROMA_411:
1208     r.n = 1; r.d = 4; break;
1209   default:
1210     r.n = 0; r.d = 0;
1211   }
1212   return r;
1213 }
1214 
y4m_chroma_ss_y_ratio(int chroma_mode)1215 y4m_ratio_t y4m_chroma_ss_y_ratio(int chroma_mode)
1216 {
1217   y4m_ratio_t r;
1218   switch (chroma_mode) {
1219   case Y4M_CHROMA_444ALPHA:
1220   case Y4M_CHROMA_444:
1221   case Y4M_CHROMA_MONO:
1222   case Y4M_CHROMA_422:
1223   case Y4M_CHROMA_411:
1224     r.n = 1; r.d = 1; break;
1225   case Y4M_CHROMA_420JPEG:
1226   case Y4M_CHROMA_420MPEG2:
1227   case Y4M_CHROMA_420PALDV:
1228     r.n = 1; r.d = 2; break;
1229   default:
1230     r.n = 0; r.d = 0;
1231   }
1232   return r;
1233 }
1234 
1235 
1236 #if 0  /* unfinished work here */
1237 y4m_ratio_t y4m_chroma_ss_x_offset(int chroma_mode, int field, int plane)
1238 {
1239   y4m_ratio_t r;
1240   switch (chroma_mode) {
1241   case Y4M_CHROMA_444ALPHA:
1242   case Y4M_CHROMA_444:
1243   case Y4M_CHROMA_MONO:
1244   case Y4M_CHROMA_422:
1245   case Y4M_CHROMA_411:
1246     r.n = 0; r.d = 1; break;
1247   case Y4M_CHROMA_420JPEG:
1248   case Y4M_CHROMA_420MPEG2:
1249   case Y4M_CHROMA_420PALDV:
1250     r.n = 1; r.d = 2; break;
1251   default:
1252     r.n = 0; r.d = 0;
1253   }
1254   return r;
1255 }
1256 
1257 y4m_ratio_t y4m_chroma_ss_y_offset(int chroma_mode, int field, int plane);
1258 {
1259   y4m_ratio_t r;
1260   switch (chroma_mode) {
1261   case Y4M_CHROMA_444ALPHA:
1262   case Y4M_CHROMA_444:
1263   case Y4M_CHROMA_MONO:
1264   case Y4M_CHROMA_422:
1265   case Y4M_CHROMA_411:
1266     r.n = 0; r.d = 1; break;
1267   case Y4M_CHROMA_420JPEG:
1268   case Y4M_CHROMA_420MPEG2:
1269   case Y4M_CHROMA_420PALDV:
1270     r.n = 1; r.d = 2; break;
1271   default:
1272     r.n = 0; r.d = 0;
1273   }
1274   return r;
1275 }
1276 #endif
1277 
1278 
1279 
y4m_chroma_parse_keyword(const char * s)1280 int y4m_chroma_parse_keyword(const char *s)
1281 {
1282   if (!strcasecmp("420jpeg", s))
1283     return Y4M_CHROMA_420JPEG;
1284   else if (!strcasecmp("420mpeg2", s))
1285     return Y4M_CHROMA_420MPEG2;
1286   else if (!strcasecmp("420paldv", s))
1287     return Y4M_CHROMA_420PALDV;
1288   else if (!strcasecmp("444", s))
1289     return Y4M_CHROMA_444;
1290   else if (!strcasecmp("422", s))
1291     return Y4M_CHROMA_422;
1292   else if (!strcasecmp("411", s))
1293     return Y4M_CHROMA_411;
1294   else if (!strcasecmp("mono", s))
1295     return Y4M_CHROMA_MONO;
1296   else if (!strcasecmp("444alpha", s))
1297     return Y4M_CHROMA_444ALPHA;
1298   else
1299     return Y4M_UNKNOWN;
1300 }
1301 
1302 
y4m_chroma_keyword(int chroma_mode)1303 const char *y4m_chroma_keyword(int chroma_mode)
1304 {
1305   switch (chroma_mode) {
1306   case Y4M_CHROMA_420JPEG:  return "420jpeg";
1307   case Y4M_CHROMA_420MPEG2: return "420mpeg2";
1308   case Y4M_CHROMA_420PALDV: return "420paldv";
1309   case Y4M_CHROMA_444:      return "444";
1310   case Y4M_CHROMA_422:      return "422";
1311   case Y4M_CHROMA_411:      return "411";
1312   case Y4M_CHROMA_MONO:     return "mono";
1313   case Y4M_CHROMA_444ALPHA: return "444alpha";
1314   default:
1315     return NULL;
1316   }
1317 }
1318 
1319 
y4m_chroma_description(int chroma_mode)1320 const char *y4m_chroma_description(int chroma_mode)
1321 {
1322   switch (chroma_mode) {
1323   case Y4M_CHROMA_420JPEG:  return "4:2:0 JPEG/MPEG-1 (interstitial)";
1324   case Y4M_CHROMA_420MPEG2: return "4:2:0 MPEG-2 (horiz. cositing)";
1325   case Y4M_CHROMA_420PALDV: return "4:2:0 PAL-DV (altern. siting)";
1326   case Y4M_CHROMA_444:      return "4:4:4 (no subsampling)";
1327   case Y4M_CHROMA_422:      return "4:2:2 (horiz. cositing)";
1328   case Y4M_CHROMA_411:      return "4:1:1 (horiz. cositing)";
1329   case Y4M_CHROMA_MONO:     return "luma plane only";
1330   case Y4M_CHROMA_444ALPHA: return "4:4:4 with alpha channel";
1331   default:
1332     return NULL;
1333   }
1334 }
1335