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