| /* gstvp9parser.c |
| * |
| * Copyright (C) 2013-2014 Intel Corporation |
| * Copyright (C) 2015 Intel Corporation |
| * Author: XuGuangxin<Guangxin.Xu@intel.com> |
| * Author: Sreerenj Balachandran<sreerenj.balachandran@intel.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:gstvp9parser |
| * @short_description: Convenience library for parsing vp9 video bitstream. |
| * |
| * For more details about the structures, you can refer to the |
| * specifications: |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <string.h> |
| #include <stdlib.h> |
| #include <gst/base/gstbitreader.h> |
| #include "vp9utils.h" |
| #include "gstvp9parser.h" |
| |
| #define MIN_TILE_WIDTH_B64 4 |
| #define MAX_TILE_WIDTH_B64 64 |
| |
| /* order of sb64, where sb64 = 64x64 */ |
| #define ALIGN_SB64(w) ((w + 63) >> 6) |
| |
| GST_DEBUG_CATEGORY (gst_vp9_parser_debug); |
| #define GST_CAT_DEFAULT gst_vp9_parser_debug |
| |
| static gboolean initialized = FALSE; |
| #define INITIALIZE_DEBUG_CATEGORY \ |
| if (!initialized) { \ |
| GST_DEBUG_CATEGORY_INIT (gst_vp9_parser_debug, "codecparsers_vp9", 0, \ |
| "vp9 parser library"); \ |
| initialized = TRUE; \ |
| } |
| |
| #define gst_vp9_read_bit(br) gst_bit_reader_get_bits_uint8_unchecked(br, 1) |
| #define gst_vp9_read_bits(br, bits) gst_bit_reader_get_bits_uint32_unchecked(br, bits) |
| |
| #define GST_VP9_PARSER_GET_PRIVATE(parser) ((GstVp9ParserPrivate *)(parser->priv)) |
| |
| typedef struct _ReferenceSize |
| { |
| guint32 width; |
| guint32 height; |
| } ReferenceSize; |
| |
| typedef struct |
| { |
| /* for loop filters */ |
| gint8 ref_deltas[GST_VP9_MAX_REF_LF_DELTAS]; |
| gint8 mode_deltas[GST_VP9_MAX_MODE_LF_DELTAS]; |
| |
| guint8 segmentation_abs_delta; |
| GstVp9SegmentationInfoData segmentation[GST_VP9_MAX_SEGMENTS]; |
| |
| ReferenceSize reference[GST_VP9_REF_FRAMES]; |
| } GstVp9ParserPrivate; |
| |
| static gint32 |
| gst_vp9_read_signed_bits (GstBitReader * br, int bits) |
| { |
| const gint32 value = gst_vp9_read_bits (br, bits); |
| return gst_vp9_read_bit (br) ? -value : value; |
| } |
| |
| static gboolean |
| verify_frame_marker (GstBitReader * br) |
| { |
| guint8 frame_marker = gst_vp9_read_bits (br, 2); |
| if (frame_marker != GST_VP9_FRAME_MARKER) { |
| GST_ERROR ("Invalid VP9 Frame Marker !"); |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| static gboolean |
| verify_sync_code (GstBitReader * const br) |
| { |
| return (gst_vp9_read_bits (br, 24) == GST_VP9_SYNC_CODE); |
| } |
| |
| static gboolean |
| parse_bitdepth_colorspace_sampling (GstVp9Parser * parser, |
| GstBitReader * const br, GstVp9FrameHdr * frame_hdr) |
| { |
| if (frame_hdr->profile > GST_VP9_PROFILE_1) |
| parser->bit_depth = |
| gst_vp9_read_bit (br) ? GST_VP9_BIT_DEPTH_12 : GST_VP9_BIT_DEPTH_10; |
| else |
| parser->bit_depth = GST_VP9_BIT_DEPTH_8; |
| |
| parser->color_space = gst_vp9_read_bits (br, 3); |
| if (parser->color_space != GST_VP9_CS_SRGB) { |
| parser->color_range = gst_vp9_read_bit (br); |
| |
| if (frame_hdr->profile == GST_VP9_PROFILE_1 |
| || frame_hdr->profile == GST_VP9_PROFILE_3) { |
| |
| parser->subsampling_x = gst_vp9_read_bit (br); |
| parser->subsampling_y = gst_vp9_read_bit (br); |
| |
| if (parser->subsampling_x == 1 && parser->subsampling_y == 1) { |
| GST_ERROR |
| ("4:2:0 subsampling is not supported in profile_1 or profile_3"); |
| goto error; |
| } |
| |
| if (gst_vp9_read_bit (br)) { |
| GST_ERROR ("Reserved bit set!"); |
| goto error; |
| } |
| } else { |
| parser->subsampling_y = parser->subsampling_x = 1; |
| } |
| } else { |
| parser->color_range = GST_VP9_CR_FULL; |
| |
| if (frame_hdr->profile == GST_VP9_PROFILE_1 |
| || frame_hdr->profile == GST_VP9_PROFILE_3) { |
| if (gst_vp9_read_bit (br)) { |
| GST_ERROR ("Reserved bit set!"); |
| goto error; |
| } |
| } else { |
| GST_ERROR |
| ("4:4:4 subsampling is not supported in profile_0 and profile_2"); |
| goto error; |
| } |
| } |
| return TRUE; |
| |
| error: |
| return FALSE; |
| } |
| |
| static guint |
| parse_profile (GstBitReader * br) |
| { |
| guint8 profile = gst_vp9_read_bit (br); |
| profile |= gst_vp9_read_bit (br) << 1; |
| if (profile > 2) |
| profile += gst_vp9_read_bit (br); |
| return profile; |
| } |
| |
| static void |
| parse_frame_size (GstBitReader * br, guint32 * width, guint32 * height) |
| { |
| const guint32 w = gst_vp9_read_bits (br, 16) + 1; |
| const guint32 h = gst_vp9_read_bits (br, 16) + 1; |
| *width = w; |
| *height = h; |
| } |
| |
| static void |
| parse_display_frame_size (GstBitReader * br, GstVp9FrameHdr * frame_hdr) |
| { |
| frame_hdr->display_size_enabled = gst_vp9_read_bit (br); |
| if (frame_hdr->display_size_enabled) |
| parse_frame_size (br, &frame_hdr->display_width, |
| &frame_hdr->display_height); |
| } |
| |
| static void |
| parse_frame_size_from_refs (const GstVp9Parser * parser, |
| GstVp9FrameHdr * frame_hdr, GstBitReader * br) |
| { |
| gboolean found = FALSE; |
| int i; |
| GstVp9ParserPrivate *priv = GST_VP9_PARSER_GET_PRIVATE (parser); |
| |
| for (i = 0; i < GST_VP9_REFS_PER_FRAME; i++) { |
| found = gst_vp9_read_bit (br); |
| |
| if (found) { |
| guint8 idx = frame_hdr->ref_frame_indices[i]; |
| frame_hdr->width = priv->reference[idx].width; |
| frame_hdr->height = priv->reference[idx].height; |
| break; |
| } |
| } |
| if (!found) |
| parse_frame_size (br, &frame_hdr->width, &frame_hdr->height); |
| } |
| |
| static GstVp9InterpolationFilter |
| parse_interp_filter (GstBitReader * br) |
| { |
| static const GstVp9InterpolationFilter filter_map[] = { |
| GST_VP9_INTERPOLATION_FILTER_EIGHTTAP_SMOOTH, |
| GST_VP9_INTERPOLATION_FILTER_EIGHTTAP, |
| GST_VP9_INTERPOLATION_FILTER_EIGHTTAP_SHARP, |
| GST_VP9_INTERPOLATION_FILTER_BILINEAR |
| }; |
| |
| return gst_vp9_read_bit (br) ? GST_VP9_INTERPOLATION_FILTER_SWITCHABLE : |
| filter_map[gst_vp9_read_bits (br, 2)]; |
| } |
| |
| static void |
| parse_loopfilter (GstVp9LoopFilter * lf, GstBitReader * br) |
| { |
| lf->filter_level = gst_vp9_read_bits (br, 6); |
| lf->sharpness_level = gst_vp9_read_bits (br, 3); |
| |
| lf->mode_ref_delta_update = 0; |
| |
| lf->mode_ref_delta_enabled = gst_vp9_read_bit (br); |
| if (lf->mode_ref_delta_enabled) { |
| lf->mode_ref_delta_update = gst_vp9_read_bit (br); |
| if (lf->mode_ref_delta_update) { |
| int i; |
| for (i = 0; i < GST_VP9_MAX_REF_LF_DELTAS; i++) { |
| lf->update_ref_deltas[i] = gst_vp9_read_bit (br); |
| if (lf->update_ref_deltas[i]) |
| lf->ref_deltas[i] = gst_vp9_read_signed_bits (br, 6); |
| } |
| |
| for (i = 0; i < GST_VP9_MAX_MODE_LF_DELTAS; i++) { |
| lf->update_mode_deltas[i] = gst_vp9_read_bit (br); |
| if (lf->update_mode_deltas[i]) |
| lf->mode_deltas[i] = gst_vp9_read_signed_bits (br, 6); |
| } |
| } |
| } |
| } |
| |
| static gint8 |
| parse_delta_q (GstBitReader * br) |
| { |
| return gst_vp9_read_bit (br) ? gst_vp9_read_signed_bits (br, 4) : 0; |
| } |
| |
| static void |
| parse_quantization (GstVp9QuantIndices * quant_indices, GstBitReader * br) |
| { |
| quant_indices->y_ac_qi = gst_vp9_read_bits (br, QINDEX_BITS); |
| quant_indices->y_dc_delta = parse_delta_q (br); |
| quant_indices->uv_dc_delta = parse_delta_q (br); |
| quant_indices->uv_ac_delta = parse_delta_q (br); |
| } |
| |
| static void |
| parse_segmentation (GstVp9SegmentationInfo * seg, GstBitReader * br) |
| { |
| int i; |
| |
| seg->update_map = FALSE; |
| seg->update_data = FALSE; |
| |
| seg->enabled = gst_vp9_read_bit (br); |
| if (!seg->enabled) |
| return; |
| |
| /* Segmentation map update */ |
| seg->update_map = gst_vp9_read_bit (br); |
| if (seg->update_map) { |
| for (i = 0; i < GST_VP9_SEG_TREE_PROBS; i++) { |
| seg->update_tree_probs[i] = gst_vp9_read_bit (br); |
| seg->tree_probs[i] = seg->update_tree_probs[i] ? |
| gst_vp9_read_bits (br, 8) : GST_VP9_MAX_PROB; |
| } |
| |
| seg->temporal_update = gst_vp9_read_bit (br); |
| if (seg->temporal_update) { |
| for (i = 0; i < GST_VP9_PREDICTION_PROBS; i++) { |
| seg->update_pred_probs[i] = gst_vp9_read_bit (br); |
| seg->pred_probs[i] = seg->update_pred_probs[i] ? |
| gst_vp9_read_bits (br, 8) : GST_VP9_MAX_PROB; |
| } |
| } else { |
| for (i = 0; i < GST_VP9_PREDICTION_PROBS; i++) |
| seg->pred_probs[i] = GST_VP9_MAX_PROB; |
| } |
| } |
| |
| /* Segmentation data update */ |
| seg->update_data = gst_vp9_read_bit (br); |
| |
| if (seg->update_data) { |
| /* clear all features */ |
| memset (seg->data, 0, sizeof (seg->data)); |
| |
| seg->abs_delta = gst_vp9_read_bit (br); |
| |
| for (i = 0; i < GST_VP9_MAX_SEGMENTS; i++) { |
| GstVp9SegmentationInfoData *seg_data = seg->data + i; |
| guint8 data; |
| |
| /* SEG_LVL_ALT_Q */ |
| seg_data->alternate_quantizer_enabled = gst_vp9_read_bit (br); |
| if (seg_data->alternate_quantizer_enabled) { |
| data = gst_vp9_read_bits (br, 8); |
| seg_data->alternate_quantizer = gst_vp9_read_bit (br) ? -data : data; |
| } |
| |
| /* SEG_LVL_ALT_LF */ |
| seg_data->alternate_loop_filter_enabled = gst_vp9_read_bit (br); |
| if (seg_data->alternate_loop_filter_enabled) { |
| data = gst_vp9_read_bits (br, 6); |
| seg_data->alternate_loop_filter = gst_vp9_read_bit (br) ? -data : data; |
| } |
| |
| /* SEG_LVL_REF_FRAME */ |
| seg_data->reference_frame_enabled = gst_vp9_read_bit (br); |
| if (seg_data->reference_frame_enabled) { |
| seg_data->reference_frame = gst_vp9_read_bits (br, 2); |
| } |
| |
| seg_data->reference_skip = gst_vp9_read_bit (br); |
| } |
| } |
| } |
| |
| static guint32 |
| get_max_lb_tile_cols (guint32 sb_cols) |
| { |
| gint max_log2 = 1; |
| while ((sb_cols >> max_log2) >= MIN_TILE_WIDTH_B64) |
| ++max_log2; |
| return max_log2 - 1; |
| } |
| |
| static guint32 |
| get_min_lb_tile_cols (guint32 sb_cols) |
| { |
| gint min_log2 = 0; |
| while ((MAX_TILE_WIDTH_B64 << min_log2) < sb_cols) |
| ++min_log2; |
| return min_log2; |
| } |
| |
| static gboolean |
| parse_tile_info (GstVp9FrameHdr * frame_hdr, GstBitReader * br) |
| { |
| guint32 max_ones; |
| const guint32 sb_cols = ALIGN_SB64 (frame_hdr->width); |
| guint32 min_lb_tile_cols = get_min_lb_tile_cols (sb_cols); |
| guint32 max_lb_tile_cols = get_max_lb_tile_cols (sb_cols); |
| |
| g_assert (min_lb_tile_cols <= max_lb_tile_cols); |
| max_ones = max_lb_tile_cols - min_lb_tile_cols; |
| |
| /* columns */ |
| frame_hdr->log2_tile_columns = min_lb_tile_cols; |
| while (max_ones-- && gst_vp9_read_bit (br)) |
| frame_hdr->log2_tile_columns++; |
| |
| if (frame_hdr->log2_tile_columns > 6) { |
| GST_ERROR ("Invalid number of tile columns..!"); |
| return FALSE; |
| } |
| |
| /* row */ |
| frame_hdr->log2_tile_rows = gst_vp9_read_bit (br); |
| if (frame_hdr->log2_tile_rows) |
| frame_hdr->log2_tile_rows += gst_vp9_read_bit (br); |
| |
| return TRUE; |
| } |
| |
| static void |
| loop_filter_update (GstVp9Parser * parser, const GstVp9LoopFilter * lf) |
| { |
| GstVp9ParserPrivate *priv = GST_VP9_PARSER_GET_PRIVATE (parser); |
| int i; |
| |
| for (i = 0; i < GST_VP9_MAX_REF_LF_DELTAS; i++) { |
| if (lf->update_ref_deltas[i]) |
| priv->ref_deltas[i] = lf->ref_deltas[i]; |
| } |
| |
| for (i = 0; i < GST_VP9_MAX_MODE_LF_DELTAS; i++) { |
| if (lf->update_mode_deltas[i]) |
| priv->mode_deltas[i] = lf->mode_deltas[i]; |
| } |
| } |
| |
| static guint8 |
| seg_get_base_qindex (const GstVp9Parser * parser, |
| const GstVp9FrameHdr * frame_hdr, int segid) |
| { |
| int seg_base = frame_hdr->quant_indices.y_ac_qi; |
| GstVp9ParserPrivate *priv = GST_VP9_PARSER_GET_PRIVATE (parser); |
| const GstVp9SegmentationInfoData *seg = priv->segmentation + segid; |
| /* DEBUG("id = %d, seg_base = %d, seg enable = %d, alt eanble = %d, abs = %d, alt= %d\n",segid, |
| seg_base, frame_hdr->segmentation.enabled, seg->alternate_quantizer_enabled, priv->segmentation_abs_delta, seg->alternate_quantizer); |
| */ |
| if (frame_hdr->segmentation.enabled && seg->alternate_quantizer_enabled) { |
| if (priv->segmentation_abs_delta) |
| seg_base = seg->alternate_quantizer; |
| else |
| seg_base += seg->alternate_quantizer; |
| } |
| return CLAMP (seg_base, 0, MAXQ); |
| } |
| |
| static guint8 |
| seg_get_filter_level (const GstVp9Parser * parser, |
| const GstVp9FrameHdr * frame_hdr, int segid) |
| { |
| int seg_filter = frame_hdr->loopfilter.filter_level; |
| GstVp9ParserPrivate *priv = GST_VP9_PARSER_GET_PRIVATE (parser); |
| const GstVp9SegmentationInfoData *seg = priv->segmentation + segid; |
| |
| if (frame_hdr->segmentation.enabled && seg->alternate_loop_filter_enabled) { |
| if (priv->segmentation_abs_delta) |
| seg_filter = seg->alternate_loop_filter; |
| else |
| seg_filter += seg->alternate_loop_filter; |
| } |
| return CLAMP (seg_filter, 0, GST_VP9_MAX_LOOP_FILTER); |
| } |
| |
| /*save segmentation info from frame header to parser*/ |
| static void |
| segmentation_save (GstVp9Parser * parser, const GstVp9FrameHdr * frame_hdr) |
| { |
| const GstVp9SegmentationInfo *info = &frame_hdr->segmentation; |
| if (!info->enabled) |
| return; |
| |
| if (info->update_map) { |
| g_assert (G_N_ELEMENTS (parser->mb_segment_tree_probs) == |
| G_N_ELEMENTS (info->tree_probs)); |
| g_assert (G_N_ELEMENTS (parser->segment_pred_probs) == |
| G_N_ELEMENTS (info->pred_probs)); |
| memcpy (parser->mb_segment_tree_probs, info->tree_probs, |
| sizeof (info->tree_probs)); |
| memcpy (parser->segment_pred_probs, info->pred_probs, |
| sizeof (info->pred_probs)); |
| } |
| |
| if (info->update_data) { |
| GstVp9ParserPrivate *priv = GST_VP9_PARSER_GET_PRIVATE (parser); |
| priv->segmentation_abs_delta = info->abs_delta; |
| g_assert (G_N_ELEMENTS (priv->segmentation) == G_N_ELEMENTS (info->data)); |
| memcpy (priv->segmentation, info->data, sizeof (info->data)); |
| } |
| } |
| |
| static void |
| segmentation_update (GstVp9Parser * parser, const GstVp9FrameHdr * frame_hdr) |
| { |
| int i = 0; |
| const GstVp9ParserPrivate *priv = GST_VP9_PARSER_GET_PRIVATE (parser); |
| const GstVp9LoopFilter *lf = &frame_hdr->loopfilter; |
| const GstVp9QuantIndices *quant_indices = &frame_hdr->quant_indices; |
| int default_filter = lf->filter_level; |
| const int scale = 1 << (default_filter >> 5); |
| |
| segmentation_save (parser, frame_hdr); |
| |
| for (i = 0; i < GST_VP9_MAX_SEGMENTS; i++) { |
| guint8 q = seg_get_base_qindex (parser, frame_hdr, i); |
| |
| GstVp9Segmentation *seg = parser->segmentation + i; |
| const GstVp9SegmentationInfoData *info = priv->segmentation + i; |
| |
| seg->luma_dc_quant_scale = |
| gst_vp9_dc_quant (q, quant_indices->y_dc_delta, parser->bit_depth); |
| seg->luma_ac_quant_scale = gst_vp9_ac_quant (q, 0, parser->bit_depth); |
| seg->chroma_dc_quant_scale = |
| gst_vp9_dc_quant (q, quant_indices->uv_dc_delta, parser->bit_depth); |
| seg->chroma_ac_quant_scale = |
| gst_vp9_ac_quant (q, quant_indices->uv_ac_delta, parser->bit_depth); |
| |
| if (lf->filter_level) { |
| guint8 filter = seg_get_filter_level (parser, frame_hdr, i); |
| |
| if (!lf->mode_ref_delta_enabled) { |
| memset (seg->filter_level, filter, sizeof (seg->filter_level)); |
| } else { |
| int ref, mode; |
| const int intra_filter = |
| filter + priv->ref_deltas[GST_VP9_REF_FRAME_INTRA] * scale; |
| seg->filter_level[GST_VP9_REF_FRAME_INTRA][0] = |
| CLAMP (intra_filter, 0, GST_VP9_MAX_LOOP_FILTER); |
| for (ref = GST_VP9_REF_FRAME_LAST; ref < GST_VP9_REF_FRAME_MAX; ++ref) { |
| for (mode = 0; mode < GST_VP9_MAX_MODE_LF_DELTAS; ++mode) { |
| const int inter_filter = filter + priv->ref_deltas[ref] * scale |
| + priv->mode_deltas[mode] * scale; |
| seg->filter_level[ref][mode] = |
| CLAMP (inter_filter, 0, GST_VP9_MAX_LOOP_FILTER); |
| } |
| } |
| } |
| } |
| seg->reference_frame_enabled = info->reference_frame_enabled;; |
| seg->reference_frame = info->reference_frame; |
| seg->reference_skip = info->reference_skip; |
| } |
| } |
| |
| static void |
| reference_update (GstVp9Parser * parser, const GstVp9FrameHdr * const frame_hdr) |
| { |
| guint8 flag = 1; |
| guint8 refresh_frame_flags; |
| int i; |
| GstVp9ParserPrivate *priv = GST_VP9_PARSER_GET_PRIVATE (parser); |
| ReferenceSize *reference = priv->reference; |
| if (frame_hdr->frame_type == GST_VP9_KEY_FRAME) { |
| refresh_frame_flags = 0xff; |
| } else { |
| refresh_frame_flags = frame_hdr->refresh_frame_flags; |
| } |
| for (i = 0; i < GST_VP9_REF_FRAMES; i++) { |
| if (refresh_frame_flags & flag) { |
| reference[i].width = frame_hdr->width; |
| reference[i].height = frame_hdr->height; |
| } |
| flag <<= 1; |
| } |
| } |
| |
| static inline int |
| frame_is_intra_only (const GstVp9FrameHdr * frame_hdr) |
| { |
| return frame_hdr->frame_type == GST_VP9_KEY_FRAME || frame_hdr->intra_only; |
| } |
| |
| static void |
| set_default_lf_deltas (GstVp9Parser * parser) |
| { |
| GstVp9ParserPrivate *priv = GST_VP9_PARSER_GET_PRIVATE (parser); |
| priv->ref_deltas[GST_VP9_REF_FRAME_INTRA] = 1; |
| priv->ref_deltas[GST_VP9_REF_FRAME_LAST] = 0; |
| priv->ref_deltas[GST_VP9_REF_FRAME_GOLDEN] = -1; |
| priv->ref_deltas[GST_VP9_REF_FRAME_ALTREF] = -1; |
| |
| priv->mode_deltas[0] = 0; |
| priv->mode_deltas[1] = 0; |
| } |
| |
| static void |
| set_default_segmentation_info (GstVp9Parser * parser) |
| { |
| GstVp9ParserPrivate *priv = GST_VP9_PARSER_GET_PRIVATE (parser); |
| |
| memset (priv->segmentation, 0, sizeof (priv->segmentation)); |
| |
| priv->segmentation_abs_delta = FALSE; |
| } |
| |
| static void |
| setup_past_independence (GstVp9Parser * parser, |
| GstVp9FrameHdr * const frame_hdr) |
| { |
| set_default_lf_deltas (parser); |
| set_default_segmentation_info (parser); |
| |
| memset (frame_hdr->ref_frame_sign_bias, 0, |
| sizeof (frame_hdr->ref_frame_sign_bias)); |
| } |
| |
| static void |
| gst_vp9_parser_reset (GstVp9Parser * parser) |
| { |
| GstVp9ParserPrivate *priv = parser->priv; |
| |
| parser->priv = NULL; |
| memset (parser->mb_segment_tree_probs, 0, |
| sizeof (parser->mb_segment_tree_probs)); |
| memset (parser->segment_pred_probs, 0, sizeof (parser->segment_pred_probs)); |
| memset (parser->segmentation, 0, sizeof (parser->segmentation)); |
| |
| memset (priv, 0, sizeof (GstVp9ParserPrivate)); |
| parser->priv = priv; |
| } |
| |
| static GstVp9ParserResult |
| gst_vp9_parser_update (GstVp9Parser * parser, GstVp9FrameHdr * const frame_hdr) |
| { |
| if (frame_hdr->frame_type == GST_VP9_KEY_FRAME) |
| gst_vp9_parser_reset (parser); |
| |
| if (frame_is_intra_only (frame_hdr) || frame_hdr->error_resilient_mode) |
| setup_past_independence (parser, frame_hdr); |
| |
| loop_filter_update (parser, &frame_hdr->loopfilter); |
| segmentation_update (parser, frame_hdr); |
| reference_update (parser, frame_hdr); |
| |
| return GST_VP9_PARSER_OK; |
| } |
| |
| |
| /******** API *************/ |
| |
| /** |
| * gst_vp9_parser_new: |
| * |
| * Creates a new #GstVp9Parser. It should be freed with |
| * gst_vp9_parser_free() after use. |
| * |
| * Returns: a new #GstVp9Parser |
| * |
| * Since: 1.8 |
| */ |
| GstVp9Parser * |
| gst_vp9_parser_new (void) |
| { |
| GstVp9Parser *parser; |
| GstVp9ParserPrivate *priv; |
| |
| INITIALIZE_DEBUG_CATEGORY; |
| GST_DEBUG ("Create VP9 Parser"); |
| |
| parser = g_slice_new0 (GstVp9Parser); |
| if (!parser) |
| return NULL; |
| |
| priv = g_slice_new0 (GstVp9ParserPrivate); |
| if (!priv) |
| return NULL; |
| |
| parser->priv = priv; |
| |
| return parser; |
| } |
| |
| /** |
| * gst_vp9_parser_free: |
| * @parser: the #GstVp9Parser to free |
| * |
| * Frees @parser. |
| * |
| * Since: 1.8 |
| */ |
| void |
| gst_vp9_parser_free (GstVp9Parser * parser) |
| { |
| if (parser) { |
| if (parser->priv) { |
| g_slice_free (GstVp9ParserPrivate, parser->priv); |
| parser->priv = NULL; |
| } |
| g_slice_free (GstVp9Parser, parser); |
| } |
| } |
| |
| /** |
| * gst_vp9_parser_parse_frame_header: |
| * @parser: The #GstVp9Parser |
| * @frame_hdr: The #GstVp9FrameHdr to fill |
| * @data: The data to parse |
| * @size: The size of the @data to parse |
| * |
| * Parses the VP9 bitstream contained in @data, and fills in @frame_hdr |
| * with the information. The @size argument represent the whole frame size. |
| * |
| * Returns: a #GstVp9ParserResult |
| * |
| * Since: 1.8 |
| */ |
| GstVp9ParserResult |
| gst_vp9_parser_parse_frame_header (GstVp9Parser * parser, |
| GstVp9FrameHdr * frame_hdr, const guint8 * data, gsize size) |
| { |
| GstBitReader bit_reader; |
| GstBitReader *br = &bit_reader; |
| |
| gst_bit_reader_init (br, data, size); |
| memset (frame_hdr, 0, sizeof (*frame_hdr)); |
| |
| /* Parsing Uncompressed Data Chunk */ |
| |
| if (!verify_frame_marker (br)) |
| goto error; |
| |
| frame_hdr->profile = parse_profile (br); |
| if (frame_hdr->profile > GST_VP9_PROFILE_UNDEFINED) { |
| GST_ERROR ("Stream has undefined VP9 profile !"); |
| goto error; |
| } |
| |
| frame_hdr->show_existing_frame = gst_vp9_read_bit (br); |
| if (frame_hdr->show_existing_frame) { |
| frame_hdr->frame_to_show = gst_vp9_read_bits (br, GST_VP9_REF_FRAMES_LOG2); |
| return GST_VP9_PARSER_OK; |
| } |
| |
| frame_hdr->frame_type = gst_vp9_read_bit (br); |
| frame_hdr->show_frame = gst_vp9_read_bit (br); |
| frame_hdr->error_resilient_mode = gst_vp9_read_bit (br); |
| |
| if (frame_hdr->frame_type == GST_VP9_KEY_FRAME) { |
| |
| if (!verify_sync_code (br)) { |
| GST_ERROR ("Invalid VP9 Key-frame sync code !"); |
| goto error; |
| } |
| |
| if (!parse_bitdepth_colorspace_sampling (parser, br, frame_hdr)) { |
| GST_ERROR ("Failed to parse color_space/bit_depth info !"); |
| goto error; |
| } |
| |
| parse_frame_size (br, &frame_hdr->width, &frame_hdr->height); |
| |
| parse_display_frame_size (br, frame_hdr); |
| |
| } else { |
| frame_hdr->intra_only = frame_hdr->show_frame ? 0 : gst_vp9_read_bit (br); |
| frame_hdr->reset_frame_context = frame_hdr->error_resilient_mode ? |
| 0 : gst_vp9_read_bits (br, 2); |
| |
| if (frame_hdr->intra_only) { |
| |
| if (!verify_sync_code (br)) { |
| GST_ERROR ("Invalid VP9 sync code in intra-only frame !"); |
| goto error; |
| } |
| |
| if (frame_hdr->profile > GST_VP9_PROFILE_0) { |
| if (!parse_bitdepth_colorspace_sampling (parser, br, frame_hdr)) { |
| GST_ERROR ("Failed to parse color_space/bit_depth info !"); |
| goto error; |
| } |
| } else { |
| parser->color_space = GST_VP9_CS_BT_601; |
| parser->color_range = GST_VP9_CR_LIMITED; |
| parser->subsampling_y = parser->subsampling_x = 1; |
| parser->bit_depth = GST_VP9_BIT_DEPTH_8; |
| } |
| |
| frame_hdr->refresh_frame_flags = |
| gst_vp9_read_bits (br, GST_VP9_REF_FRAMES); |
| parse_frame_size (br, &frame_hdr->width, &frame_hdr->height); |
| parse_display_frame_size (br, frame_hdr); |
| |
| } else { |
| int i; |
| frame_hdr->refresh_frame_flags = |
| gst_vp9_read_bits (br, GST_VP9_REF_FRAMES); |
| |
| for (i = 0; i < GST_VP9_REFS_PER_FRAME; i++) { |
| frame_hdr->ref_frame_indices[i] = |
| gst_vp9_read_bits (br, GST_VP9_REF_FRAMES_LOG2); |
| frame_hdr->ref_frame_sign_bias[i] = gst_vp9_read_bit (br); |
| } |
| |
| parse_frame_size_from_refs (parser, frame_hdr, br); |
| parse_display_frame_size (br, frame_hdr); |
| |
| frame_hdr->allow_high_precision_mv = gst_vp9_read_bit (br); |
| frame_hdr->mcomp_filter_type = parse_interp_filter (br); |
| } |
| } |
| |
| frame_hdr->refresh_frame_context = |
| frame_hdr->error_resilient_mode ? 0 : gst_vp9_read_bit (br); |
| frame_hdr->frame_parallel_decoding_mode = |
| frame_hdr->error_resilient_mode ? 1 : gst_vp9_read_bit (br); |
| frame_hdr->frame_context_idx = |
| gst_vp9_read_bits (br, GST_VP9_FRAME_CONTEXTS_LOG2); |
| |
| /* loopfilter header */ |
| parse_loopfilter (&frame_hdr->loopfilter, br); |
| |
| /* quantization header */ |
| parse_quantization (&frame_hdr->quant_indices, br); |
| /* set lossless_flag */ |
| frame_hdr->lossless_flag = frame_hdr->quant_indices.y_ac_qi == 0 && |
| frame_hdr->quant_indices.y_dc_delta == 0 && |
| frame_hdr->quant_indices.uv_dc_delta == 0 |
| && frame_hdr->quant_indices.uv_ac_delta == 0; |
| |
| /* segmentation header */ |
| parse_segmentation (&frame_hdr->segmentation, br); |
| |
| /* tile header */ |
| if (!parse_tile_info (frame_hdr, br)) { |
| GST_ERROR ("Failed to parse tile info...!"); |
| goto error; |
| } |
| |
| /* size of the rest of the header */ |
| frame_hdr->first_partition_size = gst_vp9_read_bits (br, 16); |
| if (!frame_hdr->first_partition_size) { |
| GST_ERROR ("Failed to parse the first partition size...!"); |
| goto error; |
| } |
| |
| frame_hdr->frame_header_length_in_bytes = |
| (gst_bit_reader_get_pos (br) + 7) / 8; |
| return gst_vp9_parser_update (parser, frame_hdr); |
| |
| error: |
| return GST_VP9_PARSER_ERROR; |
| } |