/* GStreamer * Copyright (C) <2007> Jan Schmidt * * 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 #include 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); }