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
32 #include "yuv4mpeg_intern.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;
48 /*************************************************************************
50 * Convenience functions for fd read/write
52 * - guaranteed to transfer entire payload (or fail)
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
)
66 n
= stream_read(s
, buf
, len
);
68 /* return amount left to read */
70 return len
; /* n == 0 --> eof */
72 return -len
; /* n < 0 --> error */
81 #if 0 /* not needed */
82 ssize_t
y4m_write(int fd
, char *buf
, size_t len
)
87 n
= write(fd
, buf
, len
);
88 if (n
< 0) return -len
; /* return amount left to write */
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
)
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
)
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
;
133 void y4m_copy_xtag_list(y4m_xtag_list_t
*dest
, const y4m_xtag_list_t
*src
)
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
)
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
;
156 s
[0] = '\n'; /* finish off header with newline */
157 s
[1] = '\0'; /* ...and end-of-string */
162 int y4m_xtag_count(const y4m_xtag_list_t
*xtags
)
168 const char *y4m_xtag_get(const y4m_xtag_list_t
*xtags
, int n
)
170 if (n
>= xtags
->count
)
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
);
189 int y4m_xtag_remove(y4m_xtag_list_t
*xtags
, int n
)
194 if ((n
< 0) || (n
>= xtags
->count
)) return Y4M_ERR_RANGE
;
196 for (i
= n
; i
< (xtags
->count
- 1); i
++)
197 xtags
->tags
[i
] = xtags
->tags
[i
+1];
204 int y4m_xtag_clearlist(y4m_xtag_list_t
*xtags
)
211 int y4m_xtag_addlist(y4m_xtag_list_t
*dest
, const y4m_xtag_list_t
*src
)
215 if ((dest
->count
+ src
->count
) > Y4M_MAX_XTAGS
) return Y4M_ERR_XXTAGS
;
216 for (i
= dest
->count
, j
= 0;
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
;
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;
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
)
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
)
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;
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 /*************************************************************************
340 *************************************************************************/
342 int y4m_parse_stream_tags(char *s
, y4m_stream_info_t
*i
)
349 for (token
= strtok(s
, Y4M_DELIM
);
351 token
= strtok(NULL
, Y4M_DELIM
)) {
352 if (token
[0] == '\0') continue; /* skip empty strings */
356 case 'W': /* width */
357 i
->width
= atoi(value
);
358 if (i
->width
<= 0) return Y4M_ERR_RANGE
;
360 case 'H': /* height */
361 i
->height
= atoi(value
);
362 if (i
->height
<= 0) return Y4M_ERR_RANGE
;
364 case 'F': /* frame rate (fps) */
365 if ((err
= y4m_parse_ratio(&(i
->framerate
), value
)) != Y4M_OK
)
367 if (i
->framerate
.n
< 0) return Y4M_ERR_RANGE
;
369 case 'I': /* interlacing */
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;
376 i
->interlace
= Y4M_UNKNOWN
; break;
379 case 'A': /* sample (pixel) aspect ratio */
380 if ((err
= y4m_parse_ratio(&(i
->sampleaspect
), value
)) != Y4M_OK
)
382 if (i
->sampleaspect
.n
< 0) return Y4M_ERR_RANGE
;
384 case 'X': /* 'X' meta-tag */
385 if ((err
= y4m_xtag_add(&(i
->x_tags
), token
)) != Y4M_OK
) return err
;
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
);
394 /* unknown tags are *not* ok */
395 return Y4M_ERR_BADTAG
;
400 /* Error checking... width and height must be known since we can't
403 if( i
->width
== Y4M_UNKNOWN
|| i
->height
== Y4M_UNKNOWN
)
404 return Y4M_ERR_HEADER
;
411 static int y4m_parse_frame_tags(char *s
, y4m_frame_info_t
*i
)
418 for (token
= strtok(s
, Y4M_DELIM
);
420 token
= strtok(NULL
, Y4M_DELIM
)) {
421 if (token
[0] == '\0') continue; /* skip empty strings */
425 case 'X': /* 'X' meta-tag */
426 if ((err
= y4m_xtag_add(&(i
->x_tags
), token
)) != Y4M_OK
) return err
;
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
);
435 /* unknown tags are *not* ok */
436 return Y4M_ERR_BADTAG
;
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
];
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
;
468 *p
= '\0'; /* Replace linefeed by end of string */
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
)
480 i
->framelength
= (i
->height
* i
->width
) * 3 / 2;
486 int y4m_write_stream_header(int fd
, y4m_stream_info_t
*i
)
488 char s
[Y4M_LINE_MAX
+1];
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",
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
)))
507 /* non-zero on error */
508 return (y4m_write(fd
, s
, strlen(s
)) ? Y4M_ERR_SYSTEM
: Y4M_OK
);
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
];
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
));
535 /* A clean EOF should end exactly at a frame-boundary */
536 if( remain
== sizeof(Y4M_FRAME_MAGIC
) )
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
;
555 *p
= '\0'; /* Replace linefeed by end of string */
559 if (n
>= Y4M_LINE_MAX
) return Y4M_ERR_HEADER
;
560 /* non-zero on error */
561 return y4m_parse_frame_tags(line
, i
);
566 int y4m_write_frame_header(int fd
, y4m_frame_info_t
*i
)
568 char s
[Y4M_LINE_MAX
+1];
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
)))
577 /* non-zero on error */
578 return (y4m_write(fd
, s
, strlen(s
)) ? Y4M_ERR_SYSTEM
: Y4M_OK
);
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])
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
;
611 int y4m_write_frame(int fd
, y4m_stream_info_t
*si
,
612 y4m_frame_info_t
*fi
, unsigned char *yuv
[3])
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
;
630 /*************************************************************************
632 * Read/Write entire frame, (de)interleaved (to)from two separate fields
634 *************************************************************************/
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])
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
;
655 if (y4m_read(fd
, srcbot
, width
)) return Y4M_ERR_SYSTEM
;
658 /* for chroma, width/height are half as big */
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])
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
;
687 if (y4m_write(fd
, srcbot
, width
)) return Y4M_ERR_SYSTEM
;
690 /* for chroma, width/height are half as big */
701 /*************************************************************************
703 * Handy logging of stream info
705 *************************************************************************/
707 void y4m_log_stream_info(const char *prefix
, y4m_stream_info_t
*i
)
711 snprintf(s
, sizeof(s
), " frame size: ");
712 if (i
->width
== Y4M_UNKNOWN
)
713 snprintf(s
+strlen(s
), sizeof(s
)-strlen(s
), "(?)x");
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 ");
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)");
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
);
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" :
736 if ((i
->sampleaspect
.n
== 0) && (i
->sampleaspect
.d
== 0))
737 mp_msg(MSGT_DEMUX
, MSGL_V
, "%ssample aspect ratio: ?:?\n", prefix
);
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
)
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";
762 return "unknown error code";