10l: comparison of char* ptrs with string literals
[mplayer.git] / libmpdemux / yuv4mpeg.c
blob390abc654a04bc81b284596659368ca512742897
1 /*
2 * yuv4mpeg.c: Functions for reading and writing "new" YUV4MPEG streams
4 * Copyright (C) 2001 Matthew J. Marjanovic <maddog@mir.com>
6 * This file is ripped from the lavtools package (mjpeg.sourceforge.net)
7 * Ported to mplayer by Rik Snel <rsnel@cube.dyndns.org>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
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.
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 02110-1301 USA
25 #include "config.h"
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include "yuv4mpeg.h"
32 #include "yuv4mpeg_intern.h"
33 #include "mp_msg.h"
35 static int _y4mparam_allow_unknown_tags = 1; /* default is forgiveness */
37 static void *(*_y4m_alloc)(size_t bytes) = malloc;
38 static void (*_y4m_free)(void *ptr) = free;
40 int y4m_allow_unknown_tags(int yn) {
41 int old = _y4mparam_allow_unknown_tags;
42 if (yn >= 0) _y4mparam_allow_unknown_tags = (yn) ? 1 : 0;
43 return old;
48 /*************************************************************************
50 * Convenience functions for fd read/write
52 * - guaranteed to transfer entire payload (or fail)
53 * - returns:
54 * 0 on complete success
55 * +(# of remaining bytes) on eof (for y4m_read)
56 * -(# of rem. bytes) on error (and ERRNO should be set)
58 *************************************************************************/
61 ssize_t y4m_read(stream_t *s, char *buf, size_t len)
63 ssize_t n;
65 while (len > 0) {
66 n = stream_read(s, buf, len);
67 if (n <= 0) {
68 /* return amount left to read */
69 if (n == 0)
70 return len; /* n == 0 --> eof */
71 else
72 return -len; /* n < 0 --> error */
74 buf += n;
75 len -= n;
77 return 0;
81 #if 0 /* not needed */
82 ssize_t y4m_write(int fd, char *buf, size_t len)
84 ssize_t n;
86 while (len > 0) {
87 n = write(fd, buf, len);
88 if (n < 0) return -len; /* return amount left to write */
89 buf += n;
90 len -= n;
92 return 0;
94 #endif
97 /*************************************************************************
99 * "Extra tags" handling
101 *************************************************************************/
104 static char *y4m_new_xtag(void)
106 return _y4m_alloc(Y4M_MAX_XTAG_SIZE);
110 void y4m_init_xtag_list(y4m_xtag_list_t *xtags)
112 int i;
113 xtags->count = 0;
114 for (i = 0; i < Y4M_MAX_XTAGS; i++) {
115 xtags->tags[i] = NULL;
120 void y4m_fini_xtag_list(y4m_xtag_list_t *xtags)
122 int i;
123 for (i = 0; i < Y4M_MAX_XTAGS; i++) {
124 if (xtags->tags[i] != NULL) {
125 _y4m_free(xtags->tags[i]);
126 xtags->tags[i] = NULL;
129 xtags->count = 0;
133 void y4m_copy_xtag_list(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src)
135 int i;
136 for (i = 0; i < src->count; i++) {
137 if (dest->tags[i] == NULL)
138 dest->tags[i] = y4m_new_xtag();
139 strncpy(dest->tags[i], src->tags[i], Y4M_MAX_XTAG_SIZE);
141 dest->count = src->count;
146 static int y4m_snprint_xtags(char *s, int maxn, y4m_xtag_list_t *xtags)
148 int i, room;
150 for (i = 0, room = maxn - 1; i < xtags->count; i++) {
151 int n = snprintf(s, room + 1, " %s", xtags->tags[i]);
152 if ((n < 0) || (n > room)) return Y4M_ERR_HEADER;
153 s += n;
154 room -= n;
156 s[0] = '\n'; /* finish off header with newline */
157 s[1] = '\0'; /* ...and end-of-string */
158 return Y4M_OK;
162 int y4m_xtag_count(const y4m_xtag_list_t *xtags)
164 return xtags->count;
168 const char *y4m_xtag_get(const y4m_xtag_list_t *xtags, int n)
170 if (n >= xtags->count)
171 return NULL;
172 else
173 return xtags->tags[n];
177 int y4m_xtag_add(y4m_xtag_list_t *xtags, const char *tag)
179 if (xtags->count >= Y4M_MAX_XTAGS) return Y4M_ERR_XXTAGS;
180 if (xtags->tags[xtags->count] == NULL) {
181 xtags->tags[xtags->count] = y4m_new_xtag();
183 strncpy(xtags->tags[xtags->count], tag, Y4M_MAX_XTAG_SIZE);
184 (xtags->count)++;
185 return Y4M_OK;
189 int y4m_xtag_remove(y4m_xtag_list_t *xtags, int n)
191 int i;
192 char *q;
194 if ((n < 0) || (n >= xtags->count)) return Y4M_ERR_RANGE;
195 q = xtags->tags[n];
196 for (i = n; i < (xtags->count - 1); i++)
197 xtags->tags[i] = xtags->tags[i+1];
198 xtags->tags[i] = q;
199 (xtags->count)--;
200 return Y4M_OK;
204 int y4m_xtag_clearlist(y4m_xtag_list_t *xtags)
206 xtags->count = 0;
207 return Y4M_OK;
211 int y4m_xtag_addlist(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src)
213 int i, j;
215 if ((dest->count + src->count) > Y4M_MAX_XTAGS) return Y4M_ERR_XXTAGS;
216 for (i = dest->count, j = 0;
217 j < src->count;
218 i++, j++) {
219 if (dest->tags[i] == NULL)
220 dest->tags[i] = y4m_new_xtag();
221 strncpy(dest->tags[i], src->tags[i], Y4M_MAX_XTAG_SIZE);
223 dest->count += src->count;
224 return Y4M_OK;
228 /*************************************************************************
230 * Creators/destructors for y4m_*_info_t structures
232 *************************************************************************/
235 void y4m_init_stream_info(y4m_stream_info_t *info)
237 if (info == NULL) return;
238 /* initialize info */
239 info->width = Y4M_UNKNOWN;
240 info->height = Y4M_UNKNOWN;
241 info->interlace = Y4M_UNKNOWN;
242 info->framerate = y4m_fps_UNKNOWN;
243 info->sampleaspect = y4m_sar_UNKNOWN;
244 y4m_init_xtag_list(&(info->x_tags));
248 void y4m_copy_stream_info(y4m_stream_info_t *dest, y4m_stream_info_t *src)
250 if ((dest == NULL) || (src == NULL)) return;
251 /* copy info */
252 dest->width = src->width;
253 dest->height = src->height;
254 dest->interlace = src->interlace;
255 dest->framerate = src->framerate;
256 dest->sampleaspect = src->sampleaspect;
257 y4m_copy_xtag_list(&(dest->x_tags), &(src->x_tags));
261 void y4m_fini_stream_info(y4m_stream_info_t *info)
263 if (info == NULL) return;
264 y4m_fini_xtag_list(&(info->x_tags));
268 void y4m_si_set_width(y4m_stream_info_t *si, int width)
270 si->width = width;
271 si->framelength = (si->height * si->width) * 3 / 2;
274 int y4m_si_get_width(y4m_stream_info_t *si)
275 { return si->width; }
277 void y4m_si_set_height(y4m_stream_info_t *si, int height)
279 si->height = height;
280 si->framelength = (si->height * si->width) * 3 / 2;
283 int y4m_si_get_height(y4m_stream_info_t *si)
284 { return si->height; }
286 void y4m_si_set_interlace(y4m_stream_info_t *si, int interlace)
287 { si->interlace = interlace; }
289 int y4m_si_get_interlace(y4m_stream_info_t *si)
290 { return si->interlace; }
292 void y4m_si_set_framerate(y4m_stream_info_t *si, y4m_ratio_t framerate)
293 { si->framerate = framerate; }
295 y4m_ratio_t y4m_si_get_framerate(y4m_stream_info_t *si)
296 { return si->framerate; }
298 void y4m_si_set_sampleaspect(y4m_stream_info_t *si, y4m_ratio_t sar)
299 { si->sampleaspect = sar; }
301 y4m_ratio_t y4m_si_get_sampleaspect(y4m_stream_info_t *si)
302 { return si->sampleaspect; }
304 int y4m_si_get_framelength(y4m_stream_info_t *si)
305 { return si->framelength; }
307 y4m_xtag_list_t *y4m_si_xtags(y4m_stream_info_t *si)
308 { return &(si->x_tags); }
312 void y4m_init_frame_info(y4m_frame_info_t *info)
314 if (info == NULL) return;
315 /* initialize info */
316 y4m_init_xtag_list(&(info->x_tags));
320 void y4m_copy_frame_info(y4m_frame_info_t *dest, y4m_frame_info_t *src)
322 if ((dest == NULL) || (src == NULL)) return;
323 /* copy info */
324 y4m_copy_xtag_list(&(dest->x_tags), &(src->x_tags));
328 void y4m_fini_frame_info(y4m_frame_info_t *info)
330 if (info == NULL) return;
331 y4m_fini_xtag_list(&(info->x_tags));
336 /*************************************************************************
338 * Tag parsing
340 *************************************************************************/
342 int y4m_parse_stream_tags(char *s, y4m_stream_info_t *i)
344 char *token, *value;
345 char tag;
346 int err;
348 /* parse fields */
349 for (token = strtok(s, Y4M_DELIM);
350 token != NULL;
351 token = strtok(NULL, Y4M_DELIM)) {
352 if (token[0] == '\0') continue; /* skip empty strings */
353 tag = token[0];
354 value = token + 1;
355 switch (tag) {
356 case 'W': /* width */
357 i->width = atoi(value);
358 if (i->width <= 0) return Y4M_ERR_RANGE;
359 break;
360 case 'H': /* height */
361 i->height = atoi(value);
362 if (i->height <= 0) return Y4M_ERR_RANGE;
363 break;
364 case 'F': /* frame rate (fps) */
365 if ((err = y4m_parse_ratio(&(i->framerate), value)) != Y4M_OK)
366 return err;
367 if (i->framerate.n < 0) return Y4M_ERR_RANGE;
368 break;
369 case 'I': /* interlacing */
370 switch (value[0]) {
371 case 'p': i->interlace = Y4M_ILACE_NONE; break;
372 case 't': i->interlace = Y4M_ILACE_TOP_FIRST; break;
373 case 'b': i->interlace = Y4M_ILACE_BOTTOM_FIRST; break;
374 case '?':
375 default:
376 i->interlace = Y4M_UNKNOWN; break;
378 break;
379 case 'A': /* sample (pixel) aspect ratio */
380 if ((err = y4m_parse_ratio(&(i->sampleaspect), value)) != Y4M_OK)
381 return err;
382 if (i->sampleaspect.n < 0) return Y4M_ERR_RANGE;
383 break;
384 case 'X': /* 'X' meta-tag */
385 if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err;
386 break;
387 default:
388 /* possible error on unknown options */
389 if (_y4mparam_allow_unknown_tags) {
390 /* unknown tags ok: store in xtag list and warn... */
391 if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err;
392 mp_msg(MSGT_DEMUX, MSGL_WARN, "Unknown stream tag encountered: '%s'\n", token);
393 } else {
394 /* unknown tags are *not* ok */
395 return Y4M_ERR_BADTAG;
397 break;
400 /* Error checking... width and height must be known since we can't
401 * parse without them
403 if( i->width == Y4M_UNKNOWN || i->height == Y4M_UNKNOWN )
404 return Y4M_ERR_HEADER;
405 /* ta da! done. */
406 return Y4M_OK;
411 static int y4m_parse_frame_tags(char *s, y4m_frame_info_t *i)
413 char *token, *value;
414 char tag;
415 int err;
417 /* parse fields */
418 for (token = strtok(s, Y4M_DELIM);
419 token != NULL;
420 token = strtok(NULL, Y4M_DELIM)) {
421 if (token[0] == '\0') continue; /* skip empty strings */
422 tag = token[0];
423 value = token + 1;
424 switch (tag) {
425 case 'X': /* 'X' meta-tag */
426 if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err;
427 break;
428 default:
429 /* possible error on unknown options */
430 if (_y4mparam_allow_unknown_tags) {
431 /* unknown tags ok: store in xtag list and warn... */
432 if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err;
433 mp_msg(MSGT_DEMUX, MSGL_WARN, "Unknown frame tag encountered: '%s'\n", token);
434 } else {
435 /* unknown tags are *not* ok */
436 return Y4M_ERR_BADTAG;
438 break;
441 /* ta da! done. */
442 return Y4M_OK;
449 /*************************************************************************
451 * Read/Write stream header
453 *************************************************************************/
456 int y4m_read_stream_header(stream_t *s, y4m_stream_info_t *i)
458 char line[Y4M_LINE_MAX];
459 char *p;
460 int n;
461 int err;
463 /* read the header line */
464 for (n = 0, p = line; n < Y4M_LINE_MAX; n++, p++) {
465 if (y4m_read(s, p, 1))
466 return Y4M_ERR_SYSTEM;
467 if (*p == '\n') {
468 *p = '\0'; /* Replace linefeed by end of string */
469 break;
472 if (n >= Y4M_LINE_MAX)
473 return Y4M_ERR_HEADER;
474 /* look for keyword in header */
475 if (strncmp(line, Y4M_MAGIC, strlen(Y4M_MAGIC)))
476 return Y4M_ERR_MAGIC;
477 if ((err = y4m_parse_stream_tags(line + strlen(Y4M_MAGIC), i)) != Y4M_OK)
478 return err;
480 i->framelength = (i->height * i->width) * 3 / 2;
481 return Y4M_OK;
485 #if 0
486 int y4m_write_stream_header(int fd, y4m_stream_info_t *i)
488 char s[Y4M_LINE_MAX+1];
489 int n;
490 int err;
492 y4m_ratio_reduce(&(i->framerate));
493 y4m_ratio_reduce(&(i->sampleaspect));
494 n = snprintf(s, sizeof(s), "%s W%d H%d F%d:%d I%s A%d:%d",
495 Y4M_MAGIC,
496 i->width,
497 i->height,
498 i->framerate.n, i->framerate.d,
499 (i->interlace == Y4M_ILACE_NONE) ? "p" :
500 (i->interlace == Y4M_ILACE_TOP_FIRST) ? "t" :
501 (i->interlace == Y4M_ILACE_BOTTOM_FIRST) ? "b" : "?",
502 i->sampleaspect.n, i->sampleaspect.d);
503 if ((n < 0) || (n > Y4M_LINE_MAX)) return Y4M_ERR_HEADER;
504 if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, &(i->x_tags)))
505 != Y4M_OK)
506 return err;
507 /* non-zero on error */
508 return (y4m_write(fd, s, strlen(s)) ? Y4M_ERR_SYSTEM : Y4M_OK);
510 #endif
515 /*************************************************************************
517 * Read/Write frame header
519 *************************************************************************/
521 int y4m_read_frame_header(stream_t *s, y4m_frame_info_t *i)
523 char line[Y4M_LINE_MAX];
524 char *p;
525 int n;
526 ssize_t remain;
528 /* This is more clever than read_stream_header...
529 Try to read "FRAME\n" all at once, and don't try to parse
530 if nothing else is there...
532 remain = y4m_read(s, line, sizeof(Y4M_FRAME_MAGIC));
533 if (remain != 0)
535 /* A clean EOF should end exactly at a frame-boundary */
536 if( remain == sizeof(Y4M_FRAME_MAGIC) )
537 return Y4M_ERR_EOF;
538 else
539 return Y4M_ERR_SYSTEM;
541 if (strncmp(line, Y4M_FRAME_MAGIC, sizeof(Y4M_FRAME_MAGIC)-1))
542 return Y4M_ERR_MAGIC;
543 if (line[sizeof(Y4M_FRAME_MAGIC)-1] == '\n')
544 return Y4M_OK; /* done -- no tags: that was the end-of-line. */
546 if (line[sizeof(Y4M_FRAME_MAGIC)-1] != Y4M_DELIM[0]) {
547 return Y4M_ERR_MAGIC; /* wasn't a space -- what was it? */
550 /* proceed to get the tags... (overwrite the magic) */
551 for (n = 0, p = line; n < Y4M_LINE_MAX; n++, p++) {
552 if (y4m_read(s, p, 1))
553 return Y4M_ERR_SYSTEM;
554 if (*p == '\n') {
555 *p = '\0'; /* Replace linefeed by end of string */
556 break;
559 if (n >= Y4M_LINE_MAX) return Y4M_ERR_HEADER;
560 /* non-zero on error */
561 return y4m_parse_frame_tags(line, i);
565 #if 0
566 int y4m_write_frame_header(int fd, y4m_frame_info_t *i)
568 char s[Y4M_LINE_MAX+1];
569 int n;
570 int err;
572 n = snprintf(s, sizeof(s), "%s", Y4M_FRAME_MAGIC);
573 if ((n < 0) || (n > Y4M_LINE_MAX)) return Y4M_ERR_HEADER;
574 if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, &(i->x_tags)))
575 != Y4M_OK)
576 return err;
577 /* non-zero on error */
578 return (y4m_write(fd, s, strlen(s)) ? Y4M_ERR_SYSTEM : Y4M_OK);
580 #endif
584 /*************************************************************************
586 * Read/Write entire frame
588 *************************************************************************/
590 int y4m_read_frame(stream_t *s, y4m_stream_info_t *si,
591 y4m_frame_info_t *fi, unsigned char *yuv[3])
593 int err;
594 int w = si->width;
595 int h = si->height;
597 /* Read frame header */
598 if ((err = y4m_read_frame_header(s, fi)) != Y4M_OK) return err;
599 /* Read luminance scanlines */
600 if (y4m_read(s, yuv[0], w*h)) return Y4M_ERR_SYSTEM;
601 /* Read chrominance scanlines */
602 if (y4m_read(s, yuv[1], w*h/4)) return Y4M_ERR_SYSTEM;
603 if (y4m_read(s, yuv[2], w*h/4)) return Y4M_ERR_SYSTEM;
605 return Y4M_OK;
610 #if 0
611 int y4m_write_frame(int fd, y4m_stream_info_t *si,
612 y4m_frame_info_t *fi, unsigned char *yuv[3])
614 int err;
615 int w = si->width;
616 int h = si->height;
618 /* Write frame header */
619 if ((err = y4m_write_frame_header(fd, fi)) != Y4M_OK) return err;
620 /* Write luminance,chrominance scanlines */
621 if (y4m_write(fd, yuv[0], w*h) ||
622 y4m_write(fd, yuv[1], w*h/4) ||
623 y4m_write(fd, yuv[2], w*h/4))
624 return Y4M_ERR_SYSTEM;
625 return Y4M_OK;
627 #endif
630 /*************************************************************************
632 * Read/Write entire frame, (de)interleaved (to)from two separate fields
634 *************************************************************************/
636 #if 0
637 int y4m_read_fields(int fd, y4m_stream_info_t *si, y4m_frame_info_t *fi,
638 unsigned char *upper_field[3],
639 unsigned char *lower_field[3])
641 int i, y, err;
642 int width = si->width;
643 int height = si->height;
645 /* Read frame header */
646 if ((err = y4m_read_frame_header(fd, fi)) != Y4M_OK) return err;
647 /* Read Y', Cb, and Cr planes */
648 for (i = 0; i < 3; i++) {
649 unsigned char *srctop = upper_field[i];
650 unsigned char *srcbot = lower_field[i];
651 /* alternately write one line from each */
652 for (y = 0; y < height; y += 2) {
653 if (y4m_read(fd, srctop, width)) return Y4M_ERR_SYSTEM;
654 srctop += width;
655 if (y4m_read(fd, srcbot, width)) return Y4M_ERR_SYSTEM;
656 srcbot += width;
658 /* for chroma, width/height are half as big */
659 if (i == 0) {
660 width /= 2;
661 height /= 2;
664 return Y4M_OK;
669 int y4m_write_fields(int fd, y4m_stream_info_t *si, y4m_frame_info_t *fi,
670 unsigned char *upper_field[3],
671 unsigned char *lower_field[3])
673 int i, y, err;
674 int width = si->width;
675 int height = si->height;
677 /* Write frame header */
678 if ((err = y4m_write_frame_header(fd, fi)) != Y4M_OK) return err;
679 /* Write Y', Cb, and Cr planes */
680 for (i = 0; i < 3; i++) {
681 unsigned char *srctop = upper_field[i];
682 unsigned char *srcbot = lower_field[i];
683 /* alternately write one line from each */
684 for (y = 0; y < height; y += 2) {
685 if (y4m_write(fd, srctop, width)) return Y4M_ERR_SYSTEM;
686 srctop += width;
687 if (y4m_write(fd, srcbot, width)) return Y4M_ERR_SYSTEM;
688 srcbot += width;
690 /* for chroma, width/height are half as big */
691 if (i == 0) {
692 width /= 2;
693 height /= 2;
696 return Y4M_OK;
698 #endif
701 /*************************************************************************
703 * Handy logging of stream info
705 *************************************************************************/
707 void y4m_log_stream_info(const char *prefix, y4m_stream_info_t *i)
709 char s[256];
711 snprintf(s, sizeof(s), " frame size: ");
712 if (i->width == Y4M_UNKNOWN)
713 snprintf(s+strlen(s), sizeof(s)-strlen(s), "(?)x");
714 else
715 snprintf(s+strlen(s), sizeof(s)-strlen(s), "%dx", i->width);
716 if (i->height == Y4M_UNKNOWN)
717 snprintf(s+strlen(s), sizeof(s)-strlen(s), "(?) pixels ");
718 else
719 snprintf(s+strlen(s), sizeof(s)-strlen(s), "%d pixels ", i->height);
720 if (i->framelength == Y4M_UNKNOWN)
721 snprintf(s+strlen(s), sizeof(s)-strlen(s), "(? bytes)");
722 else
723 snprintf(s+strlen(s), sizeof(s)-strlen(s), "(%d bytes)", i->framelength);
724 mp_msg(MSGT_DEMUX, MSGL_V, "%s%s\n", prefix, s);
725 if ((i->framerate.n == 0) && (i->framerate.d == 0))
726 mp_msg(MSGT_DEMUX, MSGL_V, "%s frame rate: ??? fps\n", prefix);
727 else
728 mp_msg(MSGT_DEMUX, MSGL_V, "%s frame rate: %d/%d fps (~%f)\n", prefix,
729 i->framerate.n, i->framerate.d,
730 (double) i->framerate.n / (double) i->framerate.d);
731 mp_msg(MSGT_DEMUX, MSGL_V, "%s interlace: %s\n", prefix,
732 (i->interlace == Y4M_ILACE_NONE) ? "none/progressive" :
733 (i->interlace == Y4M_ILACE_TOP_FIRST) ? "top-field-first" :
734 (i->interlace == Y4M_ILACE_BOTTOM_FIRST) ? "bottom-field-first" :
735 "anyone's guess");
736 if ((i->sampleaspect.n == 0) && (i->sampleaspect.d == 0))
737 mp_msg(MSGT_DEMUX, MSGL_V, "%ssample aspect ratio: ?:?\n", prefix);
738 else
739 mp_msg(MSGT_DEMUX, MSGL_V, "%ssample aspect ratio: %d:%d\n", prefix,
740 i->sampleaspect.n, i->sampleaspect.d);
744 /*************************************************************************
746 * Convert error code to string
748 *************************************************************************/
750 const char *y4m_strerr(int err)
752 switch (err) {
753 case Y4M_OK: return "no error";
754 case Y4M_ERR_RANGE: return "parameter out of range";
755 case Y4M_ERR_SYSTEM: return "stream ended unexpectedly (failed read/write)";
756 case Y4M_ERR_HEADER: return "bad stream or frame header";
757 case Y4M_ERR_BADTAG: return "unknown header tag";
758 case Y4M_ERR_MAGIC: return "bad header magic";
759 case Y4M_ERR_XXTAGS: return "too many xtags";
760 case Y4M_ERR_EOF: return "end-of-file";
761 default:
762 return "unknown error code";