gstreamer/gst/videoparsers/mpegvideoparse.c
Alessandro Decina b01ee6bbf1 mpegvideoparse: fix sequence header parsing
After bitrate there's 11 bits (1 marker bit + VBV buffer size) before the load
intra quantiser flag.
2011-08-04 09:52:25 +02:00

275 lines
6.8 KiB
C

/* GStreamer
* Copyright (C) <2007> Jan Schmidt <thaytan@mad.scientist.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "mpegvideoparse.h"
#include <string.h>
#include <gst/base/gstbitreader.h>
GST_DEBUG_CATEGORY_EXTERN (mpegv_parse_debug);
#define GST_CAT_DEFAULT mpegv_parse_debug
#define GET_BITS(b, num, bits) G_STMT_START { \
if (!gst_bit_reader_get_bits_uint32(b, bits, num)) \
goto failed; \
GST_TRACE ("parsed %d bits: %d", num, *(bits)); \
} G_STMT_END
#define MARKER_BIT(b) G_STMT_START { \
guint32 i; \
GET_BITS(b, 1, &i); \
if (i != 0x1) \
goto failed; \
} G_STMT_END
static inline gboolean
find_start_code (GstBitReader * b)
{
guint32 bits;
/* 0 bits until byte aligned */
while (b->bit != 0) {
GET_BITS (b, 1, &bits);
}
/* 0 bytes until startcode */
while (gst_bit_reader_peek_bits_uint32 (b, &bits, 32)) {
if (bits >> 8 == 0x1) {
return TRUE;
} else {
gst_bit_reader_skip (b, 8);
}
}
return FALSE;
failed:
return FALSE;
}
static gboolean
gst_mpeg_video_params_parse_extension (MPEGVParams * params, GstBitReader * br)
{
guint32 bits;
/* double-check */
GET_BITS (br, 32, &bits);
if (bits != 0x100 + MPEG_PACKET_EXTENSION)
goto failed;
/* extension_start_code identifier */
GET_BITS (br, 4, &bits);
/* profile_and_level_indication */
GET_BITS (br, 4, &bits);
params->profile = bits;
GET_BITS (br, 4, &bits);
params->level = bits;
/* progressive_sequence */
GET_BITS (br, 1, &bits);
params->progressive = bits;
/* chroma_format */
GET_BITS (br, 2, &bits);
/* horizontal_size_extension */
GET_BITS (br, 2, &bits);
params->width += (bits << 12);
/* vertical_size_extension */
GET_BITS (br, 2, &bits);
params->height += (bits << 12);
/* bit_rate_extension */
GET_BITS (br, 12, &bits);
if (params->bitrate)
params->bitrate += (bits << 18) * 400;
/* marker_bit */
MARKER_BIT (br);
/* vbv_buffer_size_extension */
GET_BITS (br, 8, &bits);
/* low_delay */
GET_BITS (br, 1, &bits);
/* frame_rate_extension_n */
GET_BITS (br, 2, &bits);
params->fps_n *= bits + 1;
/* frame_rate_extension_d */
GET_BITS (br, 5, &bits);
params->fps_d *= bits + 1;
return TRUE;
/* ERRORS */
failed:
{
GST_WARNING ("Failed to parse sequence extension");
return FALSE;
}
}
/* Set the Pixel Aspect Ratio in our hdr from a DAR code in the data */
static void
set_par_from_dar (MPEGVParams * params, guint8 asr_code)
{
/* Pixel_width = DAR_width * display_vertical_size */
/* Pixel_height = DAR_height * display_horizontal_size */
switch (asr_code) {
case 0x02: /* 3:4 DAR = 4:3 pixels */
params->par_w = 4 * params->height;
params->par_h = 3 * params->width;
break;
case 0x03: /* 9:16 DAR */
params->par_w = 16 * params->height;
params->par_h = 9 * params->width;
break;
case 0x04: /* 1:2.21 DAR */
params->par_w = 221 * params->height;
params->par_h = 100 * params->width;
break;
case 0x01: /* Square pixels */
params->par_w = params->par_h = 1;
break;
default:
GST_DEBUG ("unknown/invalid aspect_ratio_information %d", asr_code);
break;
}
}
static void
set_fps_from_code (MPEGVParams * params, guint8 fps_code)
{
const gint framerates[][2] = {
{30, 1}, {24000, 1001}, {24, 1}, {25, 1},
{30000, 1001}, {30, 1}, {50, 1}, {60000, 1001},
{60, 1}, {30, 1}
};
if (fps_code && fps_code < 10) {
params->fps_n = framerates[fps_code][0];
params->fps_d = framerates[fps_code][1];
} else {
GST_DEBUG ("unknown/invalid frame_rate_code %d", fps_code);
/* Force a valid framerate */
/* FIXME or should this be kept unknown ?? */
params->fps_n = 30000;
params->fps_d = 1001;
}
}
static gboolean
gst_mpeg_video_params_parse_sequence (MPEGVParams * params, GstBitReader * br)
{
guint32 bits;
GET_BITS (br, 32, &bits);
if (bits != 0x100 + MPEG_PACKET_SEQUENCE)
goto failed;
/* assume MPEG-1 till otherwise discovered */
params->mpeg_version = 1;
GET_BITS (br, 12, &bits);
params->width = bits;
GET_BITS (br, 12, &bits);
params->height = bits;
GET_BITS (br, 4, &bits);
set_par_from_dar (params, bits);
GET_BITS (br, 4, &bits);
set_fps_from_code (params, bits);
GET_BITS (br, 18, &bits);
if (bits == 0x3ffff) {
/* VBR stream */
params->bitrate = 0;
} else {
/* Value in header is in units of 400 bps */
params->bitrate *= 400;
}
/* skip 1 + VBV buffer size */
if (!gst_bit_reader_skip (br, 11))
goto failed;
/* constrained_parameters_flag */
GET_BITS (br, 1, &bits);
/* load_intra_quantiser_matrix */
GET_BITS (br, 1, &bits);
if (bits) {
if (!gst_bit_reader_skip (br, 8 * 64))
goto failed;
}
/* load_non_intra_quantiser_matrix */
GET_BITS (br, 1, &bits);
if (bits) {
if (!gst_bit_reader_skip (br, 8 * 64))
goto failed;
}
/* check for MPEG-2 sequence extension */
while (find_start_code (br)) {
gst_bit_reader_peek_bits_uint32 (br, &bits, 32);
if (bits == 0x100 + MPEG_PACKET_EXTENSION) {
if (!gst_mpeg_video_params_parse_extension (params, br))
goto failed;
params->mpeg_version = 2;
}
}
/* dump some info */
GST_LOG ("width x height: %d x %d", params->width, params->height);
GST_LOG ("fps: %d/%d", params->fps_n, params->fps_d);
GST_LOG ("par: %d/%d", params->par_w, params->par_h);
GST_LOG ("profile/level: %d/%d", params->profile, params->level);
GST_LOG ("bitrate/progressive: %d/%d", params->bitrate, params->progressive);
return TRUE;
/* ERRORS */
failed:
{
GST_WARNING ("Failed to parse sequence header");
/* clear out stuff */
memset (params, 0, sizeof (*params));
return FALSE;
}
}
gboolean
gst_mpeg_video_params_parse_config (MPEGVParams * params, const guint8 * data,
guint size)
{
GstBitReader br;
if (size < 4)
return FALSE;
gst_bit_reader_init (&br, data, size);
return gst_mpeg_video_params_parse_sequence (params, &br);
}