gstreamer/tests/y4mreader.c

262 lines
5.9 KiB
C
Raw Normal View History

/*
* y4mreader.c - Y4M parser
*
* Copyright (C) 2015 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include "y4mreader.h"
#include <stdio.h>
/* format documentation:
* http://wiki.multimedia.cx/index.php?title=YUV4MPEG2 */
static inline gboolean
parse_int (const gchar * str, glong * out_value_ptr)
{
gint saved_errno;
glong value;
gboolean ret;
if (!str)
return FALSE;
str += 1;
if (!str)
return FALSE;
saved_errno = errno;
errno = 0;
value = strtol (str, NULL, 0);
ret = (errno == 0);
errno = saved_errno;
*out_value_ptr = value;
return ret;
}
static gboolean
parse_header (Y4MReader * file)
{
gint i, j;
guint8 header[BUFSIZ];
gint8 b;
size_t s;
gchar *str;
memset (header, 0, BUFSIZ);
s = fread (header, 1, 9, file->fp);
if (s < 9)
return FALSE;
if (memcmp (header, "YUV4MPEG2", 9) != 0)
return FALSE;
for (i = 9; i < BUFSIZ - 1; i++) {
b = fgetc (file->fp);
if (b == EOF)
return FALSE;
if (b == 0xa)
break;
header[i] = b;
}
if (i == BUFSIZ - 1)
return FALSE;
j = 9;
while (j < i) {
if ((header[j] != 0x20) && (header[j - 1] == 0x20)) {
switch (header[j]) {
case 'W':
if (!parse_int ((gchar *) & header[j], (glong *) & file->width))
return FALSE;
break;
case 'H':
if (!parse_int ((gchar *) & header[j], (glong *) & file->height))
return FALSE;
break;
case 'C':
str = (char *) &header[j + 1];
if (strncmp (str, "420", 3) != 0) {
g_warning ("Unsupported chroma subsampling.");
return FALSE; /* unsupported chroma subsampling */
}
break;
case 'I':
str = (char *) &header[j + 1];
if (*str != 'p' && *str != '?') {
g_warning ("Interlaced content are not supported.");
return FALSE; /* interlaced is unsupported */
}
break;
case 'F': /* frame rate ratio */
{
guint num, den;
if (!parse_int ((gchar *) & header[j], (glong *) & num))
return FALSE;
while ((header[j] != ':') && (j < i))
j++;
if (!parse_int ((gchar *) & header[j], (glong *) & den))
return FALSE;
if (num <= 0 || den <= 0) {
file->fps_n = 30; /* default to 30 fps */
file->fps_d = 1;
} else {
file->fps_n = num;
file->fps_d = den;
}
break;
}
case 'A': /* sample aspect ration */
break;
case 'X': /* metadata */
break;
default:
break;
}
}
j++;
}
return TRUE;
}
Y4MReader *
y4m_reader_open (const gchar * filename)
{
Y4MReader *imagefile;
imagefile = g_slice_new0 (Y4MReader);
if (filename) {
imagefile->fp = fopen (filename, "r");
if (!imagefile->fp) {
g_warning ("open file %s error", filename);
goto bail;
}
} else {
imagefile->fp = stdin;
}
if (!parse_header (imagefile))
goto bail;
return imagefile;
bail:
if (imagefile->fp && imagefile->fp != stdin)
fclose (imagefile->fp);
g_slice_free (Y4MReader, imagefile);
return NULL;
}
void
y4m_reader_close (Y4MReader * file)
{
g_return_if_fail (file);
if (file->fp && file->fp != stdin)
fclose (file->fp);
g_slice_free (Y4MReader, file);
}
static gboolean
skip_frame_header (Y4MReader * file)
{
gint i;
guint8 header[BUFSIZ];
gint8 b;
size_t s;
memset (header, 0, BUFSIZ);
s = fread (header, 1, 5, file->fp);
if (s < 5)
return FALSE;
if (memcmp (header, "FRAME", 5) != 0)
return FALSE;
for (i = 5; i < BUFSIZ - 1; i++) {
b = fgetc (file->fp);
if (b == EOF)
return FALSE;
if (b == 0xa)
break;
header[i] = b;
}
return (i < BUFSIZ - 1);
}
gboolean
y4m_reader_load_image (Y4MReader * file, GstVaapiImage * image)
{
guint8 *plane;
size_t s;
guint frame_size, stride, i;
g_return_val_if_fail (gst_vaapi_image_is_mapped (image), FALSE);
g_return_val_if_fail (file && file->fp, FALSE);
/* only valid for I420 */
frame_size = file->height * file->width * 3 / 2;
if (gst_vaapi_image_get_data_size (image) < frame_size)
return FALSE;
if (gst_vaapi_image_get_plane_count (image) != 3)
return FALSE;
if (!skip_frame_header (file))
return FALSE;
/* Y plane */
plane = gst_vaapi_image_get_plane (image, 0);
stride = gst_vaapi_image_get_pitch (image, 0);
for (i = 0; i < file->height; i++) {
s = fread (plane, 1, file->width, file->fp);
if (s != file->width)
return FALSE;
plane += stride;
}
/* U plane */
plane = gst_vaapi_image_get_plane (image, 1);
stride = gst_vaapi_image_get_pitch (image, 1);
for (i = 0; i < file->height / 2; i++) {
s = fread (plane, 1, file->width / 2, file->fp);
if (s != file->width / 2)
return FALSE;
plane += stride;
}
/* V plane */
plane = gst_vaapi_image_get_plane (image, 2);
stride = gst_vaapi_image_get_pitch (image, 2);
for (i = 0; i < file->height / 2; i++) {
s = fread (plane, 1, file->width / 2, file->fp);
if (s != file->width / 2)
return FALSE;
plane += stride;
}
return TRUE;
}