mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-14 13:21:28 +00:00
262 lines
5.9 KiB
C
262 lines
5.9 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|