| /* Gstreamer |
| * Copyright (C) <2011> Intel Corporation |
| * Copyright (C) <2011> Collabora Ltd. |
| * Copyright (C) <2011> Thibault Saunier <thibault.saunier@collabora.com> |
| * |
| * From bad/sys/vdpau/mpeg/mpegutil.c: |
| * Copyright (C) <2007> Jan Schmidt <thaytan@mad.scientist.com> |
| * Copyright (C) <2009> Carl-Anton Ingmarsson <ca.ingmarsson@gmail.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., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| /** |
| * SECTION:gstmpegvideoparser |
| * @title: GstMpegvideoParser |
| * @short_description: Convenience library for mpeg1 and 2 video |
| * bitstream parsing. |
| * |
| * Provides useful functions for mpeg videos bitstream parsing. |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include "config.h" |
| #endif |
| |
| #include "gstmpegvideoparser.h" |
| #include "parserutils.h" |
| |
| #include <string.h> |
| #include <gst/base/gstbitreader.h> |
| #include <gst/base/gstbytereader.h> |
| |
| #define MARKER_BIT 0x1 |
| |
| /* default intra quant matrix, in zig-zag order */ |
| static const guint8 default_intra_quantizer_matrix[64] = { |
| 8, |
| 16, 16, |
| 19, 16, 19, |
| 22, 22, 22, 22, |
| 22, 22, 26, 24, 26, |
| 27, 27, 27, 26, 26, 26, |
| 26, 27, 27, 27, 29, 29, 29, |
| 34, 34, 34, 29, 29, 29, 27, 27, |
| 29, 29, 32, 32, 34, 34, 37, |
| 38, 37, 35, 35, 34, 35, |
| 38, 38, 40, 40, 40, |
| 48, 48, 46, 46, |
| 56, 56, 58, |
| 69, 69, |
| 83 |
| }; |
| |
| static const guint8 mpeg_zigzag_8x8[64] = { |
| 0, 1, 8, 16, 9, 2, 3, 10, |
| 17, 24, 32, 25, 18, 11, 4, 5, |
| 12, 19, 26, 33, 40, 48, 41, 34, |
| 27, 20, 13, 6, 7, 14, 21, 28, |
| 35, 42, 49, 56, 57, 50, 43, 36, |
| 29, 22, 15, 23, 30, 37, 44, 51, |
| 58, 59, 52, 45, 38, 31, 39, 46, |
| 53, 60, 61, 54, 47, 55, 62, 63 |
| }; |
| |
| enum |
| { |
| GST_MPEG_VIDEO_MACROBLOCK_ESCAPE = G_MAXUINT, |
| }; |
| |
| /* Table B-1: Variable length codes for macroblock_address_increment */ |
| static const VLCTable mpeg2_mbaddr_vlc_table[] = { |
| {1, 0x01, 1}, |
| {2, 0x03, 3}, |
| {3, 0x02, 3}, |
| {4, 0x03, 4}, |
| {5, 0x02, 4}, |
| {6, 0x03, 5}, |
| {7, 0x02, 5}, |
| {8, 0x07, 7}, |
| {9, 0x06, 7}, |
| {10, 0x0b, 8}, |
| {11, 0x0a, 8}, |
| {12, 0x09, 8}, |
| {13, 0x08, 8}, |
| {14, 0x07, 8}, |
| {15, 0x06, 8}, |
| {16, 0x17, 10}, |
| {17, 0x16, 10}, |
| {18, 0x15, 10}, |
| {19, 0x14, 10}, |
| {20, 0x13, 10}, |
| {21, 0x12, 10}, |
| {22, 0x23, 11}, |
| {23, 0x22, 11}, |
| {24, 0x21, 11}, |
| {25, 0x20, 11}, |
| {26, 0x1f, 11}, |
| {27, 0x1e, 11}, |
| {28, 0x1d, 11}, |
| {29, 0x1c, 11}, |
| {30, 0x1b, 11}, |
| {31, 0x1a, 11}, |
| {32, 0x19, 11}, |
| {33, 0x18, 11}, |
| {GST_MPEG_VIDEO_MACROBLOCK_ESCAPE, 0x08, 11} |
| }; |
| |
| GST_DEBUG_CATEGORY_STATIC (mpegvideo_parser_debug); |
| #define GST_CAT_DEFAULT mpegvideo_parser_debug |
| |
| #define INITIALIZE_DEBUG_CATEGORY \ |
| GST_DEBUG_CATEGORY_INIT (mpegvideo_parser_debug, "codecparsers_mpegvideo", \ |
| 0, "Mpegvideo parser library"); |
| |
| |
| /* Set the Pixel Aspect Ratio in our hdr from a ASR code in the data */ |
| static void |
| set_par_from_asr_mpeg1 (GstMpegVideoSequenceHdr * seqhdr, guint8 asr_code) |
| { |
| int ratios[16][2] = { |
| {0, 0}, /* 0, Invalid */ |
| {1, 1}, /* 1, 1.0 */ |
| {10000, 6735}, /* 2, 0.6735 */ |
| {64, 45}, /* 3, 0.7031 16:9 625 line */ |
| {10000, 7615}, /* 4, 0.7615 */ |
| {10000, 8055}, /* 5, 0.8055 */ |
| {32, 27}, /* 6, 0.8437 */ |
| {10000, 8935}, /* 7, 0.8935 */ |
| {10000, 9375}, /* 8, 0.9375 */ |
| {10000, 9815}, /* 9, 0.9815 */ |
| {10000, 10255}, /* 10, 1.0255 */ |
| {10000, 10695}, /* 11, 1.0695 */ |
| {8, 9}, /* 12, 1.125 */ |
| {10000, 11575}, /* 13, 1.1575 */ |
| {10000, 12015}, /* 14, 1.2015 */ |
| {0, 0}, /* 15, invalid */ |
| }; |
| asr_code &= 0xf; |
| |
| seqhdr->par_w = ratios[asr_code][0]; |
| seqhdr->par_h = ratios[asr_code][1]; |
| } |
| |
| static void |
| set_fps_from_code (GstMpegVideoSequenceHdr * seqhdr, 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) { |
| seqhdr->fps_n = framerates[fps_code][0]; |
| seqhdr->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 ?? */ |
| seqhdr->fps_n = 30000; |
| seqhdr->fps_d = 1001; |
| } |
| } |
| |
| /* @size and @offset are wrt current reader position */ |
| static inline gint |
| scan_for_start_codes (const GstByteReader * reader, guint offset, guint size) |
| { |
| const guint8 *data; |
| guint i = 0; |
| |
| g_assert ((guint64) offset + size <= reader->size - reader->byte); |
| |
| /* we can't find the pattern with less than 4 bytes */ |
| if (G_UNLIKELY (size < 4)) |
| return -1; |
| |
| data = reader->data + reader->byte + offset; |
| |
| while (i <= (size - 4)) { |
| if (data[i + 2] > 1) { |
| i += 3; |
| } else if (data[i + 1]) { |
| i += 2; |
| } else if (data[i] || data[i + 2] != 1) { |
| i++; |
| } else { |
| break; |
| } |
| } |
| |
| if (i <= (size - 4)) |
| return offset + i; |
| |
| /* nothing found */ |
| return -1; |
| } |
| |
| /****** API *******/ |
| |
| /** |
| * gst_mpeg_video_parse: |
| * @packet: a #GstMpegVideoPacket to fill with the data and offset of the |
| * next packet found |
| * @data: The data to parse |
| * @size: The size of @data |
| * @offset: The offset from which to start parsing |
| * |
| * Parses the MPEG 1/2 video bitstream contained in @data, and returns the |
| * offset, and if known also the size, in @packet. This function will scan |
| * the data to find the next packet if needed. |
| * |
| * Returns: TRUE if a packet start code was found, otherwise FALSE. |
| */ |
| gboolean |
| gst_mpeg_video_parse (GstMpegVideoPacket * packet, |
| const guint8 * data, gsize size, guint offset) |
| { |
| gint off; |
| GstByteReader br; |
| |
| INITIALIZE_DEBUG_CATEGORY; |
| |
| if (size <= offset) { |
| GST_DEBUG ("Can't parse from offset %d, buffer is to small", offset); |
| return FALSE; |
| } |
| |
| size -= offset; |
| gst_byte_reader_init (&br, &data[offset], size); |
| |
| off = scan_for_start_codes (&br, 0, size); |
| |
| if (off < 0) { |
| GST_DEBUG ("No start code prefix in this buffer"); |
| return FALSE; |
| } |
| |
| if (gst_byte_reader_skip (&br, off + 3) == FALSE) |
| goto failed; |
| |
| if (gst_byte_reader_get_uint8 (&br, &packet->type) == FALSE) |
| goto failed; |
| |
| packet->data = data; |
| packet->offset = offset + off + 4; |
| packet->size = -1; |
| |
| /* try to find end of packet */ |
| size -= off + 4; |
| off = scan_for_start_codes (&br, 0, size); |
| |
| if (off > 0) |
| packet->size = off; |
| |
| return TRUE; |
| |
| failed: |
| { |
| GST_WARNING ("Failed to parse"); |
| return FALSE; |
| } |
| } |
| |
| /** |
| * gst_mpeg_video_packet_parse_sequence_header: |
| * @packet: The #GstMpegVideoPacket that carries the data |
| * @seqhdr: (out): The #GstMpegVideoSequenceHdr structure to fill |
| * |
| * Parses the @seqhdr MPEG Video Sequence Header structure members |
| * from video @packet |
| * |
| * Returns: %TRUE if the seqhdr could be parsed correctly, %FALSE otherwise. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_mpeg_video_packet_parse_sequence_header (const GstMpegVideoPacket * packet, |
| GstMpegVideoSequenceHdr * seqhdr) |
| { |
| GstBitReader br; |
| guint8 bits; |
| |
| g_return_val_if_fail (seqhdr != NULL, FALSE); |
| |
| if (packet->size < 8) |
| return FALSE; |
| |
| INITIALIZE_DEBUG_CATEGORY; |
| |
| gst_bit_reader_init (&br, &packet->data[packet->offset], packet->size); |
| |
| /* Setting the height/width codes */ |
| READ_UINT16 (&br, seqhdr->width, 12); |
| READ_UINT16 (&br, seqhdr->height, 12); |
| |
| READ_UINT8 (&br, seqhdr->aspect_ratio_info, 4); |
| /* Interpret PAR according to MPEG-1. Needs to be reinterpreted |
| * later, if a sequence_display extension is seen */ |
| set_par_from_asr_mpeg1 (seqhdr, seqhdr->aspect_ratio_info); |
| |
| READ_UINT8 (&br, seqhdr->frame_rate_code, 4); |
| set_fps_from_code (seqhdr, seqhdr->frame_rate_code); |
| |
| READ_UINT32 (&br, seqhdr->bitrate_value, 18); |
| if (seqhdr->bitrate_value == 0x3ffff) { |
| /* VBR stream */ |
| seqhdr->bitrate = 0; |
| } else { |
| /* Value in header is in units of 400 bps */ |
| seqhdr->bitrate = seqhdr->bitrate_value * 400; |
| } |
| |
| READ_UINT8 (&br, bits, 1); |
| if (bits != MARKER_BIT) |
| goto failed; |
| |
| /* VBV buffer size */ |
| READ_UINT16 (&br, seqhdr->vbv_buffer_size_value, 10); |
| |
| /* constrained_parameters_flag */ |
| READ_UINT8 (&br, seqhdr->constrained_parameters_flag, 1); |
| |
| /* load_intra_quantiser_matrix */ |
| READ_UINT8 (&br, seqhdr->load_intra_quantiser_matrix, 1); |
| if (seqhdr->load_intra_quantiser_matrix) { |
| gint i; |
| for (i = 0; i < 64; i++) |
| READ_UINT8 (&br, seqhdr->intra_quantizer_matrix[i], 8); |
| } else |
| memcpy (seqhdr->intra_quantizer_matrix, default_intra_quantizer_matrix, 64); |
| |
| /* non intra quantizer matrix */ |
| READ_UINT8 (&br, seqhdr->load_non_intra_quantiser_matrix, 1); |
| if (seqhdr->load_non_intra_quantiser_matrix) { |
| gint i; |
| for (i = 0; i < 64; i++) |
| READ_UINT8 (&br, seqhdr->non_intra_quantizer_matrix[i], 8); |
| } else |
| memset (seqhdr->non_intra_quantizer_matrix, 16, 64); |
| |
| /* dump some info */ |
| GST_LOG ("width x height: %d x %d", seqhdr->width, seqhdr->height); |
| GST_LOG ("fps: %d/%d", seqhdr->fps_n, seqhdr->fps_d); |
| GST_LOG ("par: %d/%d", seqhdr->par_w, seqhdr->par_h); |
| GST_LOG ("bitrate: %d", seqhdr->bitrate); |
| |
| return TRUE; |
| |
| /* ERRORS */ |
| failed: |
| { |
| GST_WARNING ("Failed to parse sequence header"); |
| /* clear out stuff */ |
| memset (seqhdr, 0, sizeof (*seqhdr)); |
| return FALSE; |
| } |
| } |
| |
| /** |
| * gst_mpeg_video_packet_parse_sequence_extension: |
| * @packet: The #GstMpegVideoPacket that carries the data |
| * @seqext: (out): The #GstMpegVideoSequenceExt structure to fill |
| * |
| * Parses the @seqext MPEG Video Sequence Extension structure members |
| * from video @packet |
| * |
| * Returns: %TRUE if the seqext could be parsed correctly, %FALSE otherwise. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_mpeg_video_packet_parse_sequence_extension (const GstMpegVideoPacket * |
| packet, GstMpegVideoSequenceExt * seqext) |
| { |
| GstBitReader br; |
| |
| g_return_val_if_fail (seqext != NULL, FALSE); |
| |
| if (packet->size < 6) { |
| GST_DEBUG ("not enough bytes to parse the extension"); |
| return FALSE; |
| } |
| |
| gst_bit_reader_init (&br, &packet->data[packet->offset], packet->size); |
| |
| if (gst_bit_reader_get_bits_uint8_unchecked (&br, 4) != |
| GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE) { |
| GST_DEBUG ("Not parsing a sequence extension"); |
| return FALSE; |
| } |
| |
| /* skip profile and level escape bit */ |
| seqext->profile_level_escape_bit = |
| gst_bit_reader_get_bits_uint8_unchecked (&br, 1); |
| |
| seqext->profile = gst_bit_reader_get_bits_uint8_unchecked (&br, 3); |
| seqext->level = gst_bit_reader_get_bits_uint8_unchecked (&br, 4); |
| |
| /* progressive */ |
| seqext->progressive = gst_bit_reader_get_bits_uint8_unchecked (&br, 1); |
| |
| /* chroma format */ |
| seqext->chroma_format = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); |
| |
| /* resolution extension */ |
| seqext->horiz_size_ext = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); |
| seqext->vert_size_ext = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); |
| |
| seqext->bitrate_ext = gst_bit_reader_get_bits_uint16_unchecked (&br, 12); |
| |
| /* skip marker bits */ |
| gst_bit_reader_skip_unchecked (&br, 1); |
| |
| seqext->vbv_buffer_size_extension = |
| gst_bit_reader_get_bits_uint8_unchecked (&br, 8); |
| seqext->low_delay = gst_bit_reader_get_bits_uint8_unchecked (&br, 1); |
| |
| /* framerate extension */ |
| seqext->fps_n_ext = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); |
| seqext->fps_d_ext = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_mpeg_video_packet_parse_sequence_display_extension: |
| * @packet: The #GstMpegVideoPacket that carries the data |
| * @seqdisplayext: (out): The #GstMpegVideoSequenceDisplayExt |
| * structure to fill |
| * |
| * Parses the @seqext MPEG Video Sequence Display Extension structure |
| * members from video @packet |
| * |
| * Returns: %TRUE if the seqext could be parsed correctly, %FALSE otherwise. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_mpeg_video_packet_parse_sequence_display_extension (const GstMpegVideoPacket |
| * packet, GstMpegVideoSequenceDisplayExt * seqdisplayext) |
| { |
| GstBitReader br; |
| |
| g_return_val_if_fail (seqdisplayext != NULL, FALSE); |
| |
| if (packet->size < 5) { |
| GST_DEBUG ("not enough bytes to parse the extension"); |
| return FALSE; |
| } |
| |
| gst_bit_reader_init (&br, &packet->data[packet->offset], packet->size); |
| |
| if (gst_bit_reader_get_bits_uint8_unchecked (&br, 4) != |
| GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_DISPLAY) { |
| GST_DEBUG ("Not parsing a sequence display extension"); |
| return FALSE; |
| } |
| |
| seqdisplayext->video_format = |
| gst_bit_reader_get_bits_uint8_unchecked (&br, 3); |
| seqdisplayext->colour_description_flag = |
| gst_bit_reader_get_bits_uint8_unchecked (&br, 1); |
| |
| if (seqdisplayext->colour_description_flag) { |
| seqdisplayext->colour_primaries = |
| gst_bit_reader_get_bits_uint8_unchecked (&br, 8); |
| seqdisplayext->transfer_characteristics = |
| gst_bit_reader_get_bits_uint8_unchecked (&br, 8); |
| seqdisplayext->matrix_coefficients = |
| gst_bit_reader_get_bits_uint8_unchecked (&br, 8); |
| } |
| |
| if (gst_bit_reader_get_remaining (&br) < 29) { |
| GST_DEBUG ("Not enough remaining bytes to parse the extension"); |
| return FALSE; |
| } |
| |
| seqdisplayext->display_horizontal_size = |
| gst_bit_reader_get_bits_uint16_unchecked (&br, 14); |
| /* skip marker bit */ |
| gst_bit_reader_skip_unchecked (&br, 1); |
| seqdisplayext->display_vertical_size = |
| gst_bit_reader_get_bits_uint16_unchecked (&br, 14); |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_mpeg_video_packet_parse_sequence_scalable_extension: |
| * @packet: The #GstMpegVideoPacket that carries the data |
| * @seqscaleext: (out): The #GstMpegVideoSequenceScalableExt structure to fill |
| * |
| * Parses the @seqscaleext MPEG Video Sequence Scalable Extension structure |
| * members from video @packet |
| * |
| * Returns: %TRUE if the seqext could be parsed correctly, %FALSE otherwise. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_mpeg_video_packet_parse_sequence_scalable_extension |
| (const GstMpegVideoPacket * packet, |
| GstMpegVideoSequenceScalableExt * seqscaleext) { |
| GstBitReader br; |
| |
| g_return_val_if_fail (seqscaleext != NULL, FALSE); |
| |
| if (packet->size < 2) { |
| GST_DEBUG ("not enough bytes to parse the extension"); |
| return FALSE; |
| } |
| |
| gst_bit_reader_init (&br, &packet->data[packet->offset], packet->size); |
| |
| if (gst_bit_reader_get_bits_uint8_unchecked (&br, 4) != |
| GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_SCALABLE) { |
| GST_DEBUG ("Not parsing a sequence scalable extension"); |
| return FALSE; |
| } |
| |
| READ_UINT8 (&br, seqscaleext->scalable_mode, 2); |
| READ_UINT8 (&br, seqscaleext->layer_id, 4); |
| |
| if (seqscaleext->scalable_mode == GST_MPEG_VIDEO_SEQ_SCALABLE_MODE_SPATIAL) { |
| READ_UINT16 (&br, seqscaleext->lower_layer_prediction_horizontal_size, 14); |
| |
| SKIP (&br, 1); |
| |
| READ_UINT16 (&br, seqscaleext->lower_layer_prediction_vertical_size, 14); |
| |
| READ_UINT8 (&br, seqscaleext->horizontal_subsampling_factor_m, 5); |
| READ_UINT8 (&br, seqscaleext->horizontal_subsampling_factor_n, 5); |
| READ_UINT8 (&br, seqscaleext->vertical_subsampling_factor_m, 5); |
| READ_UINT8 (&br, seqscaleext->vertical_subsampling_factor_n, 5); |
| } |
| |
| if (seqscaleext->scalable_mode == GST_MPEG_VIDEO_SEQ_SCALABLE_MODE_TEMPORAL) { |
| READ_UINT8 (&br, seqscaleext->picture_mux_enable, 1); |
| if (seqscaleext->picture_mux_enable) |
| READ_UINT8 (&br, seqscaleext->mux_to_progressive_sequence, 1); |
| READ_UINT8 (&br, seqscaleext->picture_mux_order, 3); |
| READ_UINT8 (&br, seqscaleext->picture_mux_factor, 3); |
| } |
| |
| return TRUE; |
| |
| failed: |
| GST_WARNING ("error parsing \"Sequence Scalable Extension\""); |
| return FALSE; |
| } |
| |
| gboolean |
| gst_mpeg_video_finalise_mpeg2_sequence_header (GstMpegVideoSequenceHdr * seqhdr, |
| GstMpegVideoSequenceExt * seqext, |
| GstMpegVideoSequenceDisplayExt * displayext) |
| { |
| guint32 w; |
| guint32 h; |
| |
| if (seqext) { |
| seqhdr->fps_n = seqhdr->fps_n * (seqext->fps_n_ext + 1); |
| seqhdr->fps_d = seqhdr->fps_d * (seqext->fps_d_ext + 1); |
| /* Extend width and height to 14 bits by adding the extension bits */ |
| seqhdr->width |= (seqext->horiz_size_ext << 12); |
| seqhdr->height |= (seqext->vert_size_ext << 12); |
| seqhdr->bitrate += (seqext->bitrate_ext << 18) * 400; |
| } |
| |
| w = seqhdr->width; |
| h = seqhdr->height; |
| if (displayext) { |
| /* Use the display size for calculating PAR when display ext present. |
| * But we are handling this like what DVD players are doing. Which means, |
| * ignore the display extension values if they are greater than the width/height |
| * values provided by seqhdr and calculate the PAR based on the seqhdr values. */ |
| if (displayext->display_horizontal_size < w) |
| w = displayext->display_horizontal_size; |
| if (displayext->display_vertical_size < h) |
| h = displayext->display_vertical_size; |
| } |
| |
| /* Pixel_width = DAR_width * display_vertical_size */ |
| /* Pixel_height = DAR_height * display_horizontal_size */ |
| switch (seqhdr->aspect_ratio_info) { |
| case 0x01: /* Square pixels */ |
| seqhdr->par_w = seqhdr->par_h = 1; |
| break; |
| case 0x02: /* 3:4 DAR = 4:3 pixels */ |
| seqhdr->par_w = 4 * h; |
| seqhdr->par_h = 3 * w; |
| break; |
| case 0x03: /* 9:16 DAR */ |
| seqhdr->par_w = 16 * h; |
| seqhdr->par_h = 9 * w; |
| break; |
| case 0x04: /* 1:2.21 DAR */ |
| seqhdr->par_w = 221 * h; |
| seqhdr->par_h = 100 * w; |
| break; |
| default: |
| GST_DEBUG ("unknown/invalid aspect_ratio_information %d", |
| seqhdr->aspect_ratio_info); |
| break; |
| } |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_mpeg_video_packet_parse_quant_matrix_extension: |
| * @packet: The #GstMpegVideoPacket that carries the data |
| * @quant: (out): The #GstMpegVideoQuantMatrixExt structure to fill |
| * |
| * Parses the @quant MPEG Video Quantization Matrix Extension |
| * structure members from video @packet |
| * |
| * Returns: %TRUE if the quant matrix extension could be parsed correctly, |
| * %FALSE otherwise. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_mpeg_video_packet_parse_quant_matrix_extension (const GstMpegVideoPacket * |
| packet, GstMpegVideoQuantMatrixExt * quant) |
| { |
| guint8 i; |
| GstBitReader br; |
| |
| g_return_val_if_fail (quant != NULL, FALSE); |
| |
| if (packet->size < 1) { |
| GST_DEBUG ("not enough bytes to parse the extension"); |
| return FALSE; |
| } |
| |
| gst_bit_reader_init (&br, &packet->data[packet->offset], packet->size); |
| |
| if (gst_bit_reader_get_bits_uint8_unchecked (&br, 4) != |
| GST_MPEG_VIDEO_PACKET_EXT_QUANT_MATRIX) { |
| GST_DEBUG ("Not parsing a quant matrix extension"); |
| return FALSE; |
| } |
| |
| READ_UINT8 (&br, quant->load_intra_quantiser_matrix, 1); |
| if (quant->load_intra_quantiser_matrix) { |
| for (i = 0; i < 64; i++) { |
| READ_UINT8 (&br, quant->intra_quantiser_matrix[i], 8); |
| } |
| } |
| |
| READ_UINT8 (&br, quant->load_non_intra_quantiser_matrix, 1); |
| if (quant->load_non_intra_quantiser_matrix) { |
| for (i = 0; i < 64; i++) { |
| READ_UINT8 (&br, quant->non_intra_quantiser_matrix[i], 8); |
| } |
| } |
| |
| READ_UINT8 (&br, quant->load_chroma_intra_quantiser_matrix, 1); |
| if (quant->load_chroma_intra_quantiser_matrix) { |
| for (i = 0; i < 64; i++) { |
| READ_UINT8 (&br, quant->chroma_intra_quantiser_matrix[i], 8); |
| } |
| } |
| |
| READ_UINT8 (&br, quant->load_chroma_non_intra_quantiser_matrix, 1); |
| if (quant->load_chroma_non_intra_quantiser_matrix) { |
| for (i = 0; i < 64; i++) { |
| READ_UINT8 (&br, quant->chroma_non_intra_quantiser_matrix[i], 8); |
| } |
| } |
| |
| return TRUE; |
| |
| failed: |
| GST_WARNING ("error parsing \"Quant Matrix Extension\""); |
| return FALSE; |
| } |
| |
| /** |
| * gst_mpeg_video_packet_parse_picture_extension: |
| * @packet: The #GstMpegVideoPacket that carries the data |
| * @ext: (out): The #GstMpegVideoPictureExt structure to fill |
| * |
| * Parse the @ext MPEG Video Picture Extension structure members from |
| * video @packet |
| * |
| * Returns: %TRUE if the picture extension could be parsed correctly, |
| * %FALSE otherwise. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_mpeg_video_packet_parse_picture_extension (const GstMpegVideoPacket * |
| packet, GstMpegVideoPictureExt * ext) |
| { |
| GstBitReader br; |
| |
| g_return_val_if_fail (ext != NULL, FALSE); |
| |
| if (packet->size < 5) |
| return FALSE; |
| |
| gst_bit_reader_init (&br, &packet->data[packet->offset], packet->size); |
| |
| if (gst_bit_reader_get_bits_uint8_unchecked (&br, 4) != |
| GST_MPEG_VIDEO_PACKET_EXT_PICTURE) { |
| GST_DEBUG ("Extension is not a picture extension"); |
| return FALSE; |
| } |
| |
| /* f_code */ |
| READ_UINT8 (&br, ext->f_code[0][0], 4); |
| READ_UINT8 (&br, ext->f_code[0][1], 4); |
| READ_UINT8 (&br, ext->f_code[1][0], 4); |
| READ_UINT8 (&br, ext->f_code[1][1], 4); |
| |
| /* intra DC precision */ |
| READ_UINT8 (&br, ext->intra_dc_precision, 2); |
| |
| /* picture structure */ |
| READ_UINT8 (&br, ext->picture_structure, 2); |
| |
| /* top field first */ |
| READ_UINT8 (&br, ext->top_field_first, 1); |
| |
| /* frame pred frame dct */ |
| READ_UINT8 (&br, ext->frame_pred_frame_dct, 1); |
| |
| /* concealment motion vectors */ |
| READ_UINT8 (&br, ext->concealment_motion_vectors, 1); |
| |
| /* q scale type */ |
| READ_UINT8 (&br, ext->q_scale_type, 1); |
| |
| /* intra vlc format */ |
| READ_UINT8 (&br, ext->intra_vlc_format, 1); |
| |
| /* alternate scan */ |
| READ_UINT8 (&br, ext->alternate_scan, 1); |
| |
| /* repeat first field */ |
| READ_UINT8 (&br, ext->repeat_first_field, 1); |
| |
| /* chroma_420_type */ |
| READ_UINT8 (&br, ext->chroma_420_type, 1); |
| |
| /* progressive_frame */ |
| READ_UINT8 (&br, ext->progressive_frame, 1); |
| |
| /* composite display */ |
| READ_UINT8 (&br, ext->composite_display, 1); |
| |
| if (ext->composite_display) { |
| |
| /* v axis */ |
| READ_UINT8 (&br, ext->v_axis, 1); |
| |
| /* field sequence */ |
| READ_UINT8 (&br, ext->field_sequence, 3); |
| |
| /* sub carrier */ |
| READ_UINT8 (&br, ext->sub_carrier, 1); |
| |
| /* burst amplitude */ |
| READ_UINT8 (&br, ext->burst_amplitude, 7); |
| |
| /* sub_carrier phase */ |
| READ_UINT8 (&br, ext->sub_carrier_phase, 8); |
| } |
| |
| return TRUE; |
| |
| failed: |
| GST_WARNING ("error parsing \"Picture Coding Extension\""); |
| return FALSE; |
| |
| } |
| |
| /** |
| * gst_mpeg_video_packet_parse_picture_header: |
| * @packet: The #GstMpegVideoPacket that carries the data |
| * @pichdr: (out): The #GstMpegVideoPictureHdr structure to fill |
| * |
| * Parsers the @pichdr MPEG Video Picture Header structure members |
| * from video @packet |
| * |
| * Returns: %TRUE if the picture sequence could be parsed correctly, %FALSE |
| * otherwise. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_mpeg_video_packet_parse_picture_header (const GstMpegVideoPacket * packet, |
| GstMpegVideoPictureHdr * hdr) |
| { |
| GstBitReader br; |
| |
| if (packet->size < 4) |
| goto failed; |
| |
| gst_bit_reader_init (&br, &packet->data[packet->offset], packet->size); |
| |
| /* temperal sequence number */ |
| if (!gst_bit_reader_get_bits_uint16 (&br, &hdr->tsn, 10)) |
| goto failed; |
| |
| |
| /* frame type */ |
| if (!gst_bit_reader_get_bits_uint8 (&br, (guint8 *) & hdr->pic_type, 3)) |
| goto failed; |
| |
| |
| if (hdr->pic_type == 0 || hdr->pic_type > 4) |
| goto bad_pic_type; /* Corrupted picture packet */ |
| |
| /* VBV delay */ |
| if (!gst_bit_reader_get_bits_uint16 (&br, &hdr->vbv_delay, 16)) |
| goto failed; |
| |
| if (hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_P |
| || hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_B) { |
| |
| READ_UINT8 (&br, hdr->full_pel_forward_vector, 1); |
| |
| READ_UINT8 (&br, hdr->f_code[0][0], 3); |
| hdr->f_code[0][1] = hdr->f_code[0][0]; |
| } else { |
| hdr->full_pel_forward_vector = 0; |
| hdr->f_code[0][0] = hdr->f_code[0][1] = 0; |
| } |
| |
| if (hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_B) { |
| READ_UINT8 (&br, hdr->full_pel_backward_vector, 1); |
| |
| READ_UINT8 (&br, hdr->f_code[1][0], 3); |
| hdr->f_code[1][1] = hdr->f_code[1][0]; |
| } else { |
| hdr->full_pel_backward_vector = 0; |
| hdr->f_code[1][0] = hdr->f_code[1][1] = 0; |
| } |
| |
| return TRUE; |
| |
| bad_pic_type: |
| { |
| GST_WARNING ("Unsupported picture type : %d", hdr->pic_type); |
| return FALSE; |
| } |
| |
| failed: |
| { |
| GST_WARNING ("Not enough data to parse picture header"); |
| return FALSE; |
| } |
| } |
| |
| /** |
| * gst_mpeg_video_packet_parse_gop: |
| * @packet: The #GstMpegVideoPacket that carries the data |
| * @gop: (out): The #GstMpegVideoGop structure to fill |
| * |
| * Parses the @gop MPEG Video Group of Picture structure members from |
| * video @packet |
| * |
| * Returns: %TRUE if the gop could be parsed correctly, %FALSE otherwise. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_mpeg_video_packet_parse_gop (const GstMpegVideoPacket * packet, |
| GstMpegVideoGop * gop) |
| { |
| GstBitReader br; |
| |
| g_return_val_if_fail (gop != NULL, FALSE); |
| |
| if (packet->size < 4) |
| return FALSE; |
| |
| gst_bit_reader_init (&br, &packet->data[packet->offset], packet->size); |
| |
| READ_UINT8 (&br, gop->drop_frame_flag, 1); |
| |
| READ_UINT8 (&br, gop->hour, 5); |
| |
| READ_UINT8 (&br, gop->minute, 6); |
| |
| /* skip unused bit */ |
| if (!gst_bit_reader_skip (&br, 1)) |
| return FALSE; |
| |
| READ_UINT8 (&br, gop->second, 6); |
| |
| READ_UINT8 (&br, gop->frame, 6); |
| |
| READ_UINT8 (&br, gop->closed_gop, 1); |
| |
| READ_UINT8 (&br, gop->broken_link, 1); |
| |
| return TRUE; |
| |
| failed: |
| GST_WARNING ("error parsing \"GOP\""); |
| return FALSE; |
| } |
| |
| /** |
| * gst_mpeg_video_packet_parse_slice_header: |
| * @packet: The #GstMpegVideoPacket that carries the data |
| * @slice_hdr: (out): The #GstMpegVideoSliceHdr structure to fill |
| * @seqhdr: The #GstMpegVideoSequenceHdr header |
| * @seqscaleext: The #GstMpegVideoSequenceScalableExt header |
| * |
| * Parses the @GstMpegVideoSliceHdr structure members from @data |
| * |
| * Returns: %TRUE if the slice could be parsed correctly, %FALSE otherwise. |
| * |
| * Since: 1.2 |
| */ |
| gboolean |
| gst_mpeg_video_packet_parse_slice_header (const GstMpegVideoPacket * packet, |
| GstMpegVideoSliceHdr * slice_hdr, GstMpegVideoSequenceHdr * seqhdr, |
| GstMpegVideoSequenceScalableExt * seqscaleext) |
| { |
| GstBitReader br; |
| guint height; |
| guint mb_inc; |
| guint8 bits, extra_bits; |
| guint8 vertical_position, vertical_position_extension = 0; |
| |
| g_return_val_if_fail (seqhdr != NULL, FALSE); |
| |
| if (packet->size < 1) |
| return FALSE; |
| |
| gst_bit_reader_init (&br, &packet->data[packet->offset], packet->size); |
| |
| if (packet->type < GST_MPEG_VIDEO_PACKET_SLICE_MIN || |
| packet->type > GST_MPEG_VIDEO_PACKET_SLICE_MAX) { |
| GST_DEBUG ("Not parsing a slice"); |
| return FALSE; |
| } |
| vertical_position = packet->type - GST_MPEG_VIDEO_PACKET_SLICE_MIN; |
| |
| height = seqhdr->height; |
| if (height > 2800) |
| READ_UINT8 (&br, vertical_position_extension, 3); |
| |
| slice_hdr->vertical_position = packet->type; |
| slice_hdr->vertical_position_ext = vertical_position_extension; |
| |
| if (seqscaleext) |
| if (seqscaleext->scalable_mode == |
| GST_MPEG_VIDEO_SEQ_SCALABLE_MODE_DATA_PARTITIONING) |
| READ_UINT8 (&br, slice_hdr->priority_breakpoint, 7); |
| |
| READ_UINT8 (&br, slice_hdr->quantiser_scale_code, 5); |
| |
| READ_UINT8 (&br, slice_hdr->slice_ext_flag, 1); |
| if (!slice_hdr->slice_ext_flag) |
| slice_hdr->intra_slice = 0; |
| else { |
| READ_UINT8 (&br, slice_hdr->intra_slice, 1); |
| READ_UINT8 (&br, slice_hdr->slice_picture_id_enable, 1); |
| READ_UINT8 (&br, slice_hdr->slice_picture_id, 6); |
| |
| READ_UINT8 (&br, bits, 1); |
| while (bits) { |
| READ_UINT8 (&br, extra_bits, 8); |
| READ_UINT8 (&br, bits, 1); |
| } |
| } |
| |
| slice_hdr->header_size = gst_bit_reader_get_pos (&br); |
| |
| if (height > 2800) |
| slice_hdr->mb_row = (vertical_position_extension << 7) + vertical_position; |
| else |
| slice_hdr->mb_row = vertical_position; |
| |
| slice_hdr->mb_column = -1; |
| do { |
| if (!decode_vlc (&br, &mb_inc, mpeg2_mbaddr_vlc_table, |
| G_N_ELEMENTS (mpeg2_mbaddr_vlc_table))) { |
| GST_WARNING ("failed to decode first macroblock_address_increment"); |
| goto failed; |
| } |
| slice_hdr->mb_column += |
| mb_inc == GST_MPEG_VIDEO_MACROBLOCK_ESCAPE ? 33 : mb_inc; |
| } while (mb_inc == GST_MPEG_VIDEO_MACROBLOCK_ESCAPE); |
| |
| return TRUE; |
| |
| failed: |
| GST_WARNING ("error parsing \"Slice\""); |
| return FALSE; |
| } |
| |
| /** |
| * gst_mpeg_video_quant_matrix_get_raster_from_zigzag: |
| * @out_quant: (out): The resulting quantization matrix |
| * @quant: The source quantization matrix |
| * |
| * Converts quantization matrix @quant from zigzag scan order to |
| * raster scan order and store the resulting factors into @out_quant. |
| * |
| * Note: it is an error to pass the same table in both @quant and |
| * @out_quant arguments. |
| * |
| * Since: 1.2 |
| */ |
| void |
| gst_mpeg_video_quant_matrix_get_raster_from_zigzag (guint8 out_quant[64], |
| const guint8 quant[64]) |
| { |
| guint i; |
| |
| g_return_if_fail (out_quant != quant); |
| |
| for (i = 0; i < 64; i++) |
| out_quant[mpeg_zigzag_8x8[i]] = quant[i]; |
| } |
| |
| /** |
| * gst_mpeg_video_quant_matrix_get_zigzag_from_raster: |
| * @out_quant: (out): The resulting quantization matrix |
| * @quant: The source quantization matrix |
| * |
| * Converts quantization matrix @quant from raster scan order to |
| * zigzag scan order and store the resulting factors into @out_quant. |
| * |
| * Note: it is an error to pass the same table in both @quant and |
| * @out_quant arguments. |
| * |
| * Since: 1.2 |
| */ |
| void |
| gst_mpeg_video_quant_matrix_get_zigzag_from_raster (guint8 out_quant[64], |
| const guint8 quant[64]) |
| { |
| guint i; |
| |
| g_return_if_fail (out_quant != quant); |
| |
| for (i = 0; i < 64; i++) |
| out_quant[i] = quant[mpeg_zigzag_8x8[i]]; |
| } |