gstreamer/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth265parser.c
Aleksandr Slobodeniuk a98330636a h264/h265parser: clarify some gtk-doc annotations
Needed for the documentation since the transfer
is not obvious.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5287>
2023-09-08 13:41:01 +00:00

5133 lines
159 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Gstreamer H.265 bitstream parser
* Copyright (C) 2012 Intel Corporation
* Copyright (C) 2013 Sreerenj Balachandran <sreerenj.balachandran@intel.com>
*
* Contact: 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:gsth265parser
* @title: GstH265Parser
* @short_description: Convenience library for h265 video bitstream parsing.
*
* It offers you bitstream parsing in HEVC mode and non-HEVC mode. To identify
* Nals in a bitstream and parse its headers, you should call:
*
* * gst_h265_parser_identify_nalu() to identify the following nalu in
* non-HEVC bitstreams
*
* * gst_h265_parser_identify_nalu_hevc() to identify the nalu in
* HEVC bitstreams
*
* Then, depending on the #GstH265NalUnitType of the newly parsed #GstH265NalUnit,
* you should call the differents functions to parse the structure:
*
* * From #GST_H265_NAL_SLICE_TRAIL_N to #GST_H265_NAL_SLICE_CRA_NUT: gst_h265_parser_parse_slice_hdr()
*
* * `GST_H265_NAL_*_SEI`: gst_h265_parser_parse_sei()
*
* * #GST_H265_NAL_VPS: gst_h265_parser_parse_vps()
*
* * #GST_H265_NAL_SPS: gst_h265_parser_parse_sps()
*
* * #GST_H265_NAL_PPS: #gst_h265_parser_parse_pps()
*
* * Any other: gst_h265_parser_parse_nal()
*
* Note: You should always call gst_h265_parser_parse_nal() if you don't
* actually need #GstH265NalUnitType to be parsed for your personal use, in
* order to guarantee that the #GstH265Parser is always up to date.
*
* For more details about the structures, look at the ITU-T H.265
* specifications, you can download them from:
*
* * ITU-T H.265: http://www.itu.int/rec/T-REC-H.265
*
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "nalutils.h"
#include "gsth265parser.h"
#include <gst/base/gstbytereader.h>
#include <gst/base/gstbitreader.h>
#include <string.h>
#include <math.h>
#ifndef GST_DISABLE_GST_DEBUG
#define GST_CAT_DEFAULT gst_h265_debug_category_get()
static GstDebugCategory *
gst_h265_debug_category_get (void)
{
static gsize cat_gonce = 0;
if (g_once_init_enter (&cat_gonce)) {
GstDebugCategory *cat = NULL;
GST_DEBUG_CATEGORY_INIT (cat, "codecparsers_h265", 0, "h265 parse library");
g_once_init_leave (&cat_gonce, (gsize) cat);
}
return (GstDebugCategory *) cat_gonce;
}
#endif /* GST_DISABLE_GST_DEBUG */
/**** Default scaling_lists according to Table 7-5 and 7-6 *****/
/* Table 7-5 */
static const guint8 default_scaling_list0[16] = {
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16
};
/* Combined the values in Table 7-6 to make the calculation easier
* Default scaling list of 8x8 and 16x16 matrices for matrixId = 0, 1 and 2
* Default scaling list of 32x32 matrix for matrixId = 0
*/
static const guint8 default_scaling_list1[64] = {
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16,
17, 16, 17, 18, 17, 18, 18, 17, 18, 21, 19, 20,
21, 20, 19, 21, 24, 22, 22, 24, 24, 22, 22, 24,
25, 25, 27, 30, 27, 25, 25, 29, 31, 35, 35, 31,
29, 36, 41, 44, 41, 36, 47, 54, 54, 47, 65, 70,
65, 88, 88, 115
};
/* Combined the values in Table 7-6 to make the calculation easier
* Default scaling list of 8x8 and 16x16 matrices for matrixId = 3, 4 and 5
* Default scaling list of 32x32 matrix for matrixId = 1
*/
static const guint8 default_scaling_list2[64] = {
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17,
17, 17, 17, 18, 18, 18, 18, 18, 18, 20, 20, 20,
20, 20, 20, 20, 24, 24, 24, 24, 24, 24, 24, 24,
25, 25, 25, 25, 25, 25, 25, 28, 28, 28, 28, 28,
28, 33, 33, 33, 33, 33, 41, 41, 41, 41, 54, 54,
54, 71, 71, 91
};
static const guint8 zigzag_4x4[16] = {
0, 1, 4, 8,
5, 2, 3, 6,
9, 12, 13, 10,
7, 11, 14, 15,
};
static const guint8 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
};
static const guint8 uprightdiagonal_4x4[16] = {
0, 4, 1, 8,
5, 2, 12, 9,
6, 3, 13, 10,
7, 14, 11, 15
};
static const guint8 uprightdiagonal_8x8[64] = {
0, 8, 1, 16, 9, 2, 24, 17,
10, 3, 32, 25, 18, 11, 4, 40,
33, 26, 19, 12, 5, 48, 41, 34,
27, 20, 13, 6, 56, 49, 42, 35,
28, 21, 14, 7, 57, 50, 43, 36,
29, 22, 15, 58, 51, 44, 37, 30,
23, 59, 52, 45, 38, 31, 60, 53,
46, 39, 61, 54, 47, 62, 55, 63
};
typedef struct
{
guint par_n, par_d;
} PAR;
/* Table E-1 - Meaning of sample aspect ratio indicator (1..16) */
static const PAR aspect_ratios[17] = {
{0, 0},
{1, 1},
{12, 11},
{10, 11},
{16, 11},
{40, 33},
{24, 11},
{20, 11},
{32, 11},
{80, 33},
{18, 11},
{15, 11},
{64, 33},
{160, 99},
{4, 3},
{3, 2},
{2, 1}
};
/***** Utils ****/
#define EXTENDED_SAR 255
static GstH265VPS *
gst_h265_parser_get_vps (GstH265Parser * parser, guint8 vps_id)
{
GstH265VPS *vps;
vps = &parser->vps[vps_id];
if (vps->valid)
return vps;
return NULL;
}
static GstH265SPS *
gst_h265_parser_get_sps (GstH265Parser * parser, guint8 sps_id)
{
GstH265SPS *sps;
sps = &parser->sps[sps_id];
if (sps->valid)
return sps;
return NULL;
}
static GstH265PPS *
gst_h265_parser_get_pps (GstH265Parser * parser, guint8 pps_id)
{
GstH265PPS *pps;
pps = &parser->pps[pps_id];
if (pps->valid)
return pps;
return NULL;
}
static gboolean
gst_h265_parse_nalu_header (GstH265NalUnit * nalu)
{
guint8 *data = nalu->data + nalu->offset;
GstBitReader br;
if (nalu->size < 2)
return FALSE;
gst_bit_reader_init (&br, data, nalu->size - nalu->offset);
/* skip the forbidden_zero_bit */
gst_bit_reader_skip_unchecked (&br, 1);
nalu->type = gst_bit_reader_get_bits_uint8_unchecked (&br, 6);
nalu->layer_id = gst_bit_reader_get_bits_uint8_unchecked (&br, 6);
nalu->temporal_id_plus1 = gst_bit_reader_get_bits_uint8_unchecked (&br, 3);
nalu->header_bytes = 2;
return TRUE;
}
struct h265_profile_string
{
GstH265Profile profile;
const gchar *name;
};
static const struct h265_profile_string h265_profiles[] = {
/* keep in sync with definition in the header */
{GST_H265_PROFILE_MAIN, "main"},
{GST_H265_PROFILE_MAIN_10, "main-10"},
{GST_H265_PROFILE_MAIN_STILL_PICTURE, "main-still-picture"},
{GST_H265_PROFILE_MONOCHROME, "monochrome"},
{GST_H265_PROFILE_MONOCHROME_12, "monochrome-12"},
{GST_H265_PROFILE_MONOCHROME_16, "monochrome-16"},
{GST_H265_PROFILE_MAIN_12, "main-12"},
{GST_H265_PROFILE_MAIN_422_10, "main-422-10"},
{GST_H265_PROFILE_MAIN_422_12, "main-422-12"},
{GST_H265_PROFILE_MAIN_444, "main-444"},
{GST_H265_PROFILE_MAIN_444_10, "main-444-10"},
{GST_H265_PROFILE_MAIN_444_12, "main-444-12"},
{GST_H265_PROFILE_MAIN_INTRA, "main-intra"},
{GST_H265_PROFILE_MAIN_10_INTRA, "main-10-intra"},
{GST_H265_PROFILE_MAIN_12_INTRA, "main-12-intra"},
{GST_H265_PROFILE_MAIN_422_10_INTRA, "main-422-10-intra"},
{GST_H265_PROFILE_MAIN_422_12_INTRA, "main-422-12-intra"},
{GST_H265_PROFILE_MAIN_444_INTRA, "main-444-intra"},
{GST_H265_PROFILE_MAIN_444_10_INTRA, "main-444-10-intra"},
{GST_H265_PROFILE_MAIN_444_12_INTRA, "main-444-12-intra"},
{GST_H265_PROFILE_MAIN_444_16_INTRA, "main-444-16-intra"},
{GST_H265_PROFILE_MAIN_444_STILL_PICTURE, "main-444-still-picture"},
{GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE, "main-444-16-still-picture"},
{GST_H265_PROFILE_MONOCHROME_10, "monochrome-10"},
{GST_H265_PROFILE_HIGH_THROUGHPUT_444, "high-throughput-444"},
{GST_H265_PROFILE_HIGH_THROUGHPUT_444_10, "high-throughput-444-10"},
{GST_H265_PROFILE_HIGH_THROUGHPUT_444_14, "high-throughput-444-14"},
{GST_H265_PROFILE_HIGH_THROUGHPUT_444_16_INTRA,
"high-throughput-444-16-intra"},
{GST_H265_PROFILE_SCREEN_EXTENDED_MAIN, "screen-extended-main"},
{GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_10, "screen-extended-main-10"},
{GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444, "screen-extended-main-444"},
{GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444_10, "screen-extended-main-444-10"},
{GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444,
"screen-extended-high-throughput-444"},
{GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_10,
"screen-extended-high-throughput-444-10"},
{GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_14,
"screen-extended-high-throughput-444-14"},
{GST_H265_PROFILE_MULTIVIEW_MAIN, "multiview-main"},
{GST_H265_PROFILE_SCALABLE_MAIN, "scalable-main"},
{GST_H265_PROFILE_SCALABLE_MAIN_10, "scalable-main-10"},
{GST_H265_PROFILE_SCALABLE_MONOCHROME, "scalable-monochrome"},
{GST_H265_PROFILE_SCALABLE_MONOCHROME_12, "scalable-monochrome-12"},
{GST_H265_PROFILE_SCALABLE_MONOCHROME_16, "scalable-monochrome-16"},
{GST_H265_PROFILE_SCALABLE_MAIN_444, "scalable-main-444"},
{GST_H265_PROFILE_3D_MAIN, "3d-main"},
};
/****** Parsing functions *****/
static gboolean
gst_h265_parse_profile_tier_level (GstH265ProfileTierLevel * ptl,
NalReader * nr, guint8 maxNumSubLayersMinus1)
{
guint i, j;
GST_DEBUG ("parsing \"ProfileTierLevel parameters\"");
READ_UINT8 (nr, ptl->profile_space, 2);
READ_UINT8 (nr, ptl->tier_flag, 1);
READ_UINT8 (nr, ptl->profile_idc, 5);
for (j = 0; j < 32; j++)
READ_UINT8 (nr, ptl->profile_compatibility_flag[j], 1);
READ_UINT8 (nr, ptl->progressive_source_flag, 1);
READ_UINT8 (nr, ptl->interlaced_source_flag, 1);
READ_UINT8 (nr, ptl->non_packed_constraint_flag, 1);
READ_UINT8 (nr, ptl->frame_only_constraint_flag, 1);
READ_UINT8 (nr, ptl->max_12bit_constraint_flag, 1);
READ_UINT8 (nr, ptl->max_10bit_constraint_flag, 1);
READ_UINT8 (nr, ptl->max_8bit_constraint_flag, 1);
READ_UINT8 (nr, ptl->max_422chroma_constraint_flag, 1);
READ_UINT8 (nr, ptl->max_420chroma_constraint_flag, 1);
READ_UINT8 (nr, ptl->max_monochrome_constraint_flag, 1);
READ_UINT8 (nr, ptl->intra_constraint_flag, 1);
READ_UINT8 (nr, ptl->one_picture_only_constraint_flag, 1);
READ_UINT8 (nr, ptl->lower_bit_rate_constraint_flag, 1);
READ_UINT8 (nr, ptl->max_14bit_constraint_flag, 1);
/* skip the reserved zero bits */
if (!nal_reader_skip (nr, 34))
goto error;
READ_UINT8 (nr, ptl->level_idc, 8);
for (j = 0; j < maxNumSubLayersMinus1; j++) {
READ_UINT8 (nr, ptl->sub_layer_profile_present_flag[j], 1);
READ_UINT8 (nr, ptl->sub_layer_level_present_flag[j], 1);
}
if (maxNumSubLayersMinus1 > 0) {
for (i = maxNumSubLayersMinus1; i < 8; i++)
if (!nal_reader_skip (nr, 2))
goto error;
}
for (i = 0; i < maxNumSubLayersMinus1; i++) {
if (ptl->sub_layer_profile_present_flag[i]) {
READ_UINT8 (nr, ptl->sub_layer_profile_space[i], 2);
READ_UINT8 (nr, ptl->sub_layer_tier_flag[i], 1);
READ_UINT8 (nr, ptl->sub_layer_profile_idc[i], 5);
for (j = 0; j < 32; j++)
READ_UINT8 (nr, ptl->sub_layer_profile_compatibility_flag[i][j], 1);
READ_UINT8 (nr, ptl->sub_layer_progressive_source_flag[i], 1);
READ_UINT8 (nr, ptl->sub_layer_interlaced_source_flag[i], 1);
READ_UINT8 (nr, ptl->sub_layer_non_packed_constraint_flag[i], 1);
READ_UINT8 (nr, ptl->sub_layer_frame_only_constraint_flag[i], 1);
if (!nal_reader_skip (nr, 44))
goto error;
}
if (ptl->sub_layer_level_present_flag[i])
READ_UINT8 (nr, ptl->sub_layer_level_idc[i], 8);
}
return TRUE;
error:
GST_WARNING ("error parsing \"ProfileTierLevel Parameters\"");
return FALSE;
}
static gboolean
gst_h265_parse_sub_layer_hrd_parameters (GstH265SubLayerHRDParams * sub_hrd,
NalReader * nr, guint8 CpbCnt, guint8 sub_pic_hrd_params_present_flag)
{
guint i;
GST_DEBUG ("parsing \"SubLayer HRD Parameters\"");
for (i = 0; i <= CpbCnt; i++) {
READ_UE_MAX (nr, sub_hrd->bit_rate_value_minus1[i], G_MAXUINT32 - 1);
READ_UE_MAX (nr, sub_hrd->cpb_size_value_minus1[i], G_MAXUINT32 - 1);
if (sub_pic_hrd_params_present_flag) {
READ_UE_MAX (nr, sub_hrd->cpb_size_du_value_minus1[i], G_MAXUINT32 - 1);
READ_UE_MAX (nr, sub_hrd->bit_rate_du_value_minus1[i], G_MAXUINT32 - 1);
}
READ_UINT8 (nr, sub_hrd->cbr_flag[i], 1);
}
return TRUE;
error:
GST_WARNING ("error parsing \"SubLayerHRD Parameters \"");
return FALSE;
}
static gboolean
gst_h265_parse_hrd_parameters (GstH265HRDParams * hrd, NalReader * nr,
guint8 commonInfPresentFlag, guint8 maxNumSubLayersMinus1)
{
guint i;
GST_DEBUG ("parsing \"HRD Parameters\"");
/* set default values for fields that might not be present in the bitstream
and have valid defaults */
hrd->initial_cpb_removal_delay_length_minus1 = 23;
hrd->au_cpb_removal_delay_length_minus1 = 23;
hrd->dpb_output_delay_length_minus1 = 23;
if (commonInfPresentFlag) {
READ_UINT8 (nr, hrd->nal_hrd_parameters_present_flag, 1);
READ_UINT8 (nr, hrd->vcl_hrd_parameters_present_flag, 1);
if (hrd->nal_hrd_parameters_present_flag
|| hrd->vcl_hrd_parameters_present_flag) {
READ_UINT8 (nr, hrd->sub_pic_hrd_params_present_flag, 1);
if (hrd->sub_pic_hrd_params_present_flag) {
READ_UINT8 (nr, hrd->tick_divisor_minus2, 8);
READ_UINT8 (nr, hrd->du_cpb_removal_delay_increment_length_minus1, 5);
READ_UINT8 (nr, hrd->sub_pic_cpb_params_in_pic_timing_sei_flag, 1);
READ_UINT8 (nr, hrd->dpb_output_delay_du_length_minus1, 5);
}
READ_UINT8 (nr, hrd->bit_rate_scale, 4);
READ_UINT8 (nr, hrd->cpb_size_scale, 4);
if (hrd->sub_pic_hrd_params_present_flag)
READ_UINT8 (nr, hrd->cpb_size_du_scale, 4);
READ_UINT8 (nr, hrd->initial_cpb_removal_delay_length_minus1, 5);
READ_UINT8 (nr, hrd->au_cpb_removal_delay_length_minus1, 5);
READ_UINT8 (nr, hrd->dpb_output_delay_length_minus1, 5);
}
}
for (i = 0; i <= maxNumSubLayersMinus1; i++) {
READ_UINT8 (nr, hrd->fixed_pic_rate_general_flag[i], 1);
if (!hrd->fixed_pic_rate_general_flag[i]) {
READ_UINT8 (nr, hrd->fixed_pic_rate_within_cvs_flag[i], 1);
} else
hrd->fixed_pic_rate_within_cvs_flag[i] = 1;
if (hrd->fixed_pic_rate_within_cvs_flag[i]) {
READ_UE_MAX (nr, hrd->elemental_duration_in_tc_minus1[i], 2047);
} else
READ_UINT8 (nr, hrd->low_delay_hrd_flag[i], 1);
if (!hrd->low_delay_hrd_flag[i])
READ_UE_MAX (nr, hrd->cpb_cnt_minus1[i], 31);
if (hrd->nal_hrd_parameters_present_flag)
if (!gst_h265_parse_sub_layer_hrd_parameters (&hrd->sublayer_hrd_params
[i], nr, hrd->cpb_cnt_minus1[i],
hrd->sub_pic_hrd_params_present_flag))
goto error;
if (hrd->vcl_hrd_parameters_present_flag)
if (!gst_h265_parse_sub_layer_hrd_parameters (&hrd->sublayer_hrd_params
[i], nr, hrd->cpb_cnt_minus1[i],
hrd->sub_pic_hrd_params_present_flag))
goto error;
}
return TRUE;
error:
GST_WARNING ("error parsing \"HRD Parameters\"");
return FALSE;
}
static gboolean
gst_h265_parse_vui_parameters (GstH265SPS * sps, NalReader * nr)
{
GstH265VUIParams *vui = &sps->vui_params;
GST_DEBUG ("parsing \"VUI Parameters\"");
/* set default values for fields that might not be present in the bitstream
and have valid defaults */
vui->video_format = 5;
vui->colour_primaries = 2;
vui->transfer_characteristics = 2;
vui->matrix_coefficients = 2;
vui->motion_vectors_over_pic_boundaries_flag = 1;
vui->max_bytes_per_pic_denom = 2;
vui->max_bits_per_min_cu_denom = 1;
vui->log2_max_mv_length_horizontal = 15;
vui->log2_max_mv_length_vertical = 15;
if (sps && sps->profile_tier_level.progressive_source_flag
&& sps->profile_tier_level.interlaced_source_flag)
vui->frame_field_info_present_flag = 1;
READ_UINT8 (nr, vui->aspect_ratio_info_present_flag, 1);
if (vui->aspect_ratio_info_present_flag) {
READ_UINT8 (nr, vui->aspect_ratio_idc, 8);
if (vui->aspect_ratio_idc == EXTENDED_SAR) {
READ_UINT16 (nr, vui->sar_width, 16);
READ_UINT16 (nr, vui->sar_height, 16);
vui->par_n = vui->sar_width;
vui->par_d = vui->sar_height;
} else if (vui->aspect_ratio_idc <= 16) {
vui->par_n = aspect_ratios[vui->aspect_ratio_idc].par_n;
vui->par_d = aspect_ratios[vui->aspect_ratio_idc].par_d;
}
}
READ_UINT8 (nr, vui->overscan_info_present_flag, 1);
if (vui->overscan_info_present_flag)
READ_UINT8 (nr, vui->overscan_appropriate_flag, 1);
READ_UINT8 (nr, vui->video_signal_type_present_flag, 1);
if (vui->video_signal_type_present_flag) {
READ_UINT8 (nr, vui->video_format, 3);
READ_UINT8 (nr, vui->video_full_range_flag, 1);
READ_UINT8 (nr, vui->colour_description_present_flag, 1);
if (vui->colour_description_present_flag) {
READ_UINT8 (nr, vui->colour_primaries, 8);
READ_UINT8 (nr, vui->transfer_characteristics, 8);
READ_UINT8 (nr, vui->matrix_coefficients, 8);
}
}
READ_UINT8 (nr, vui->chroma_loc_info_present_flag, 1);
if (vui->chroma_loc_info_present_flag) {
READ_UE_MAX (nr, vui->chroma_sample_loc_type_top_field, 5);
READ_UE_MAX (nr, vui->chroma_sample_loc_type_bottom_field, 5);
}
READ_UINT8 (nr, vui->neutral_chroma_indication_flag, 1);
READ_UINT8 (nr, vui->field_seq_flag, 1);
READ_UINT8 (nr, vui->frame_field_info_present_flag, 1);
READ_UINT8 (nr, vui->default_display_window_flag, 1);
if (vui->default_display_window_flag) {
READ_UE (nr, vui->def_disp_win_left_offset);
READ_UE (nr, vui->def_disp_win_right_offset);
READ_UE (nr, vui->def_disp_win_top_offset);
READ_UE (nr, vui->def_disp_win_bottom_offset);
}
READ_UINT8 (nr, vui->timing_info_present_flag, 1);
if (vui->timing_info_present_flag) {
READ_UINT32 (nr, vui->num_units_in_tick, 32);
if (vui->num_units_in_tick == 0)
GST_WARNING ("num_units_in_tick = 0 detected in stream "
"(incompliant to H.265 E.2.1).");
READ_UINT32 (nr, vui->time_scale, 32);
if (vui->time_scale == 0)
GST_WARNING ("time_scale = 0 detected in stream "
"(incompliant to H.265 E.2.1).");
READ_UINT8 (nr, vui->poc_proportional_to_timing_flag, 1);
if (vui->poc_proportional_to_timing_flag)
READ_UE_MAX (nr, vui->num_ticks_poc_diff_one_minus1, G_MAXUINT32 - 1);
READ_UINT8 (nr, vui->hrd_parameters_present_flag, 1);
if (vui->hrd_parameters_present_flag)
if (!gst_h265_parse_hrd_parameters (&vui->hrd_params, nr, 1,
sps->max_sub_layers_minus1))
goto error;
}
READ_UINT8 (nr, vui->bitstream_restriction_flag, 1);
if (vui->bitstream_restriction_flag) {
READ_UINT8 (nr, vui->tiles_fixed_structure_flag, 1);
READ_UINT8 (nr, vui->motion_vectors_over_pic_boundaries_flag, 1);
READ_UINT8 (nr, vui->restricted_ref_pic_lists_flag, 1);
READ_UE_MAX (nr, vui->min_spatial_segmentation_idc, 4096);
READ_UE_MAX (nr, vui->max_bytes_per_pic_denom, 16);
READ_UE_MAX (nr, vui->max_bits_per_min_cu_denom, 16);
READ_UE_MAX (nr, vui->log2_max_mv_length_horizontal, 16);
READ_UE_MAX (nr, vui->log2_max_mv_length_vertical, 15);
}
vui->parsed = TRUE;
return TRUE;
error:
GST_WARNING ("error parsing \"VUI Parameters\"");
vui->parsed = FALSE;
return FALSE;
}
static gboolean
get_scaling_list_params (GstH265ScalingList * dest_scaling_list,
guint8 sizeId, guint8 matrixId, guint8 ** sl, guint8 * size,
gint16 ** scaling_list_dc_coef_minus8)
{
switch (sizeId) {
case GST_H265_QUANT_MATIX_4X4:
*sl = dest_scaling_list->scaling_lists_4x4[matrixId];
if (size)
*size = 16;
break;
case GST_H265_QUANT_MATIX_8X8:
*sl = dest_scaling_list->scaling_lists_8x8[matrixId];
if (size)
*size = 64;
break;
case GST_H265_QUANT_MATIX_16X16:
*sl = dest_scaling_list->scaling_lists_16x16[matrixId];
if (size)
*size = 64;
if (scaling_list_dc_coef_minus8)
*scaling_list_dc_coef_minus8 =
dest_scaling_list->scaling_list_dc_coef_minus8_16x16;
break;
case GST_H265_QUANT_MATIX_32X32:
*sl = dest_scaling_list->scaling_lists_32x32[matrixId];
if (size)
*size = 64;
if (scaling_list_dc_coef_minus8)
*scaling_list_dc_coef_minus8 =
dest_scaling_list->scaling_list_dc_coef_minus8_32x32;
break;
default:
return FALSE;
}
return TRUE;
}
static gboolean
get_default_scaling_lists (guint8 ** sl, guint8 sizeId, guint8 matrixId)
{
switch (sizeId) {
case GST_H265_QUANT_MATIX_4X4:
memcpy (*sl, default_scaling_list0, 16);
break;
case GST_H265_QUANT_MATIX_8X8:
case GST_H265_QUANT_MATIX_16X16:
if (matrixId <= 2)
memcpy (*sl, default_scaling_list1, 64);
else
memcpy (*sl, default_scaling_list2, 64);
break;
case GST_H265_QUANT_MATIX_32X32:
if (matrixId == 0)
memcpy (*sl, default_scaling_list1, 64);
else
memcpy (*sl, default_scaling_list2, 64);
break;
default:
return FALSE;
break;
}
return TRUE;
}
static gboolean
gst_h265_parser_parse_scaling_lists (NalReader * nr,
GstH265ScalingList * dest_scaling_list, gboolean use_default)
{
guint8 sizeId;
guint8 matrixId;
guint8 scaling_list_pred_mode_flag = 0;
guint8 scaling_list_pred_matrix_id_delta = 0;
guint8 size, i;
GST_DEBUG ("parsing scaling lists");
for (sizeId = 0; sizeId < 4; sizeId++) {
for (matrixId = 0; matrixId < ((sizeId == 3) ? 2 : 6); matrixId++) {
gint16 *scaling_list_dc_coef_minus8 = NULL;
guint8 *sl;
if (!get_scaling_list_params (dest_scaling_list, sizeId, matrixId, &sl,
&size, &scaling_list_dc_coef_minus8))
goto error;
/* use_default_scaling_matrices forcefully which means,
* sps_scaling_list_enabled_flag=TRUE,
* sps_scaling_list_data_present_flag=FALSE,
* pps_scaling_list_data_present_falg=FALSE */
if (use_default) {
if (!get_default_scaling_lists (&sl, sizeId, matrixId))
goto error;
/* Inferring the value of scaling_list_dc_coef_minus8 */
if (sizeId > 1)
scaling_list_dc_coef_minus8[matrixId] = 8;
} else {
READ_UINT8 (nr, scaling_list_pred_mode_flag, 1);
if (!scaling_list_pred_mode_flag) {
guint8 refMatrixId;
READ_UE_MAX (nr, scaling_list_pred_matrix_id_delta, matrixId);
if (!scaling_list_pred_matrix_id_delta) {
if (!get_default_scaling_lists (&sl, sizeId, matrixId))
goto error;
/* Inferring the value of scaling_list_dc_coef_minus8 */
if (sizeId > 1)
scaling_list_dc_coef_minus8[matrixId] = 8;
} else {
guint8 *temp_sl;
refMatrixId = matrixId - scaling_list_pred_matrix_id_delta; /* 7-30 */
if (!get_scaling_list_params (dest_scaling_list, sizeId,
refMatrixId, &temp_sl, NULL, NULL))
goto error;
for (i = 0; i < size; i++)
sl[i] = temp_sl[i]; /* 7-31 */
/* Inferring the value of scaling_list_dc_coef_minus8 */
if (sizeId > 1)
scaling_list_dc_coef_minus8[matrixId] =
scaling_list_dc_coef_minus8[refMatrixId];
}
} else {
guint8 nextCoef = 8;
gint8 scaling_list_delta_coef;
if (sizeId > 1) {
READ_SE_ALLOWED (nr, scaling_list_dc_coef_minus8[matrixId], -7,
247);
nextCoef = scaling_list_dc_coef_minus8[matrixId] + 8;
}
for (i = 0; i < size; i++) {
READ_SE_ALLOWED (nr, scaling_list_delta_coef, -128, 127);
nextCoef = (nextCoef + scaling_list_delta_coef) & 0xff;
sl[i] = nextCoef;
}
}
}
}
}
return TRUE;
error:
GST_WARNING ("error parsing scaling lists");
return FALSE;
}
static gboolean
gst_h265_parser_parse_short_term_ref_pic_sets (GstH265ShortTermRefPicSet *
stRPS, NalReader * nr, guint8 stRpsIdx, GstH265SPS * sps)
{
guint8 num_short_term_ref_pic_sets;
guint8 RefRpsIdx = 0;
gint16 deltaRps = 0;
guint8 use_delta_flag[16] = { 0 };
guint8 used_by_curr_pic_flag[16] = { 0 };
guint32 delta_poc_s0_minus1[16] = { 0 };
guint32 delta_poc_s1_minus1[16] = { 0 };
gint j, i = 0;
gint dPoc;
GST_DEBUG ("parsing \"ShortTermRefPicSetParameters\"");
/* set default values for fields that might not be present in the bitstream
and have valid defaults */
for (j = 0; j < 16; j++)
use_delta_flag[j] = 1;
num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets;
if (stRpsIdx != 0)
READ_UINT8 (nr, stRPS->inter_ref_pic_set_prediction_flag, 1);
if (stRPS->inter_ref_pic_set_prediction_flag) {
GstH265ShortTermRefPicSet *RefRPS;
if (stRpsIdx == num_short_term_ref_pic_sets)
READ_UE_MAX (nr, stRPS->delta_idx_minus1, stRpsIdx - 1);
READ_UINT8 (nr, stRPS->delta_rps_sign, 1);
READ_UE_MAX (nr, stRPS->abs_delta_rps_minus1, 32767);
RefRpsIdx = stRpsIdx - stRPS->delta_idx_minus1 - 1; /* 7-45 */
deltaRps = (1 - 2 * stRPS->delta_rps_sign) * (stRPS->abs_delta_rps_minus1 + 1); /* 7-46 */
RefRPS = &sps->short_term_ref_pic_set[RefRpsIdx];
stRPS->NumDeltaPocsOfRefRpsIdx = RefRPS->NumDeltaPocs;
for (j = 0; j <= RefRPS->NumDeltaPocs; j++) {
READ_UINT8 (nr, used_by_curr_pic_flag[j], 1);
if (!used_by_curr_pic_flag[j])
READ_UINT8 (nr, use_delta_flag[j], 1);
}
/* 7-47: calculate NumNegativePics, DeltaPocS0 and UsedByCurrPicS0 */
i = 0;
for (j = (RefRPS->NumPositivePics - 1); j >= 0; j--) {
dPoc = RefRPS->DeltaPocS1[j] + deltaRps;
if (dPoc < 0 && use_delta_flag[RefRPS->NumNegativePics + j]) {
stRPS->DeltaPocS0[i] = dPoc;
stRPS->UsedByCurrPicS0[i++] =
used_by_curr_pic_flag[RefRPS->NumNegativePics + j];
}
}
if (deltaRps < 0 && use_delta_flag[RefRPS->NumDeltaPocs]) {
stRPS->DeltaPocS0[i] = deltaRps;
stRPS->UsedByCurrPicS0[i++] = used_by_curr_pic_flag[RefRPS->NumDeltaPocs];
}
for (j = 0; j < RefRPS->NumNegativePics; j++) {
dPoc = RefRPS->DeltaPocS0[j] + deltaRps;
if (dPoc < 0 && use_delta_flag[j]) {
stRPS->DeltaPocS0[i] = dPoc;
stRPS->UsedByCurrPicS0[i++] = used_by_curr_pic_flag[j];
}
}
stRPS->NumNegativePics = i;
/* 7-48: calculate NumPositivePics, DeltaPocS1 and UsedByCurrPicS1 */
i = 0;
for (j = (RefRPS->NumNegativePics - 1); j >= 0; j--) {
dPoc = RefRPS->DeltaPocS0[j] + deltaRps;
if (dPoc > 0 && use_delta_flag[j]) {
stRPS->DeltaPocS1[i] = dPoc;
stRPS->UsedByCurrPicS1[i++] = used_by_curr_pic_flag[j];
}
}
if (deltaRps > 0 && use_delta_flag[RefRPS->NumDeltaPocs]) {
stRPS->DeltaPocS1[i] = deltaRps;
stRPS->UsedByCurrPicS1[i++] = used_by_curr_pic_flag[RefRPS->NumDeltaPocs];
}
for (j = 0; j < RefRPS->NumPositivePics; j++) {
dPoc = RefRPS->DeltaPocS1[j] + deltaRps;
if (dPoc > 0 && use_delta_flag[RefRPS->NumNegativePics + j]) {
stRPS->DeltaPocS1[i] = dPoc;
stRPS->UsedByCurrPicS1[i++] =
used_by_curr_pic_flag[RefRPS->NumNegativePics + j];
}
}
stRPS->NumPositivePics = i;
} else {
/* 7-49 */
READ_UE_MAX (nr, stRPS->NumNegativePics,
sps->max_dec_pic_buffering_minus1[sps->max_sub_layers_minus1]);
/* 7-50 */
READ_UE_MAX (nr, stRPS->NumPositivePics,
(sps->max_dec_pic_buffering_minus1[sps->max_sub_layers_minus1] -
stRPS->NumNegativePics));
for (i = 0; i < stRPS->NumNegativePics; i++) {
READ_UE_MAX (nr, delta_poc_s0_minus1[i], 32767);
/* 7-51 */
READ_UINT8 (nr, stRPS->UsedByCurrPicS0[i], 1);
if (i == 0) {
/* 7-53 */
stRPS->DeltaPocS0[i] = -(delta_poc_s0_minus1[i] + 1);
} else {
/* 7-55 */
stRPS->DeltaPocS0[i] =
stRPS->DeltaPocS0[i - 1] - (delta_poc_s0_minus1[i] + 1);
}
}
for (j = 0; j < stRPS->NumPositivePics; j++) {
READ_UE_MAX (nr, delta_poc_s1_minus1[j], 32767);
/* 7-52 */
READ_UINT8 (nr, stRPS->UsedByCurrPicS1[j], 1);
if (j == 0) {
/* 7-54 */
stRPS->DeltaPocS1[j] = delta_poc_s1_minus1[j] + 1;
} else {
/* 7-56 */
stRPS->DeltaPocS1[j] =
stRPS->DeltaPocS1[j - 1] + (delta_poc_s1_minus1[j] + 1);
}
}
}
/* 7-57 */
stRPS->NumDeltaPocs = stRPS->NumPositivePics + stRPS->NumNegativePics;
return TRUE;
error:
GST_WARNING ("error parsing \"ShortTermRefPicSet Parameters\"");
return FALSE;
}
static gboolean
gst_h265_slice_parse_ref_pic_list_modification (GstH265SliceHdr * slice,
NalReader * nr, gint NumPocTotalCurr)
{
guint i;
GstH265RefPicListModification *rpl_mod = &slice->ref_pic_list_modification;
const guint n = ceil_log2 (NumPocTotalCurr);
READ_UINT8 (nr, rpl_mod->ref_pic_list_modification_flag_l0, 1);
if (rpl_mod->ref_pic_list_modification_flag_l0) {
for (i = 0; i <= slice->num_ref_idx_l0_active_minus1; i++) {
READ_UINT32 (nr, rpl_mod->list_entry_l0[i], n);
CHECK_ALLOWED_MAX (rpl_mod->list_entry_l0[i], (NumPocTotalCurr - 1));
}
}
if (GST_H265_IS_B_SLICE (slice)) {
READ_UINT8 (nr, rpl_mod->ref_pic_list_modification_flag_l1, 1);
if (rpl_mod->ref_pic_list_modification_flag_l1)
for (i = 0; i <= slice->num_ref_idx_l1_active_minus1; i++) {
READ_UINT32 (nr, rpl_mod->list_entry_l1[i], n);
CHECK_ALLOWED_MAX (rpl_mod->list_entry_l1[i], (NumPocTotalCurr - 1));
}
}
return TRUE;
error:
GST_WARNING ("error parsing \"Reference picture list modifications\"");
return FALSE;
}
static gboolean
gst_h265_slice_parse_pred_weight_table (GstH265SliceHdr * slice, NalReader * nr)
{
GstH265PredWeightTable *p;
gint i, j;
GstH265PPS *pps = slice->pps;
GstH265SPS *sps = pps->sps;
GST_DEBUG ("parsing \"Prediction weight table\"");
p = &slice->pred_weight_table;
READ_UE_MAX (nr, p->luma_log2_weight_denom, 7);
if (sps->chroma_format_idc != 0) {
READ_SE_ALLOWED (nr, p->delta_chroma_log2_weight_denom,
(0 - p->luma_log2_weight_denom), (7 - p->luma_log2_weight_denom));
}
for (i = 0; i <= slice->num_ref_idx_l0_active_minus1; i++)
READ_UINT8 (nr, p->luma_weight_l0_flag[i], 1);
if (sps->chroma_format_idc != 0)
for (i = 0; i <= slice->num_ref_idx_l0_active_minus1; i++)
READ_UINT8 (nr, p->chroma_weight_l0_flag[i], 1);
for (i = 0; i <= slice->num_ref_idx_l0_active_minus1; i++) {
if (p->luma_weight_l0_flag[i]) {
READ_SE_ALLOWED (nr, p->delta_luma_weight_l0[i], -128, 127);
READ_SE_ALLOWED (nr, p->luma_offset_l0[i], -128, 127);
}
if (p->chroma_weight_l0_flag[i])
for (j = 0; j < 2; j++) {
READ_SE_ALLOWED (nr, p->delta_chroma_weight_l0[i][j], -128, 127);
READ_SE_ALLOWED (nr, p->delta_chroma_offset_l0[i][j], -512, 511);
}
}
if (GST_H265_IS_B_SLICE (slice)) {
for (i = 0; i <= slice->num_ref_idx_l1_active_minus1; i++)
READ_UINT8 (nr, p->luma_weight_l1_flag[i], 1);
if (sps->chroma_format_idc != 0)
for (i = 0; i <= slice->num_ref_idx_l1_active_minus1; i++)
READ_UINT8 (nr, p->chroma_weight_l1_flag[i], 1);
for (i = 0; i <= slice->num_ref_idx_l1_active_minus1; i++) {
if (p->luma_weight_l1_flag[i]) {
READ_SE_ALLOWED (nr, p->delta_luma_weight_l1[i], -128, 127);
READ_SE_ALLOWED (nr, p->luma_offset_l1[i], -128, 127);
}
if (p->chroma_weight_l1_flag[i])
for (j = 0; j < 2; j++) {
READ_SE_ALLOWED (nr, p->delta_chroma_weight_l1[i][j], -128, 127);
READ_SE_ALLOWED (nr, p->delta_chroma_offset_l1[i][j], -512, 511);
}
}
}
return TRUE;
error:
GST_WARNING ("error parsing \"Prediction weight table\"");
return FALSE;
}
static GstH265ParserResult
gst_h265_parser_parse_buffering_period (GstH265Parser * parser,
GstH265BufferingPeriod * per, NalReader * nr)
{
GstH265SPS *sps;
guint8 sps_id;
guint i;
guint n;
GST_DEBUG ("parsing \"Buffering period\"");
READ_UE_MAX (nr, sps_id, GST_H265_MAX_SPS_COUNT - 1);
sps = gst_h265_parser_get_sps (parser, sps_id);
if (!sps) {
GST_WARNING ("couldn't find associated sequence parameter set with id: %d",
sps_id);
return GST_H265_PARSER_BROKEN_LINK;
}
per->sps = sps;
if (sps->vui_parameters_present_flag) {
GstH265VUIParams *vui = &sps->vui_params;
GstH265HRDParams *hrd = &vui->hrd_params;
if (!hrd->sub_pic_hrd_params_present_flag)
READ_UINT8 (nr, per->irap_cpb_params_present_flag, 1);
if (per->irap_cpb_params_present_flag) {
READ_UINT8 (nr, per->cpb_delay_offset,
(hrd->au_cpb_removal_delay_length_minus1 + 1));
READ_UINT8 (nr, per->dpb_delay_offset,
(hrd->dpb_output_delay_length_minus1 + 1));
}
n = hrd->initial_cpb_removal_delay_length_minus1 + 1;
READ_UINT8 (nr, per->concatenation_flag, 1);
READ_UINT8 (nr, per->au_cpb_removal_delay_delta_minus1,
(hrd->au_cpb_removal_delay_length_minus1 + 1));
if (hrd->nal_hrd_parameters_present_flag) {
for (i = 0; i <= hrd->cpb_cnt_minus1[i]; i++) {
READ_UINT8 (nr, per->nal_initial_cpb_removal_delay[i], n);
READ_UINT8 (nr, per->nal_initial_cpb_removal_offset[i], n);
if (hrd->sub_pic_hrd_params_present_flag
|| per->irap_cpb_params_present_flag) {
READ_UINT8 (nr, per->nal_initial_alt_cpb_removal_delay[i], n);
READ_UINT8 (nr, per->nal_initial_alt_cpb_removal_offset[i], n);
}
}
}
if (hrd->vcl_hrd_parameters_present_flag) {
for (i = 0; i <= hrd->cpb_cnt_minus1[i]; i++) {
READ_UINT8 (nr, per->vcl_initial_cpb_removal_delay[i], n);
READ_UINT8 (nr, per->vcl_initial_cpb_removal_offset[i], n);
if (hrd->sub_pic_hrd_params_present_flag
|| per->irap_cpb_params_present_flag) {
READ_UINT8 (nr, per->vcl_initial_alt_cpb_removal_delay[i], n);
READ_UINT8 (nr, per->vcl_initial_alt_cpb_removal_offset[i], n);
}
}
}
}
return GST_H265_PARSER_OK;
error:
GST_WARNING ("error parsing \"Buffering period\"");
return GST_H265_PARSER_ERROR;
}
static GstH265ParserResult
gst_h265_parser_parse_pic_timing (GstH265Parser * parser,
GstH265PicTiming * tim, NalReader * nr)
{
GstH265ProfileTierLevel *profile_tier_level;
guint i;
GST_DEBUG ("parsing \"Picture timing\"");
if (!parser->last_sps || !parser->last_sps->valid) {
GST_WARNING ("didn't get the associated sequence parameter set for the "
"current access unit");
goto error;
}
profile_tier_level = &parser->last_sps->profile_tier_level;
/* set default values */
if (!profile_tier_level->progressive_source_flag
&& profile_tier_level->interlaced_source_flag)
tim->source_scan_type = 0;
else if (profile_tier_level->progressive_source_flag
&& !profile_tier_level->interlaced_source_flag)
tim->source_scan_type = 1;
else
tim->source_scan_type = 2;
if (parser->last_sps->vui_parameters_present_flag) {
GstH265VUIParams *vui = &parser->last_sps->vui_params;
if (vui->frame_field_info_present_flag) {
READ_UINT8 (nr, tim->pic_struct, 4);
READ_UINT8 (nr, tim->source_scan_type, 2);
READ_UINT8 (nr, tim->duplicate_flag, 1);
} else {
/* set default values */
tim->pic_struct = 0;
}
if (vui->hrd_parameters_present_flag) {
GstH265HRDParams *hrd = &vui->hrd_params;
READ_UINT8 (nr, tim->au_cpb_removal_delay_minus1,
(hrd->au_cpb_removal_delay_length_minus1 + 1));
READ_UINT8 (nr, tim->pic_dpb_output_delay,
(hrd->dpb_output_delay_length_minus1 + 1));
if (hrd->sub_pic_hrd_params_present_flag)
READ_UINT8 (nr, tim->pic_dpb_output_du_delay,
(hrd->dpb_output_delay_du_length_minus1 + 1));
if (hrd->sub_pic_hrd_params_present_flag
&& hrd->sub_pic_cpb_params_in_pic_timing_sei_flag) {
READ_UE (nr, tim->num_decoding_units_minus1);
READ_UINT8 (nr, tim->du_common_cpb_removal_delay_flag, 1);
if (tim->du_common_cpb_removal_delay_flag)
READ_UINT8 (nr, tim->du_common_cpb_removal_delay_increment_minus1,
(hrd->du_cpb_removal_delay_increment_length_minus1 + 1));
tim->num_nalus_in_du_minus1 =
g_new0 (guint32, (tim->num_decoding_units_minus1 + 1));
tim->du_cpb_removal_delay_increment_minus1 =
g_new0 (guint8, (tim->num_decoding_units_minus1 + 1));
for (i = 0; i <= tim->num_decoding_units_minus1; i++) {
READ_UE (nr, tim->num_nalus_in_du_minus1[i]);
if (!tim->du_common_cpb_removal_delay_flag
&& (i < tim->num_decoding_units_minus1))
READ_UINT8 (nr, tim->du_cpb_removal_delay_increment_minus1[i],
(hrd->du_cpb_removal_delay_increment_length_minus1 + 1));
}
}
}
}
return GST_H265_PARSER_OK;
error:
GST_WARNING ("error parsing \"Picture timing\"");
return GST_H265_PARSER_ERROR;
}
static GstH265ParserResult
gst_h265_parser_parse_recovery_point (GstH265Parser * parser,
GstH265RecoveryPoint * rp, NalReader * nr)
{
GstH265SPS *const sps = parser->last_sps;
gint32 max_pic_order_cnt_lsb;
GST_DEBUG ("parsing \"Recovery point\"");
if (!sps || !sps->valid) {
GST_WARNING ("didn't get the associated sequence parameter set for the "
"current access unit");
goto error;
}
max_pic_order_cnt_lsb = pow (2, (sps->log2_max_pic_order_cnt_lsb_minus4 + 4));
READ_SE_ALLOWED (nr, rp->recovery_poc_cnt, -max_pic_order_cnt_lsb / 2,
max_pic_order_cnt_lsb - 1);
READ_UINT8 (nr, rp->exact_match_flag, 1);
READ_UINT8 (nr, rp->broken_link_flag, 1);
return GST_H265_PARSER_OK;
error:
GST_WARNING ("error parsing \"Recovery point\"");
return GST_H265_PARSER_ERROR;
}
static GstH265ParserResult
gst_h265_parser_parse_registered_user_data (GstH265Parser * parser,
GstH265RegisteredUserData * rud, NalReader * nr, guint payload_size)
{
guint8 *data = NULL;
guint i;
rud->data = NULL;
rud->size = 0;
if (payload_size < 2) {
GST_WARNING ("Too small payload size %d", payload_size);
return GST_H265_PARSER_BROKEN_DATA;
}
READ_UINT8 (nr, rud->country_code, 8);
--payload_size;
if (rud->country_code == 0xFF) {
READ_UINT8 (nr, rud->country_code_extension, 8);
--payload_size;
} else {
rud->country_code_extension = 0;
}
if (payload_size < 1) {
GST_WARNING ("No more remaining payload data to store");
return GST_H265_PARSER_BROKEN_DATA;
}
data = g_malloc (payload_size);
for (i = 0; i < payload_size; ++i) {
READ_UINT8 (nr, data[i], 8);
}
GST_MEMDUMP ("SEI user data", data, payload_size);
rud->data = data;
rud->size = payload_size;
return GST_H265_PARSER_OK;
error:
{
GST_WARNING ("error parsing \"Registered User Data\"");
g_free (data);
return GST_H265_PARSER_ERROR;
}
}
static GstH265ParserResult
gst_h265_parser_parse_user_data_unregistered (GstH265Parser * parser,
GstH265UserDataUnregistered * urud, NalReader * nr, guint payload_size)
{
guint8 *data = NULL;
gint i;
if (payload_size < 16) {
GST_WARNING ("Too small payload size %d", payload_size);
return GST_H265_PARSER_BROKEN_DATA;
}
for (int i = 0; i < 16; i++) {
READ_UINT8 (nr, urud->uuid[i], 8);
}
payload_size -= 16;
urud->size = payload_size;
data = g_malloc0 (payload_size);
for (i = 0; i < payload_size; ++i) {
READ_UINT8 (nr, data[i], 8);
}
if (payload_size < 1) {
GST_WARNING ("No more remaining payload data to store");
g_clear_pointer (&data, g_free);
return GST_H265_PARSER_BROKEN_DATA;
}
urud->data = data;
GST_MEMDUMP ("SEI user data unregistered", data, payload_size);
return GST_H265_PARSER_OK;
error:
{
GST_WARNING ("error parsing \"User Data Unregistered\"");
g_clear_pointer (&data, g_free);
return GST_H265_PARSER_ERROR;
}
}
static GstH265ParserResult
gst_h265_parser_parse_time_code (GstH265Parser * parser,
GstH265TimeCode * tc, NalReader * nr)
{
guint i;
GST_DEBUG ("parsing \"Time code\"");
READ_UINT8 (nr, tc->num_clock_ts, 2);
for (i = 0; i < tc->num_clock_ts; i++) {
READ_UINT8 (nr, tc->clock_timestamp_flag[i], 1);
if (tc->clock_timestamp_flag[i]) {
READ_UINT8 (nr, tc->units_field_based_flag[i], 1);
READ_UINT8 (nr, tc->counting_type[i], 5);
READ_UINT8 (nr, tc->full_timestamp_flag[i], 1);
READ_UINT8 (nr, tc->discontinuity_flag[i], 1);
READ_UINT8 (nr, tc->cnt_dropped_flag[i], 1);
READ_UINT16 (nr, tc->n_frames[i], 9);
if (tc->full_timestamp_flag[i]) {
tc->seconds_flag[i] = TRUE;
READ_UINT8 (nr, tc->seconds_value[i], 6);
tc->minutes_flag[i] = TRUE;
READ_UINT8 (nr, tc->minutes_value[i], 6);
tc->hours_flag[i] = TRUE;
READ_UINT8 (nr, tc->hours_value[i], 5);
} else {
READ_UINT8 (nr, tc->seconds_flag[i], 1);
if (tc->seconds_flag[i]) {
READ_UINT8 (nr, tc->seconds_value[i], 6);
READ_UINT8 (nr, tc->minutes_flag[i], 1);
if (tc->minutes_flag[i]) {
READ_UINT8 (nr, tc->minutes_value[i], 6);
READ_UINT8 (nr, tc->hours_flag[i], 1);
if (tc->hours_flag[i]) {
READ_UINT8 (nr, tc->hours_value[i], 5);
}
}
}
}
}
READ_UINT8 (nr, tc->time_offset_length[i], 5);
if (tc->time_offset_length[i] > 0)
READ_UINT32 (nr, tc->time_offset_value[i], tc->time_offset_length[i]);
}
return GST_H265_PARSER_OK;
error:
GST_WARNING ("error parsing \"Time code\"");
return GST_H265_PARSER_ERROR;
}
static GstH265ParserResult
gst_h265_parser_parse_mastering_display_colour_volume (GstH265Parser * parser,
GstH265MasteringDisplayColourVolume * mdcv, NalReader * nr)
{
guint i;
GST_DEBUG ("parsing \"Mastering display colour volume\"");
for (i = 0; i < 3; i++) {
READ_UINT16 (nr, mdcv->display_primaries_x[i], 16);
READ_UINT16 (nr, mdcv->display_primaries_y[i], 16);
}
READ_UINT16 (nr, mdcv->white_point_x, 16);
READ_UINT16 (nr, mdcv->white_point_y, 16);
READ_UINT32 (nr, mdcv->max_display_mastering_luminance, 32);
READ_UINT32 (nr, mdcv->min_display_mastering_luminance, 32);
return GST_H265_PARSER_OK;
error:
GST_WARNING ("error parsing \"Mastering display colour volume\"");
return GST_H265_PARSER_ERROR;
}
static GstH265ParserResult
gst_h265_parser_parse_content_light_level_info (GstH265Parser * parser,
GstH265ContentLightLevel * cll, NalReader * nr)
{
GST_DEBUG ("parsing \"Content light level\"");
READ_UINT16 (nr, cll->max_content_light_level, 16);
READ_UINT16 (nr, cll->max_pic_average_light_level, 16);
return GST_H265_PARSER_OK;
error:
GST_WARNING ("error parsing \"Content light level\"");
return GST_H265_PARSER_ERROR;
}
/******** API *************/
/**
* gst_h265_parser_new:
*
* Creates a new #GstH265Parser. It should be freed with
* gst_h265_parser_free after use.
*
* Returns: a new #GstH265Parser
*/
GstH265Parser *
gst_h265_parser_new (void)
{
GstH265Parser *parser;
parser = g_new0 (GstH265Parser, 1);
return parser;
}
/**
* gst_h265_parser_free:
* @parser: the #GstH265Parser to free
*
* Frees @parser and sets it to %NULL
*/
void
gst_h265_parser_free (GstH265Parser * parser)
{
g_free (parser);
parser = NULL;
}
/**
* gst_h265_parser_identify_nalu_unchecked:
* @parser: a #GstH265Parser
* @data: The data to parse
* @offset: the offset from which to parse @data
* @size: the size of @data
* @nalu: The #GstH265NalUnit where to store parsed nal headers
*
* Parses @data and fills @nalu from the next nalu data from @data.
*
* This differs from @gst_h265_parser_identify_nalu in that it doesn't
* check whether the packet is complete or not.
*
* Note: Only use this function if you already know the provided @data
* is a complete NALU, else use @gst_h265_parser_identify_nalu.
*
* Returns: a #GstH265ParserResult
*/
GstH265ParserResult
gst_h265_parser_identify_nalu_unchecked (GstH265Parser * parser,
const guint8 * data, guint offset, gsize size, GstH265NalUnit * nalu)
{
gint off1;
memset (nalu, 0, sizeof (*nalu));
if (size < offset + 4) {
GST_DEBUG ("Can't parse, buffer has too small size %" G_GSIZE_FORMAT
", offset %u", size, offset);
return GST_H265_PARSER_ERROR;
}
off1 = scan_for_start_codes (data + offset, size - offset);
if (off1 < 0) {
GST_DEBUG ("No start code prefix in this buffer");
return GST_H265_PARSER_NO_NAL;
}
nalu->sc_offset = offset + off1;
/* The scanner ensures one byte passed the start code but to
* identify an HEVC NAL, we need 2. */
if (size - nalu->sc_offset - 3 < 2) {
GST_DEBUG ("Not enough bytes after start code to identify");
return GST_H265_PARSER_NO_NAL;
}
/* sc might have 2 or 3 0-bytes */
if (nalu->sc_offset > 0 && data[nalu->sc_offset - 1] == 00)
nalu->sc_offset--;
nalu->offset = offset + off1 + 3;
nalu->data = (guint8 *) data;
nalu->size = size - nalu->offset;
if (!gst_h265_parse_nalu_header (nalu)) {
GST_WARNING ("error parsing \"NAL unit header\"");
nalu->size = 0;
return GST_H265_PARSER_BROKEN_DATA;
}
nalu->valid = TRUE;
if (nalu->type == GST_H265_NAL_EOS || nalu->type == GST_H265_NAL_EOB) {
GST_DEBUG ("end-of-seq or end-of-stream nal found");
nalu->size = 2;
return GST_H265_PARSER_OK;
}
return GST_H265_PARSER_OK;
}
/**
* gst_h265_parser_identify_nalu:
* @parser: a #GstH265Parser
* @data: The data to parse
* @offset: the offset from which to parse @data
* @size: the size of @data
* @nalu: The #GstH265NalUnit where to store parsed nal headers
*
* Parses @data and fills @nalu from the next nalu data from @data
*
* Returns: a #GstH265ParserResult
*/
GstH265ParserResult
gst_h265_parser_identify_nalu (GstH265Parser * parser,
const guint8 * data, guint offset, gsize size, GstH265NalUnit * nalu)
{
GstH265ParserResult res;
gint off2;
res =
gst_h265_parser_identify_nalu_unchecked (parser, data, offset, size,
nalu);
if (res != GST_H265_PARSER_OK)
goto beach;
/* The two NALs are exactly 2 bytes size and are placed at the end of an AU,
* there is no need to wait for the following */
if (nalu->type == GST_H265_NAL_EOS || nalu->type == GST_H265_NAL_EOB)
goto beach;
off2 = scan_for_start_codes (data + nalu->offset, size - nalu->offset);
if (off2 < 0) {
GST_DEBUG ("Nal start %d, No end found", nalu->offset);
return GST_H265_PARSER_NO_NAL_END;
}
/* Callers assumes that enough data will available to identify the next NAL,
* but scan_for_start_codes() only ensure 1 extra byte is available. Ensure
* we have the required two header bytes (3 bytes start code and 2 byte
* header). */
if (size - (nalu->offset + off2) < 5) {
GST_DEBUG ("Not enough bytes identify the next NAL.");
return GST_H265_PARSER_NO_NAL_END;
}
/* Mini performance improvement:
* We could have a way to store how many 0s were skipped to avoid
* parsing them again on the next NAL */
while (off2 > 0 && data[nalu->offset + off2 - 1] == 00)
off2--;
nalu->size = off2;
if (nalu->size < 3)
return GST_H265_PARSER_BROKEN_DATA;
GST_DEBUG ("Complete nal found. Off: %d, Size: %d", nalu->offset, nalu->size);
beach:
return res;
}
/**
* gst_h265_parser_identify_nalu_hevc:
* @parser: a #GstH265Parser
* @data: The data to parse, must be the beging of the Nal unit
* @offset: the offset from which to parse @data
* @size: the size of @data
* @nal_length_size: the size in bytes of the HEVC nal length prefix.
* @nalu: The #GstH265NalUnit where to store parsed nal headers
*
* Parses @data and sets @nalu.
*
* Returns: a #GstH265ParserResult
*/
GstH265ParserResult
gst_h265_parser_identify_nalu_hevc (GstH265Parser * parser,
const guint8 * data, guint offset, gsize size, guint8 nal_length_size,
GstH265NalUnit * nalu)
{
GstBitReader br;
memset (nalu, 0, sizeof (*nalu));
/* Would overflow guint below otherwise: the callers needs to ensure that
* this never happens */
if (offset > G_MAXUINT32 - nal_length_size) {
GST_WARNING ("offset + nal_length_size overflow");
nalu->size = 0;
return GST_H265_PARSER_BROKEN_DATA;
}
if (size < offset + nal_length_size) {
GST_DEBUG ("Can't parse, buffer has too small size %" G_GSIZE_FORMAT
", offset %u", size, offset);
return GST_H265_PARSER_ERROR;
}
size = size - offset;
gst_bit_reader_init (&br, data + offset, size);
nalu->size = gst_bit_reader_get_bits_uint32_unchecked (&br,
nal_length_size * 8);
nalu->sc_offset = offset;
nalu->offset = offset + nal_length_size;
if (nalu->size > G_MAXUINT32 - nal_length_size) {
GST_WARNING ("NALU size + nal_length_size overflow");
nalu->size = 0;
return GST_H265_PARSER_BROKEN_DATA;
}
if (size < (gsize) nalu->size + nal_length_size) {
nalu->size = 0;
return GST_H265_PARSER_NO_NAL_END;
}
nalu->data = (guint8 *) data;
if (!gst_h265_parse_nalu_header (nalu)) {
GST_WARNING ("error parsing \"NAL unit header\"");
nalu->size = 0;
return GST_H265_PARSER_BROKEN_DATA;
}
if (nalu->size < 2)
return GST_H265_PARSER_BROKEN_DATA;
nalu->valid = TRUE;
return GST_H265_PARSER_OK;
}
/**
* gst_h265_parser_identify_and_split_nalu_hevc:
* @parser: a #GstH265Parser
* @data: The data to parse, must be the beging of the Nal unit
* @offset: the offset from which to parse @data
* @size: the size of @data
* @nal_length_size: the size in bytes of the HEVC nal length prefix.
* @nalus: a caller allocated GArray of #GstH265NalUnit where to store parsed nal headers
* @consumed: the size of consumed bytes
*
* Parses @data for packetized (e.g., hvc1/hev1) bitstream and
* sets @nalus. In addition to nal identifying process,
* this method scans start-code prefix to split malformed packet into
* actual nal chunks.
*
* Returns: a #GstH265ParserResult
*
* Since: 1.22
*/
GstH265ParserResult
gst_h265_parser_identify_and_split_nalu_hevc (GstH265Parser * parser,
const guint8 * data, guint offset, gsize size, guint8 nal_length_size,
GArray * nalus, gsize * consumed)
{
GstBitReader br;
guint nalu_size;
guint remaining;
guint off;
guint sc_size;
g_return_val_if_fail (data != NULL, GST_H265_PARSER_ERROR);
g_return_val_if_fail (nalus != NULL, GST_H265_PARSER_ERROR);
g_return_val_if_fail (nal_length_size > 0 && nal_length_size < 5,
GST_H265_PARSER_ERROR);
g_array_set_size (nalus, 0);
if (consumed)
*consumed = 0;
/* Would overflow guint below otherwise: the callers needs to ensure that
* this never happens */
if (offset > G_MAXUINT32 - nal_length_size) {
GST_WARNING ("offset + nal_length_size overflow");
return GST_H265_PARSER_BROKEN_DATA;
}
if (size < offset + nal_length_size) {
GST_DEBUG ("Can't parse, buffer has too small size %" G_GSIZE_FORMAT
", offset %u", size, offset);
return GST_H265_PARSER_ERROR;
}
/* Read nal unit size and unwrap the size field */
gst_bit_reader_init (&br, data + offset, size - offset);
nalu_size = gst_bit_reader_get_bits_uint32_unchecked (&br,
nal_length_size * 8);
if (nalu_size < 2) {
GST_WARNING ("too small nal size %d", nalu_size);
return GST_H265_PARSER_BROKEN_DATA;
}
if (size < (gsize) nalu_size + nal_length_size) {
GST_WARNING ("larger nalu size %d than data size %" G_GSIZE_FORMAT,
nalu_size + nal_length_size, size);
return GST_H265_PARSER_BROKEN_DATA;
}
if (consumed)
*consumed = nalu_size + nal_length_size;
off = offset + nal_length_size;
remaining = nalu_size;
sc_size = nal_length_size;
/* Drop trailing start-code since it will not be scanned */
if (remaining >= 3) {
if (data[off + remaining - 1] == 0x01 && data[off + remaining - 2] == 0x00
&& data[off + remaining - 3] == 0x00) {
remaining -= 3;
/* 4 bytes start-code */
if (remaining > 0 && data[off + remaining - 1] == 0x00)
remaining--;
}
}
/* Looping to split malformed nal units. nal-length field was dropped above
* so expected bitstream structure are:
*
* <complete nalu>
* | nalu |
* sc scan result will be -1 and handled in CONDITION-A
*
* <nalu with startcode prefix>
* | SC | nalu |
* Hit CONDITION-C first then terminated in CONDITION-A
*
* <first nal has no startcode but others have>
* | nalu | SC | nalu | ...
* CONDITION-B handles those cases
*/
do {
GstH265NalUnit nalu;
gint sc_offset = -1;
guint skip_size = 0;
memset (&nalu, 0, sizeof (GstH265NalUnit));
/* startcode 3 bytes + minimum nal size 2 */
if (remaining >= 5)
sc_offset = scan_for_start_codes (data + off, remaining);
if (sc_offset < 0) {
if (remaining >= 2) {
/* CONDITION-A */
/* Last chunk */
nalu.size = remaining;
nalu.sc_offset = off - sc_size;
nalu.offset = off;
nalu.data = (guint8 *) data;
nalu.valid = TRUE;
gst_h265_parse_nalu_header (&nalu);
g_array_append_val (nalus, nalu);
}
break;
} else if ((sc_offset == 2 && data[off + sc_offset - 1] != 0)
|| sc_offset > 2) {
/* CONDITION-B */
/* Found trailing startcode prefix */
nalu.size = sc_offset;
if (data[off + sc_offset - 1] == 0) {
/* 4 bytes start code */
nalu.size--;
}
nalu.sc_offset = off - sc_size;
nalu.offset = off;
nalu.data = (guint8 *) data;
nalu.valid = TRUE;
gst_h265_parse_nalu_header (&nalu);
g_array_append_val (nalus, nalu);
} else {
/* CONDITION-C */
/* startcode located at beginning of this chunk without actual nal data.
* skip this start code */
}
skip_size = sc_offset + 3;
if (skip_size >= remaining)
break;
/* no more nal-length bytes but 3bytes startcode */
sc_size = 3;
if (sc_offset > 0 && data[off + sc_offset - 1] == 0)
sc_size++;
remaining -= skip_size;
off += skip_size;
} while (remaining >= 2);
if (nalus->len > 0)
return GST_H265_PARSER_OK;
GST_WARNING ("No nal found");
return GST_H265_PARSER_BROKEN_DATA;
}
/**
* gst_h265_parser_parse_nal:
* @parser: a #GstH265Parser
* @nalu: The #GstH265NalUnit to parse
*
* This function should be called in the case one doesn't need to
* parse a specific structure. It is necessary to do so to make
* sure @parser is up to date.
*
* Returns: a #GstH265ParserResult
*/
GstH265ParserResult
gst_h265_parser_parse_nal (GstH265Parser * parser, GstH265NalUnit * nalu)
{
GstH265VPS vps;
GstH265SPS sps;
GstH265PPS pps;
switch (nalu->type) {
case GST_H265_NAL_VPS:
return gst_h265_parser_parse_vps (parser, nalu, &vps);
break;
case GST_H265_NAL_SPS:
return gst_h265_parser_parse_sps (parser, nalu, &sps, FALSE);
break;
case GST_H265_NAL_PPS:
return gst_h265_parser_parse_pps (parser, nalu, &pps);
}
return GST_H265_PARSER_OK;
}
/**
* gst_h265_parser_parse_vps:
* @parser: a #GstH265Parser
* @nalu: The #GST_H265_NAL_VPS #GstH265NalUnit to parse
* @vps: The #GstH265VPS to fill.
*
* Parses @data, and fills the @vps structure.
*
* Returns: a #GstH265ParserResult
*/
GstH265ParserResult
gst_h265_parser_parse_vps (GstH265Parser * parser, GstH265NalUnit * nalu,
GstH265VPS * vps)
{
GstH265ParserResult res = gst_h265_parse_vps (nalu, vps);
if (res == GST_H265_PARSER_OK) {
GST_DEBUG ("adding video parameter set with id: %d to array", vps->id);
parser->vps[vps->id] = *vps;
parser->last_vps = &parser->vps[vps->id];
}
return res;
}
/**
* gst_h265_parse_vps:
* @nalu: The #GST_H265_NAL_VPS #GstH265NalUnit to parse
* @sps: The #GstH265VPS to fill.
*
* Parses @data, and fills the @vps structure.
*
* Returns: a #GstH265ParserResult
*/
GstH265ParserResult
gst_h265_parse_vps (GstH265NalUnit * nalu, GstH265VPS * vps)
{
NalReader nr;
guint i, j;
GST_DEBUG ("parsing VPS");
nal_reader_init (&nr, nalu->data + nalu->offset + nalu->header_bytes,
nalu->size - nalu->header_bytes);
memset (vps, 0, sizeof (*vps));
vps->cprms_present_flag = 1;
READ_UINT8 (&nr, vps->id, 4);
READ_UINT8 (&nr, vps->base_layer_internal_flag, 1);
READ_UINT8 (&nr, vps->base_layer_available_flag, 1);
READ_UINT8 (&nr, vps->max_layers_minus1, 6);
READ_UINT8 (&nr, vps->max_sub_layers_minus1, 3);
READ_UINT8 (&nr, vps->temporal_id_nesting_flag, 1);
/* skip reserved_0xffff_16bits */
if (!nal_reader_skip (&nr, 16))
goto error;
if (!gst_h265_parse_profile_tier_level (&vps->profile_tier_level, &nr,
vps->max_sub_layers_minus1))
goto error;
READ_UINT8 (&nr, vps->sub_layer_ordering_info_present_flag, 1);
for (i =
(vps->sub_layer_ordering_info_present_flag ? 0 :
vps->max_sub_layers_minus1); i <= vps->max_sub_layers_minus1; i++) {
READ_UE_MAX (&nr, vps->max_dec_pic_buffering_minus1[i], G_MAXUINT32 - 1);
READ_UE_MAX (&nr, vps->max_num_reorder_pics[i],
vps->max_dec_pic_buffering_minus1[i]);
READ_UE_MAX (&nr, vps->max_latency_increase_plus1[i], G_MAXUINT32 - 1);
}
/* setting default values if vps->sub_layer_ordering_info_present_flag is zero */
if (!vps->sub_layer_ordering_info_present_flag && vps->max_sub_layers_minus1) {
for (i = 0; i <= (vps->max_sub_layers_minus1 - 1); i++) {
vps->max_dec_pic_buffering_minus1[i] =
vps->max_dec_pic_buffering_minus1[vps->max_sub_layers_minus1];
vps->max_num_reorder_pics[i] =
vps->max_num_reorder_pics[vps->max_sub_layers_minus1];
vps->max_latency_increase_plus1[i] =
vps->max_latency_increase_plus1[vps->max_sub_layers_minus1];
}
}
READ_UINT8 (&nr, vps->max_layer_id, 6);
/* shall allow 63 */
CHECK_ALLOWED_MAX (vps->max_layer_id, 63);
READ_UE_MAX (&nr, vps->num_layer_sets_minus1, 1023);
/* allowed range is 0 to 1023 */
CHECK_ALLOWED_MAX (vps->num_layer_sets_minus1, 1023);
for (i = 1; i <= vps->num_layer_sets_minus1; i++) {
for (j = 0; j <= vps->max_layer_id; j++) {
/* layer_id_included_flag[i][j] */
/* FIXME: need to parse this when we can support parsing multi-layer info. */
if (!nal_reader_skip (&nr, 1))
goto error;
}
}
READ_UINT8 (&nr, vps->timing_info_present_flag, 1);
if (vps->timing_info_present_flag) {
READ_UINT32 (&nr, vps->num_units_in_tick, 32);
READ_UINT32 (&nr, vps->time_scale, 32);
READ_UINT8 (&nr, vps->poc_proportional_to_timing_flag, 1);
if (vps->poc_proportional_to_timing_flag)
READ_UE_MAX (&nr, vps->num_ticks_poc_diff_one_minus1, G_MAXUINT32 - 1);
READ_UE_MAX (&nr, vps->num_hrd_parameters, 1024);
/* allowed range is
* 0 to vps_num_layer_sets_minus1 + 1 */
CHECK_ALLOWED_MAX (vps->num_hrd_parameters, vps->num_layer_sets_minus1 + 1);
if (vps->num_hrd_parameters) {
READ_UE_MAX (&nr, vps->hrd_layer_set_idx, 1023);
/* allowed range is
* ( vps_base_layer_internal_flag ? 0 : 1 ) to vps_num_layer_sets_minus1
*/
CHECK_ALLOWED_MAX (vps->hrd_layer_set_idx, vps->num_layer_sets_minus1);
if (!gst_h265_parse_hrd_parameters (&vps->hrd_params, &nr,
vps->cprms_present_flag, vps->max_sub_layers_minus1))
goto error;
}
/* FIXME: VPS can have multiple hrd parameters, and therefore hrd_params
* should be an array (like Garray). But it also requires new _clear()
* method for free the array in GstH265VPS whenever gst_h265_parse_vps()
* is called. Need to work for multi-layer related parsing supporting
*
* FIXME: Following code is just work around to find correct
* vps_extension position */
/* skip the first parsed one above */
for (i = 1; i < vps->num_hrd_parameters; i++) {
guint16 hrd_layer_set_idx;
guint8 cprms_present_flag;
GstH265HRDParams hrd_params;
READ_UE_MAX (&nr, hrd_layer_set_idx, 1023);
CHECK_ALLOWED_MAX (hrd_layer_set_idx, vps->num_layer_sets_minus1);
/* need parsing if (i > 1) */
READ_UINT8 (&nr, cprms_present_flag, 1);
if (!gst_h265_parse_hrd_parameters (&hrd_params, &nr,
cprms_present_flag, vps->max_sub_layers_minus1))
goto error;
}
}
READ_UINT8 (&nr, vps->vps_extension, 1);
vps->valid = TRUE;
return GST_H265_PARSER_OK;
error:
GST_WARNING ("error parsing \"Video parameter set\"");
vps->valid = FALSE;
return GST_H265_PARSER_ERROR;
}
/**
* gst_h265_parser_parse_sps:
* @parser: a #GstH265Parser
* @nalu: The #GST_H265_NAL_SPS #GstH265NalUnit to parse
* @sps: The #GstH265SPS to fill.
* @parse_vui_params: Whether to parse the vui_params or not
*
* Parses @data, and fills the @sps structure.
*
* Returns: a #GstH265ParserResult
*/
GstH265ParserResult
gst_h265_parser_parse_sps (GstH265Parser * parser, GstH265NalUnit * nalu,
GstH265SPS * sps, gboolean parse_vui_params)
{
GstH265ParserResult res =
gst_h265_parse_sps (parser, nalu, sps, parse_vui_params);
if (res == GST_H265_PARSER_OK) {
GST_DEBUG ("adding sequence parameter set with id: %d to array", sps->id);
parser->sps[sps->id] = *sps;
parser->last_sps = &parser->sps[sps->id];
}
return res;
}
/**
* gst_h265_parse_sps:
* parser: The #GstH265Parser
* @nalu: The #GST_H265_NAL_SPS #GstH265NalUnit to parse
* @sps: The #GstH265SPS to fill.
* @parse_vui_params: Whether to parse the vui_params or not
*
* Parses @data, and fills the @sps structure.
*
* Returns: a #GstH265ParserResult
*/
GstH265ParserResult
gst_h265_parse_sps (GstH265Parser * parser, GstH265NalUnit * nalu,
GstH265SPS * sps, gboolean parse_vui_params)
{
NalReader nr;
guint i;
guint subwc[] = { 1, 2, 2, 1, 1 };
guint subhc[] = { 1, 2, 1, 1, 1 };
GST_DEBUG ("parsing SPS");
nal_reader_init (&nr, nalu->data + nalu->offset + nalu->header_bytes,
nalu->size - nalu->header_bytes);
memset (sps, 0, sizeof (*sps));
READ_UINT8 (&nr, sps->vps_id, 4);
READ_UINT8 (&nr, sps->max_sub_layers_minus1, 3);
READ_UINT8 (&nr, sps->temporal_id_nesting_flag, 1);
if (!gst_h265_parse_profile_tier_level (&sps->profile_tier_level, &nr,
sps->max_sub_layers_minus1))
goto error;
READ_UE_MAX (&nr, sps->id, GST_H265_MAX_SPS_COUNT - 1);
READ_UE_MAX (&nr, sps->chroma_format_idc, 3);
if (sps->chroma_format_idc == 3)
READ_UINT8 (&nr, sps->separate_colour_plane_flag, 1);
READ_UE_ALLOWED (&nr, sps->pic_width_in_luma_samples, 1, 16888);
READ_UE_ALLOWED (&nr, sps->pic_height_in_luma_samples, 1, 16888);
READ_UINT8 (&nr, sps->conformance_window_flag, 1);
if (sps->conformance_window_flag) {
READ_UE (&nr, sps->conf_win_left_offset);
READ_UE (&nr, sps->conf_win_right_offset);
READ_UE (&nr, sps->conf_win_top_offset);
READ_UE (&nr, sps->conf_win_bottom_offset);
}
READ_UE_MAX (&nr, sps->bit_depth_luma_minus8, 6);
READ_UE_MAX (&nr, sps->bit_depth_chroma_minus8, 6);
READ_UE_MAX (&nr, sps->log2_max_pic_order_cnt_lsb_minus4, 12);
READ_UINT8 (&nr, sps->sub_layer_ordering_info_present_flag, 1);
for (i =
(sps->sub_layer_ordering_info_present_flag ? 0 :
sps->max_sub_layers_minus1); i <= sps->max_sub_layers_minus1; i++) {
READ_UE_MAX (&nr, sps->max_dec_pic_buffering_minus1[i], 16);
READ_UE_MAX (&nr, sps->max_num_reorder_pics[i],
sps->max_dec_pic_buffering_minus1[i]);
READ_UE_MAX (&nr, sps->max_latency_increase_plus1[i], G_MAXUINT32 - 1);
}
/* setting default values if sps->sub_layer_ordering_info_present_flag is zero */
if (!sps->sub_layer_ordering_info_present_flag && sps->max_sub_layers_minus1) {
for (i = 0; i <= (sps->max_sub_layers_minus1 - 1); i++) {
sps->max_dec_pic_buffering_minus1[i] =
sps->max_dec_pic_buffering_minus1[sps->max_sub_layers_minus1];
sps->max_num_reorder_pics[i] =
sps->max_num_reorder_pics[sps->max_sub_layers_minus1];
sps->max_latency_increase_plus1[i] =
sps->max_latency_increase_plus1[sps->max_sub_layers_minus1];
}
}
/* The limits are calculted based on the profile_tier_level constraint
* in Annex-A: CtbLog2SizeY = 4 to 6 */
READ_UE_MAX (&nr, sps->log2_min_luma_coding_block_size_minus3, 3);
READ_UE_MAX (&nr, sps->log2_diff_max_min_luma_coding_block_size, 6);
READ_UE_MAX (&nr, sps->log2_min_transform_block_size_minus2, 3);
READ_UE_MAX (&nr, sps->log2_diff_max_min_transform_block_size, 3);
READ_UE_MAX (&nr, sps->max_transform_hierarchy_depth_inter, 4);
READ_UE_MAX (&nr, sps->max_transform_hierarchy_depth_intra, 4);
READ_UINT8 (&nr, sps->scaling_list_enabled_flag, 1);
if (sps->scaling_list_enabled_flag) {
READ_UINT8 (&nr, sps->scaling_list_data_present_flag, 1);
if (sps->scaling_list_data_present_flag)
if (!gst_h265_parser_parse_scaling_lists (&nr, &sps->scaling_list, FALSE))
goto error;
}
READ_UINT8 (&nr, sps->amp_enabled_flag, 1);
READ_UINT8 (&nr, sps->sample_adaptive_offset_enabled_flag, 1);
READ_UINT8 (&nr, sps->pcm_enabled_flag, 1);
if (sps->pcm_enabled_flag) {
READ_UINT8 (&nr, sps->pcm_sample_bit_depth_luma_minus1, 4);
READ_UINT8 (&nr, sps->pcm_sample_bit_depth_chroma_minus1, 4);
READ_UE_MAX (&nr, sps->log2_min_pcm_luma_coding_block_size_minus3, 2);
READ_UE_MAX (&nr, sps->log2_diff_max_min_pcm_luma_coding_block_size, 2);
READ_UINT8 (&nr, sps->pcm_loop_filter_disabled_flag, 1);
}
READ_UE_MAX (&nr, sps->num_short_term_ref_pic_sets, 64);
for (i = 0; i < sps->num_short_term_ref_pic_sets; i++)
if (!gst_h265_parser_parse_short_term_ref_pic_sets
(&sps->short_term_ref_pic_set[i], &nr, i, sps))
goto error;
READ_UINT8 (&nr, sps->long_term_ref_pics_present_flag, 1);
if (sps->long_term_ref_pics_present_flag) {
READ_UE_MAX (&nr, sps->num_long_term_ref_pics_sps, 32);
for (i = 0; i < sps->num_long_term_ref_pics_sps; i++) {
READ_UINT16 (&nr, sps->lt_ref_pic_poc_lsb_sps[i],
sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
READ_UINT8 (&nr, sps->used_by_curr_pic_lt_sps_flag[i], 1);
}
}
READ_UINT8 (&nr, sps->temporal_mvp_enabled_flag, 1);
READ_UINT8 (&nr, sps->strong_intra_smoothing_enabled_flag, 1);
READ_UINT8 (&nr, sps->vui_parameters_present_flag, 1);
if (sps->vui_parameters_present_flag && parse_vui_params)
if (!gst_h265_parse_vui_parameters (sps, &nr))
goto error;
READ_UINT8 (&nr, sps->sps_extension_flag, 1);
if (sps->sps_extension_flag) {
READ_UINT8 (&nr, sps->sps_range_extension_flag, 1);
READ_UINT8 (&nr, sps->sps_multilayer_extension_flag, 1);
READ_UINT8 (&nr, sps->sps_3d_extension_flag, 1);
READ_UINT8 (&nr, sps->sps_scc_extension_flag, 1);
READ_UINT8 (&nr, sps->sps_extension_4bits, 4);
}
if (sps->sps_range_extension_flag) {
READ_UINT8 (&nr,
sps->sps_extension_params.transform_skip_rotation_enabled_flag, 1);
READ_UINT8 (&nr,
sps->sps_extension_params.transform_skip_context_enabled_flag, 1);
READ_UINT8 (&nr, sps->sps_extension_params.implicit_rdpcm_enabled_flag, 1);
READ_UINT8 (&nr, sps->sps_extension_params.explicit_rdpcm_enabled_flag, 1);
READ_UINT8 (&nr,
sps->sps_extension_params.extended_precision_processing_flag, 1);
READ_UINT8 (&nr, sps->sps_extension_params.intra_smoothing_disabled_flag,
1);
READ_UINT8 (&nr,
sps->sps_extension_params.high_precision_offsets_enabled_flag, 1);
READ_UINT8 (&nr,
sps->sps_extension_params.persistent_rice_adaptation_enabled_flag, 1);
READ_UINT8 (&nr,
sps->sps_extension_params.cabac_bypass_alignment_enabled_flag, 1);
}
if (sps->sps_multilayer_extension_flag) {
GST_WARNING ("do not support multilayer extension, skip all"
" remaining bits");
goto done;
}
if (sps->sps_3d_extension_flag) {
GST_WARNING ("do not support 3d extension, skip all remaining bits");
goto done;
}
if (sps->sps_scc_extension_flag) {
READ_UINT8 (&nr,
sps->sps_scc_extension_params.sps_curr_pic_ref_enabled_flag, 1);
READ_UINT8 (&nr, sps->sps_scc_extension_params.palette_mode_enabled_flag,
1);
if (sps->sps_scc_extension_params.palette_mode_enabled_flag) {
READ_UE_MAX (&nr, sps->sps_scc_extension_params.palette_max_size, 64);
READ_UE_MAX (&nr,
sps->sps_scc_extension_params.delta_palette_max_predictor_size,
128 - sps->sps_scc_extension_params.palette_max_size);
READ_UINT8 (&nr,
sps->
sps_scc_extension_params.sps_palette_predictor_initializers_present_flag,
1);
if (sps->
sps_scc_extension_params.sps_palette_predictor_initializers_present_flag)
{
guint comp;
READ_UE_MAX (&nr,
sps->
sps_scc_extension_params.sps_num_palette_predictor_initializer_minus1,
sps->sps_scc_extension_params.palette_max_size +
sps->sps_scc_extension_params.delta_palette_max_predictor_size - 1);
for (comp = 0; comp < (sps->chroma_format_idc == 0 ? 1 : 3); comp++) {
guint num_bits;
guint num =
sps->
sps_scc_extension_params.sps_num_palette_predictor_initializer_minus1
+ 1;
num_bits = (comp == 0 ? sps->bit_depth_luma_minus8 + 8 :
sps->bit_depth_chroma_minus8 + 8);
for (i = 0; i < num; i++)
READ_UINT32 (&nr,
sps->sps_scc_extension_params.sps_palette_predictor_initializer
[comp]
[i], num_bits);
}
}
}
READ_UINT8 (&nr,
sps->sps_scc_extension_params.motion_vector_resolution_control_idc, 2);
READ_UINT8 (&nr,
sps->sps_scc_extension_params.intra_boundary_filtering_disabled_flag,
1);
}
done:
/* calculate ChromaArrayType */
if (!sps->separate_colour_plane_flag)
sps->chroma_array_type = sps->chroma_format_idc;
/* Calculate width and height */
sps->width = sps->pic_width_in_luma_samples;
sps->height = sps->pic_height_in_luma_samples;
if (sps->width < 0 || sps->height < 0) {
GST_WARNING ("invalid width/height in SPS");
goto error;
}
if (sps->conformance_window_flag) {
const guint crop_unit_x = subwc[sps->chroma_format_idc];
const guint crop_unit_y = subhc[sps->chroma_format_idc];
sps->crop_rect_width = sps->width -
(sps->conf_win_left_offset + sps->conf_win_right_offset) * crop_unit_x;
sps->crop_rect_height = sps->height -
(sps->conf_win_top_offset + sps->conf_win_bottom_offset) * crop_unit_y;
sps->crop_rect_x = sps->conf_win_left_offset * crop_unit_x;
sps->crop_rect_y = sps->conf_win_top_offset * crop_unit_y;
GST_LOG ("crop_rectangle x=%u y=%u width=%u, height=%u", sps->crop_rect_x,
sps->crop_rect_y, sps->crop_rect_width, sps->crop_rect_height);
}
sps->fps_num = 0;
sps->fps_den = 1;
sps->valid = TRUE;
return GST_H265_PARSER_OK;
error:
GST_WARNING ("error parsing \"Sequence parameter set\"");
sps->valid = FALSE;
return GST_H265_PARSER_ERROR;
}
/**
* gst_h265_parse_pps:
* @parser: a #GstH265Parser
* @nalu: The #GST_H265_NAL_PPS #GstH265NalUnit to parse
* @pps: The #GstH265PPS to fill.
*
* Parses @data, and fills the @pps structure.
*
* Returns: a #GstH265ParserResult
*/
GstH265ParserResult
gst_h265_parse_pps (GstH265Parser * parser, GstH265NalUnit * nalu,
GstH265PPS * pps)
{
guint32 MaxBitDepthY, MaxBitDepthC;
NalReader nr;
guint8 i;
GST_DEBUG ("parsing PPS");
nal_reader_init (&nr, nalu->data + nalu->offset + nalu->header_bytes,
nalu->size - nalu->header_bytes);
memset (pps, 0, sizeof (*pps));
READ_UE_MAX (&nr, pps->id, GST_H265_MAX_PPS_COUNT - 1);
READ_UE_MAX (&nr, pps->sps_id, GST_H265_MAX_SPS_COUNT - 1);
/* set default values for fields that might not be present in the bitstream
and have valid defaults */
pps->uniform_spacing_flag = 1;
pps->loop_filter_across_tiles_enabled_flag = 1;
READ_UINT8 (&nr, pps->dependent_slice_segments_enabled_flag, 1);
READ_UINT8 (&nr, pps->output_flag_present_flag, 1);
READ_UINT8 (&nr, pps->num_extra_slice_header_bits, 3);
READ_UINT8 (&nr, pps->sign_data_hiding_enabled_flag, 1);
READ_UINT8 (&nr, pps->cabac_init_present_flag, 1);
READ_UE_MAX (&nr, pps->num_ref_idx_l0_default_active_minus1, 14);
READ_UE_MAX (&nr, pps->num_ref_idx_l1_default_active_minus1, 14);
/* The value of init_qp_minus26 shall be in the range of
* ( 26 + QpBdOffsetY ) to +25, inclusive.
* QpBdOffsetY = 6 * bit_depth_luma_minus8 (7-5)
* and bit_depth_luma_minus8 shall be in the range of 0 to 8, inclusive.
* so the minimum possible value of init_qp_minus26 is -(26 + 6*8) */
READ_SE_ALLOWED (&nr, pps->init_qp_minus26, -(26 + 6 * 8), 25);
READ_UINT8 (&nr, pps->constrained_intra_pred_flag, 1);
READ_UINT8 (&nr, pps->transform_skip_enabled_flag, 1);
READ_UINT8 (&nr, pps->cu_qp_delta_enabled_flag, 1);
if (pps->cu_qp_delta_enabled_flag) {
READ_UE_MAX (&nr, pps->diff_cu_qp_delta_depth, 6);
}
READ_SE_ALLOWED (&nr, pps->cb_qp_offset, -12, 12);
READ_SE_ALLOWED (&nr, pps->cr_qp_offset, -12, 12);
READ_UINT8 (&nr, pps->slice_chroma_qp_offsets_present_flag, 1);
READ_UINT8 (&nr, pps->weighted_pred_flag, 1);
READ_UINT8 (&nr, pps->weighted_bipred_flag, 1);
READ_UINT8 (&nr, pps->transquant_bypass_enabled_flag, 1);
READ_UINT8 (&nr, pps->tiles_enabled_flag, 1);
READ_UINT8 (&nr, pps->entropy_coding_sync_enabled_flag, 1);
if (pps->tiles_enabled_flag) {
GstH265SPS *sps;
guint32 CtbSizeY, MinCbLog2SizeY, CtbLog2SizeY;
sps = gst_h265_parser_get_sps (parser, pps->sps_id);
if (!sps) {
GST_WARNING
("couldn't find associated sequence parameter set with id: %d",
pps->sps_id);
return GST_H265_PARSER_BROKEN_LINK;
}
MinCbLog2SizeY = sps->log2_min_luma_coding_block_size_minus3 + 3;
CtbLog2SizeY =
MinCbLog2SizeY + sps->log2_diff_max_min_luma_coding_block_size;
CtbSizeY = 1 << CtbLog2SizeY;
pps->PicHeightInCtbsY =
ceil ((gdouble) sps->pic_height_in_luma_samples / (gdouble) CtbSizeY);
pps->PicWidthInCtbsY =
ceil ((gdouble) sps->pic_width_in_luma_samples / (gdouble) CtbSizeY);
READ_UE_ALLOWED (&nr,
pps->num_tile_columns_minus1, 0, pps->PicWidthInCtbsY - 1);
READ_UE_ALLOWED (&nr,
pps->num_tile_rows_minus1, 0, pps->PicHeightInCtbsY - 1);
if (pps->num_tile_columns_minus1 + 1 >
G_N_ELEMENTS (pps->column_width_minus1)) {
GST_WARNING ("Invalid \"num_tile_columns_minus1\" %d",
pps->num_tile_columns_minus1);
goto error;
}
if (pps->num_tile_rows_minus1 + 1 > G_N_ELEMENTS (pps->row_height_minus1)) {
GST_WARNING ("Invalid \"num_tile_rows_minus1\" %d",
pps->num_tile_rows_minus1);
goto error;
}
READ_UINT8 (&nr, pps->uniform_spacing_flag, 1);
/* 6.5.1, 6-4, 6-5, 7.4.3.3.1 */
if (pps->uniform_spacing_flag) {
guint8 num_col = pps->num_tile_columns_minus1 + 1;
guint8 num_row = pps->num_tile_rows_minus1 + 1;
for (i = 0; i < num_col; i++) {
pps->column_width_minus1[i] =
((i + 1) * pps->PicWidthInCtbsY / num_col
- i * pps->PicWidthInCtbsY / num_col) - 1;
}
for (i = 0; i < num_row; i++) {
pps->row_height_minus1[i] =
((i + 1) * pps->PicHeightInCtbsY / num_row
- i * pps->PicHeightInCtbsY / num_row) - 1;
}
} else {
pps->column_width_minus1[pps->num_tile_columns_minus1] =
pps->PicWidthInCtbsY - 1;
for (i = 0; i < pps->num_tile_columns_minus1; i++) {
READ_UE (&nr, pps->column_width_minus1[i]);
pps->column_width_minus1[pps->num_tile_columns_minus1] -=
(pps->column_width_minus1[i] + 1);
}
pps->row_height_minus1[pps->num_tile_rows_minus1] =
pps->PicHeightInCtbsY - 1;
for (i = 0; i < pps->num_tile_rows_minus1; i++) {
READ_UE (&nr, pps->row_height_minus1[i]);
pps->row_height_minus1[pps->num_tile_rows_minus1] -=
(pps->row_height_minus1[i] + 1);
}
}
READ_UINT8 (&nr, pps->loop_filter_across_tiles_enabled_flag, 1);
}
READ_UINT8 (&nr, pps->loop_filter_across_slices_enabled_flag, 1);
READ_UINT8 (&nr, pps->deblocking_filter_control_present_flag, 1);
if (pps->deblocking_filter_control_present_flag) {
READ_UINT8 (&nr, pps->deblocking_filter_override_enabled_flag, 1);
READ_UINT8 (&nr, pps->deblocking_filter_disabled_flag, 1);
if (!pps->deblocking_filter_disabled_flag) {
READ_SE_ALLOWED (&nr, pps->beta_offset_div2, -6, 6);
READ_SE_ALLOWED (&nr, pps->tc_offset_div2, -6, +6);
}
}
READ_UINT8 (&nr, pps->scaling_list_data_present_flag, 1);
if (pps->scaling_list_data_present_flag)
if (!gst_h265_parser_parse_scaling_lists (&nr, &pps->scaling_list, FALSE))
goto error;
READ_UINT8 (&nr, pps->lists_modification_present_flag, 1);
READ_UE_MAX (&nr, pps->log2_parallel_merge_level_minus2, 4);
READ_UINT8 (&nr, pps->slice_segment_header_extension_present_flag, 1);
READ_UINT8 (&nr, pps->pps_extension_flag, 1);
if (pps->pps_extension_flag) {
READ_UINT8 (&nr, pps->pps_range_extension_flag, 1);
READ_UINT8 (&nr, pps->pps_multilayer_extension_flag, 1);
READ_UINT8 (&nr, pps->pps_3d_extension_flag, 1);
READ_UINT8 (&nr, pps->pps_scc_extension_flag, 1);
READ_UINT8 (&nr, pps->pps_extension_4bits, 4);
}
if (pps->pps_range_extension_flag) {
GstH265SPS *sps;
sps = gst_h265_parser_get_sps (parser, pps->sps_id);
if (!sps) {
GST_WARNING
("couldn't find associated sequence parameter set with id: %d",
pps->sps_id);
return GST_H265_PARSER_BROKEN_LINK;
}
if (pps->transform_skip_enabled_flag)
READ_UE (&nr,
pps->pps_extension_params.log2_max_transform_skip_block_size_minus2);
READ_UINT8 (&nr,
pps->pps_extension_params.cross_component_prediction_enabled_flag, 1);
READ_UINT8 (&nr,
pps->pps_extension_params.chroma_qp_offset_list_enabled_flag, 1);
if (pps->pps_extension_params.chroma_qp_offset_list_enabled_flag) {
READ_UE_MAX (&nr,
pps->pps_extension_params.diff_cu_chroma_qp_offset_depth,
sps->log2_diff_max_min_luma_coding_block_size);
READ_UE_MAX (&nr,
pps->pps_extension_params.chroma_qp_offset_list_len_minus1, 5);
for (i = 0;
i <= pps->pps_extension_params.chroma_qp_offset_list_len_minus1;
i++) {
READ_SE_ALLOWED (&nr, pps->pps_extension_params.cb_qp_offset_list[i],
-12, 12);
READ_SE_ALLOWED (&nr, pps->pps_extension_params.cr_qp_offset_list[i],
-12, 12);
}
}
MaxBitDepthY =
sps->bit_depth_luma_minus8 > 2 ? sps->bit_depth_luma_minus8 - 2 : 0;
MaxBitDepthC =
sps->bit_depth_chroma_minus8 > 2 ? sps->bit_depth_chroma_minus8 - 2 : 0;
READ_UE_ALLOWED (&nr, pps->pps_extension_params.log2_sao_offset_scale_luma,
0, MaxBitDepthY);
READ_UE_ALLOWED (&nr,
pps->pps_extension_params.log2_sao_offset_scale_chroma, 0,
MaxBitDepthC);
}
if (pps->pps_multilayer_extension_flag) {
GST_WARNING ("do not support multilayer extension, skip all"
" remaining bits");
goto done;
}
if (pps->pps_3d_extension_flag) {
GST_WARNING ("do not support 3d extension, skip all remaining bits");
goto done;
}
if (pps->pps_scc_extension_flag) {
GstH265SPS *sps;
sps = gst_h265_parser_get_sps (parser, pps->sps_id);
if (!sps) {
GST_WARNING
("couldn't find associated sequence parameter set with id: %d",
pps->sps_id);
return GST_H265_PARSER_BROKEN_LINK;
}
READ_UINT8 (&nr,
pps->pps_scc_extension_params.pps_curr_pic_ref_enabled_flag, 1);
READ_UINT8 (&nr,
pps->
pps_scc_extension_params.residual_adaptive_colour_transform_enabled_flag,
1);
if (pps->
pps_scc_extension_params.residual_adaptive_colour_transform_enabled_flag)
{
READ_UINT8 (&nr,
pps->pps_scc_extension_params.pps_slice_act_qp_offsets_present_flag,
1);
READ_SE_ALLOWED (&nr,
pps->pps_scc_extension_params.pps_act_y_qp_offset_plus5, -7, 17);
READ_SE_ALLOWED (&nr,
pps->pps_scc_extension_params.pps_act_cb_qp_offset_plus5, -7, 17);
READ_SE_ALLOWED (&nr,
pps->pps_scc_extension_params.pps_act_cr_qp_offset_plus3, -9, 15);
}
READ_UINT8 (&nr,
pps->
pps_scc_extension_params.pps_palette_predictor_initializers_present_flag,
1);
if (pps->
pps_scc_extension_params.pps_palette_predictor_initializers_present_flag)
{
READ_UE_MAX (&nr,
pps->pps_scc_extension_params.pps_num_palette_predictor_initializer,
sps->sps_scc_extension_params.palette_max_size +
sps->sps_scc_extension_params.delta_palette_max_predictor_size);
if (pps->pps_scc_extension_params.pps_num_palette_predictor_initializer >
0) {
guint comp;
READ_UINT8 (&nr, pps->pps_scc_extension_params.monochrome_palette_flag,
1);
/* It is a requirement of bitstream conformance that the value of
luma_bit_depth_entry_minus8 shall be equal to the value of
bit_depth_luma_minus8 */
READ_UE_ALLOWED (&nr,
pps->pps_scc_extension_params.luma_bit_depth_entry_minus8,
sps->bit_depth_luma_minus8, sps->bit_depth_luma_minus8);
if (!pps->pps_scc_extension_params.monochrome_palette_flag) {
/* It is a requirement of bitstream conformance that the value
of chroma_bit_depth_entry_minus8 shall be equal to the value
of bit_depth_chroma_minus8. */
READ_UE_ALLOWED (&nr,
pps->pps_scc_extension_params.chroma_bit_depth_entry_minus8,
sps->bit_depth_chroma_minus8, sps->bit_depth_chroma_minus8);
}
for (comp = 0; comp <
(pps->pps_scc_extension_params.monochrome_palette_flag ? 1 : 3);
comp++) {
guint num_bits;
guint num =
pps->
pps_scc_extension_params.pps_num_palette_predictor_initializer;
num_bits = (comp == 0 ?
pps->pps_scc_extension_params.luma_bit_depth_entry_minus8 + 8 :
pps->pps_scc_extension_params.chroma_bit_depth_entry_minus8 + 8);
for (i = 0; i < num; i++)
READ_UINT32 (&nr,
pps->pps_scc_extension_params.pps_palette_predictor_initializer
[comp][i], num_bits);
}
}
}
}
done:
pps->valid = TRUE;
return GST_H265_PARSER_OK;
error:
GST_WARNING ("error parsing \"Picture parameter set\"");
pps->valid = FALSE;
return GST_H265_PARSER_ERROR;
}
/**
* gst_h265_parser_parse_pps:
* @parser: a #GstH265Parser
* @nalu: The #GST_H265_NAL_PPS #GstH265NalUnit to parse
* @pps: The #GstH265PPS to fill.
*
* Parses @data, and fills the @pps structure.
*
* Returns: a #GstH265ParserResult
*/
GstH265ParserResult
gst_h265_parser_parse_pps (GstH265Parser * parser,
GstH265NalUnit * nalu, GstH265PPS * pps)
{
GstH265ParserResult res = gst_h265_parse_pps (parser, nalu, pps);
if (res == GST_H265_PARSER_OK) {
GST_DEBUG ("adding picture parameter set with id: %d to array", pps->id);
parser->pps[pps->id] = *pps;
parser->last_pps = &parser->pps[pps->id];
}
return res;
}
static GstH265ParserResult
gst_h265_parser_fill_sps (GstH265Parser * parser, GstH265SPS * sps)
{
GstH265VPS *vps;
GstH265VUIParams *vui = &sps->vui_params;
GstH265ParserResult ret = GST_H265_PARSER_OK;
vps = gst_h265_parser_get_vps (parser, sps->vps_id);
if (!vps) {
GST_DEBUG ("couldn't find associated video parameter set with id: %d",
sps->vps_id);
return GST_H265_PARSER_BROKEN_LINK;
}
sps->vps = vps;
if (vui && vui->timing_info_present_flag) {
/* derive framerate for progressive stream if the pic_struct
* syntax element is not present in picture timing SEI messages */
/* Fixme: handle other cases also */
if (vui->parsed && vui->timing_info_present_flag
&& !vui->field_seq_flag && !vui->frame_field_info_present_flag) {
sps->fps_num = vui->time_scale;
sps->fps_den = vui->num_units_in_tick;
GST_LOG ("framerate %d/%d in VUI", sps->fps_num, sps->fps_den);
}
} else if (vps && vps->timing_info_present_flag) {
sps->fps_num = vps->time_scale;
sps->fps_den = vps->num_units_in_tick;
GST_LOG ("framerate %d/%d in VPS", sps->fps_num, sps->fps_den);
} else {
GST_LOG ("No VUI, unknown framerate");
}
return ret;
}
static GstH265ParserResult
gst_h265_parser_fill_pps (GstH265Parser * parser, GstH265PPS * pps)
{
GstH265SPS *sps;
gint qp_bd_offset;
guint32 CtbSizeY, MinCbLog2SizeY, CtbLog2SizeY;
GstH265ParserResult ret = GST_H265_PARSER_OK;
sps = gst_h265_parser_get_sps (parser, pps->sps_id);
if (!sps) {
GST_WARNING ("couldn't find associated sequence parameter set with id: %d",
pps->sps_id);
return GST_H265_PARSER_BROKEN_LINK;
}
ret = gst_h265_parser_fill_sps (parser, sps);
if (ret != GST_H265_PARSER_OK) {
GST_WARNING ("couldn't fill sps id: %d", pps->sps_id);
return ret;
}
pps->sps = sps;
qp_bd_offset = 6 * sps->bit_depth_luma_minus8;
MinCbLog2SizeY = sps->log2_min_luma_coding_block_size_minus3 + 3;
CtbLog2SizeY = MinCbLog2SizeY + sps->log2_diff_max_min_luma_coding_block_size;
CtbSizeY = 1 << CtbLog2SizeY;
pps->PicHeightInCtbsY =
ceil ((gdouble) sps->pic_height_in_luma_samples / (gdouble) CtbSizeY);
pps->PicWidthInCtbsY =
ceil ((gdouble) sps->pic_width_in_luma_samples / (gdouble) CtbSizeY);
if (pps->init_qp_minus26 < -(26 + qp_bd_offset))
return GST_H265_PARSER_BROKEN_LINK;
if (sps->scaling_list_enabled_flag && !sps->scaling_list_data_present_flag
&& !pps->scaling_list_data_present_flag)
if (!gst_h265_parser_parse_scaling_lists (NULL, &pps->scaling_list, TRUE))
return GST_H265_PARSER_BROKEN_LINK;
if (pps->cu_qp_delta_enabled_flag)
if (pps->diff_cu_qp_delta_depth >
sps->log2_diff_max_min_luma_coding_block_size)
return GST_H265_PARSER_BROKEN_LINK;
return ret;
}
/**
* gst_h265_parser_parse_slice_hdr:
* @parser: a #GstH265Parser
* @nalu: The `GST_H265_NAL_SLICE` #GstH265NalUnit to parse
* @slice: The #GstH265SliceHdr to fill.
*
* Parses @data, and fills the @slice structure.
* The resulting @slice_hdr structure shall be deallocated with
* gst_h265_slice_hdr_free() when it is no longer needed
*
* Returns: a #GstH265ParserResult
*/
GstH265ParserResult
gst_h265_parser_parse_slice_hdr (GstH265Parser * parser,
GstH265NalUnit * nalu, GstH265SliceHdr * slice)
{
NalReader nr;
gint pps_id;
GstH265PPS *pps;
GstH265SPS *sps;
guint i;
GstH265ShortTermRefPicSet *stRPS = NULL;
guint32 UsedByCurrPicLt[16];
guint32 PicSizeInCtbsY;
gint NumPocTotalCurr = 0;
GstH265ParserResult err;
memset (slice, 0, sizeof (*slice));
if (!nalu->size) {
GST_DEBUG ("Invalid Nal Unit");
return GST_H265_PARSER_ERROR;
}
nal_reader_init (&nr, nalu->data + nalu->offset + nalu->header_bytes,
nalu->size - nalu->header_bytes);
GST_DEBUG ("parsing \"Slice header\", slice type");
READ_UINT8 (&nr, slice->first_slice_segment_in_pic_flag, 1);
if (GST_H265_IS_NAL_TYPE_IRAP (nalu->type))
READ_UINT8 (&nr, slice->no_output_of_prior_pics_flag, 1);
READ_UE_MAX (&nr, pps_id, GST_H265_MAX_PPS_COUNT - 1);
pps = gst_h265_parser_get_pps (parser, pps_id);
if (!pps) {
GST_WARNING
("couldn't find associated picture parameter set with id: %d", pps_id);
return GST_H265_PARSER_BROKEN_LINK;
}
err = gst_h265_parser_fill_pps (parser, pps);
if (err != GST_H265_PARSER_OK) {
GST_WARNING ("couldn't fill pps id: %d", pps_id);
return err;
}
slice->pps = pps;
sps = pps->sps;
if (!sps) {
GST_WARNING
("couldn't find associated sequence parameter set with id: %d",
pps->id);
return GST_H265_PARSER_BROKEN_LINK;
}
PicSizeInCtbsY = pps->PicWidthInCtbsY * pps->PicHeightInCtbsY;
/* set default values for fields that might not be present in the bitstream
* and have valid defaults */
slice->pic_output_flag = 1;
slice->collocated_from_l0_flag = 1;
slice->deblocking_filter_disabled_flag = pps->deblocking_filter_disabled_flag;
slice->beta_offset_div2 = pps->beta_offset_div2;
slice->tc_offset_div2 = pps->tc_offset_div2;
slice->loop_filter_across_slices_enabled_flag =
pps->loop_filter_across_slices_enabled_flag;
if (!slice->first_slice_segment_in_pic_flag) {
const guint n = ceil_log2 (PicSizeInCtbsY);
if (pps->dependent_slice_segments_enabled_flag)
READ_UINT8 (&nr, slice->dependent_slice_segment_flag, 1);
/* sice_segment_address parsing */
READ_UINT32 (&nr, slice->segment_address, n);
}
if (!slice->dependent_slice_segment_flag) {
for (i = 0; i < pps->num_extra_slice_header_bits; i++) {
if (!nal_reader_skip (&nr, 1))
goto error;
}
READ_UE_MAX (&nr, slice->type, 63);
if (pps->output_flag_present_flag)
READ_UINT8 (&nr, slice->pic_output_flag, 1);
if (sps->separate_colour_plane_flag == 1)
READ_UINT8 (&nr, slice->colour_plane_id, 2);
if (!GST_H265_IS_NAL_TYPE_IDR (nalu->type)) {
READ_UINT16 (&nr, slice->pic_order_cnt_lsb,
(sps->log2_max_pic_order_cnt_lsb_minus4 + 4));
READ_UINT8 (&nr, slice->short_term_ref_pic_set_sps_flag, 1);
if (!slice->short_term_ref_pic_set_sps_flag) {
guint pos = nal_reader_get_pos (&nr);
guint epb_pos = nal_reader_get_epb_count (&nr);
if (!gst_h265_parser_parse_short_term_ref_pic_sets
(&slice->short_term_ref_pic_sets, &nr,
sps->num_short_term_ref_pic_sets, sps))
goto error;
slice->short_term_ref_pic_set_size =
(nal_reader_get_pos (&nr) - pos) -
(8 * (nal_reader_get_epb_count (&nr) - epb_pos));
} else if (sps->num_short_term_ref_pic_sets > 1) {
const guint n = ceil_log2 (sps->num_short_term_ref_pic_sets);
READ_UINT8 (&nr, slice->short_term_ref_pic_set_idx, n);
CHECK_ALLOWED_MAX (slice->short_term_ref_pic_set_idx,
sps->num_short_term_ref_pic_sets - 1);
}
if (sps->long_term_ref_pics_present_flag) {
guint32 limit;
guint pos = nal_reader_get_pos (&nr);
guint epb_pos = nal_reader_get_epb_count (&nr);
if (sps->num_long_term_ref_pics_sps > 0)
READ_UE_MAX (&nr, slice->num_long_term_sps,
sps->num_long_term_ref_pics_sps);
READ_UE_MAX (&nr, slice->num_long_term_pics, 16);
limit = slice->num_long_term_sps + slice->num_long_term_pics;
for (i = 0; i < limit; i++) {
if (i < slice->num_long_term_sps) {
if (sps->num_long_term_ref_pics_sps > 1) {
const guint n = ceil_log2 (sps->num_long_term_ref_pics_sps);
READ_UINT8 (&nr, slice->lt_idx_sps[i], n);
}
} else {
READ_UINT32 (&nr, slice->poc_lsb_lt[i],
(sps->log2_max_pic_order_cnt_lsb_minus4 + 4));
READ_UINT8 (&nr, slice->used_by_curr_pic_lt_flag[i], 1);
}
/* calculate UsedByCurrPicLt */
if (i < slice->num_long_term_sps)
UsedByCurrPicLt[i] =
sps->used_by_curr_pic_lt_sps_flag[slice->lt_idx_sps[i]];
else
UsedByCurrPicLt[i] = slice->used_by_curr_pic_lt_flag[i];
READ_UINT8 (&nr, slice->delta_poc_msb_present_flag[i], 1);
if (slice->delta_poc_msb_present_flag[i])
READ_UE (&nr, slice->delta_poc_msb_cycle_lt[i]);
}
slice->long_term_ref_pic_set_size =
(nal_reader_get_pos (&nr) - pos) -
(8 * (nal_reader_get_epb_count (&nr) - epb_pos));
}
if (sps->temporal_mvp_enabled_flag)
READ_UINT8 (&nr, slice->temporal_mvp_enabled_flag, 1);
}
if (sps->sample_adaptive_offset_enabled_flag) {
READ_UINT8 (&nr, slice->sao_luma_flag, 1);
if (sps->chroma_array_type)
READ_UINT8 (&nr, slice->sao_chroma_flag, 1);
}
if (GST_H265_IS_B_SLICE (slice) || GST_H265_IS_P_SLICE (slice)) {
READ_UINT8 (&nr, slice->num_ref_idx_active_override_flag, 1);
if (slice->num_ref_idx_active_override_flag) {
READ_UE_MAX (&nr, slice->num_ref_idx_l0_active_minus1, 14);
if (GST_H265_IS_B_SLICE (slice))
READ_UE_MAX (&nr, slice->num_ref_idx_l1_active_minus1, 14);
} else {
/*set default values */
slice->num_ref_idx_l0_active_minus1 =
pps->num_ref_idx_l0_default_active_minus1;
slice->num_ref_idx_l1_active_minus1 =
pps->num_ref_idx_l1_default_active_minus1;
}
/* calculate NumPocTotalCurr */
if (slice->short_term_ref_pic_set_sps_flag)
stRPS = &sps->short_term_ref_pic_set[slice->short_term_ref_pic_set_idx];
else
stRPS = &slice->short_term_ref_pic_sets;
for (i = 0; i < stRPS->NumNegativePics; i++)
if (stRPS->UsedByCurrPicS0[i])
NumPocTotalCurr++;
for (i = 0; i < stRPS->NumPositivePics; i++)
if (stRPS->UsedByCurrPicS1[i])
NumPocTotalCurr++;
for (i = 0;
i < (slice->num_long_term_sps + slice->num_long_term_pics); i++)
if (UsedByCurrPicLt[i])
NumPocTotalCurr++;
slice->NumPocTotalCurr = NumPocTotalCurr;
if (pps->lists_modification_present_flag) {
if (NumPocTotalCurr > 1)
if (!gst_h265_slice_parse_ref_pic_list_modification (slice, &nr,
NumPocTotalCurr))
goto error;
}
if (GST_H265_IS_B_SLICE (slice))
READ_UINT8 (&nr, slice->mvd_l1_zero_flag, 1);
if (pps->cabac_init_present_flag)
READ_UINT8 (&nr, slice->cabac_init_flag, 1);
if (slice->temporal_mvp_enabled_flag) {
if (GST_H265_IS_B_SLICE (slice))
READ_UINT8 (&nr, slice->collocated_from_l0_flag, 1);
if ((slice->collocated_from_l0_flag
&& slice->num_ref_idx_l0_active_minus1 > 0)
|| (!slice->collocated_from_l0_flag
&& slice->num_ref_idx_l1_active_minus1 > 0)) {
/*fixme: add optimization */
if ((GST_H265_IS_P_SLICE (slice))
|| ((GST_H265_IS_B_SLICE (slice))
&& (slice->collocated_from_l0_flag))) {
READ_UE_MAX (&nr, slice->collocated_ref_idx,
slice->num_ref_idx_l0_active_minus1);
} else if ((GST_H265_IS_B_SLICE (slice))
&& (!slice->collocated_from_l0_flag)) {
READ_UE_MAX (&nr, slice->collocated_ref_idx,
slice->num_ref_idx_l1_active_minus1);
}
}
}
if ((pps->weighted_pred_flag && GST_H265_IS_P_SLICE (slice)) ||
(pps->weighted_bipred_flag && GST_H265_IS_B_SLICE (slice)))
if (!gst_h265_slice_parse_pred_weight_table (slice, &nr))
goto error;
READ_UE_MAX (&nr, slice->five_minus_max_num_merge_cand, 4);
if (sps->sps_scc_extension_params.motion_vector_resolution_control_idc
== 2)
READ_UINT8 (&nr, slice->use_integer_mv_flag, 1);
}
READ_SE_ALLOWED (&nr, slice->qp_delta, -87, 77);
if (pps->slice_chroma_qp_offsets_present_flag) {
READ_SE_ALLOWED (&nr, slice->cb_qp_offset, -12, 12);
READ_SE_ALLOWED (&nr, slice->cr_qp_offset, -12, 12);
}
if (pps->pps_scc_extension_params.pps_slice_act_qp_offsets_present_flag) {
READ_SE_ALLOWED (&nr, slice->slice_act_y_qp_offset, -12, 12);
READ_SE_ALLOWED (&nr, slice->slice_act_cb_qp_offset, -12, 12);
READ_SE_ALLOWED (&nr, slice->slice_act_cr_qp_offset, -12, 12);
}
if (pps->pps_extension_params.chroma_qp_offset_list_enabled_flag)
READ_UINT8 (&nr, slice->cu_chroma_qp_offset_enabled_flag, 1);
if (pps->deblocking_filter_override_enabled_flag)
READ_UINT8 (&nr, slice->deblocking_filter_override_flag, 1);
if (slice->deblocking_filter_override_flag) {
READ_UINT8 (&nr, slice->deblocking_filter_disabled_flag, 1);
if (!slice->deblocking_filter_disabled_flag) {
READ_SE_ALLOWED (&nr, slice->beta_offset_div2, -6, 6);
READ_SE_ALLOWED (&nr, slice->tc_offset_div2, -6, 6);
}
}
if (pps->loop_filter_across_slices_enabled_flag &&
(slice->sao_luma_flag || slice->sao_chroma_flag ||
!slice->deblocking_filter_disabled_flag))
READ_UINT8 (&nr, slice->loop_filter_across_slices_enabled_flag, 1);
}
if (pps->tiles_enabled_flag || pps->entropy_coding_sync_enabled_flag) {
guint32 offset_max;
if (!pps->tiles_enabled_flag && pps->entropy_coding_sync_enabled_flag)
offset_max = pps->PicHeightInCtbsY - 1;
else if (pps->tiles_enabled_flag && !pps->entropy_coding_sync_enabled_flag)
offset_max =
(pps->num_tile_columns_minus1 + 1) * (pps->num_tile_rows_minus1 + 1) -
1;
else
offset_max =
(pps->num_tile_columns_minus1 + 1) * pps->PicHeightInCtbsY - 1;
READ_UE_MAX (&nr, slice->num_entry_point_offsets, offset_max);
if (slice->num_entry_point_offsets > 0) {
READ_UE_MAX (&nr, slice->offset_len_minus1, 31);
slice->entry_point_offset_minus1 =
g_new0 (guint32, slice->num_entry_point_offsets);
for (i = 0; i < slice->num_entry_point_offsets; i++)
READ_UINT32 (&nr, slice->entry_point_offset_minus1[i],
(slice->offset_len_minus1 + 1));
}
}
if (pps->slice_segment_header_extension_present_flag) {
guint16 slice_segment_header_extension_length;
READ_UE_MAX (&nr, slice_segment_header_extension_length, 256);
for (i = 0; i < slice_segment_header_extension_length; i++)
if (!nal_reader_skip (&nr, 8))
goto error;
}
/* Skip the byte alignment bits */
if (!nal_reader_skip (&nr, 1))
goto error;
while (!nal_reader_is_byte_aligned (&nr)) {
if (!nal_reader_skip (&nr, 1))
goto error;
}
slice->header_size = nal_reader_get_pos (&nr);
slice->n_emulation_prevention_bytes = nal_reader_get_epb_count (&nr);
return GST_H265_PARSER_OK;
error:
GST_WARNING ("error parsing \"Slice header\"");
gst_h265_slice_hdr_free (slice);
return GST_H265_PARSER_ERROR;
}
static gboolean
nal_reader_has_more_data_in_payload (NalReader * nr,
guint32 payload_start_pos_bit, guint32 payloadSize)
{
if (nal_reader_is_byte_aligned (nr) &&
(nal_reader_get_pos (nr) >= (payload_start_pos_bit + 8 * payloadSize)))
return FALSE;
return TRUE;
}
static GstH265ParserResult
gst_h265_parser_parse_sei_message (GstH265Parser * parser,
guint8 nal_type, NalReader * nr, GstH265SEIMessage * sei)
{
guint32 payloadSize;
guint8 payload_type_byte, payload_size_byte;
guint remaining, payload_size;
guint32 payload_start_pos_bit;
GstH265ParserResult res = GST_H265_PARSER_OK;
GST_DEBUG ("parsing \"Sei message\"");
memset (sei, 0, sizeof (*sei));
do {
READ_UINT8 (nr, payload_type_byte, 8);
sei->payloadType += payload_type_byte;
} while (payload_type_byte == 0xff);
payloadSize = 0;
do {
READ_UINT8 (nr, payload_size_byte, 8);
payloadSize += payload_size_byte;
}
while (payload_size_byte == 0xff);
remaining = nal_reader_get_remaining (nr);
payload_size = payloadSize * 8 < remaining ? payloadSize * 8 : remaining;
payload_start_pos_bit = nal_reader_get_pos (nr);
GST_DEBUG
("SEI message received: payloadType %u, payloadSize = %u bits",
sei->payloadType, payload_size);
if (nal_type == GST_H265_NAL_PREFIX_SEI) {
switch (sei->payloadType) {
case GST_H265_SEI_BUF_PERIOD:
/* size not set; might depend on emulation_prevention_three_byte */
res = gst_h265_parser_parse_buffering_period (parser,
&sei->payload.buffering_period, nr);
break;
case GST_H265_SEI_PIC_TIMING:
/* size not set; might depend on emulation_prevention_three_byte */
res = gst_h265_parser_parse_pic_timing (parser,
&sei->payload.pic_timing, nr);
break;
case GST_H265_SEI_REGISTERED_USER_DATA:
res = gst_h265_parser_parse_registered_user_data (parser,
&sei->payload.registered_user_data, nr, payload_size >> 3);
break;
case GST_H265_SEI_USER_DATA_UNREGISTERED:
res = gst_h265_parser_parse_user_data_unregistered (parser,
&sei->payload.user_data_unregistered, nr, payload_size >> 3);
break;
case GST_H265_SEI_RECOVERY_POINT:
res = gst_h265_parser_parse_recovery_point (parser,
&sei->payload.recovery_point, nr);
break;
case GST_H265_SEI_TIME_CODE:
res = gst_h265_parser_parse_time_code (parser,
&sei->payload.time_code, nr);
break;
case GST_H265_SEI_MASTERING_DISPLAY_COLOUR_VOLUME:
res = gst_h265_parser_parse_mastering_display_colour_volume (parser,
&sei->payload.mastering_display_colour_volume, nr);
break;
case GST_H265_SEI_CONTENT_LIGHT_LEVEL:
res = gst_h265_parser_parse_content_light_level_info (parser,
&sei->payload.content_light_level, nr);
break;
default:
/* Just consume payloadSize bytes, which does not account for
emulation prevention bytes */
if (!nal_reader_skip_long (nr, payload_size))
goto error;
res = GST_H265_PARSER_OK;
break;
}
} else if (nal_type == GST_H265_NAL_SUFFIX_SEI) {
switch (sei->payloadType) {
default:
/* Just consume payloadSize bytes, which does not account for
emulation prevention bytes */
if (!nal_reader_skip_long (nr, payload_size))
goto error;
res = GST_H265_PARSER_OK;
break;
}
}
/* Not parsing the reserved_payload_extension, but it shouldn't be
* an issue because of 1: There shall not be any reserved_payload_extension
* present in bitstreams conforming to the specification.2. Even though
* it is present, the size will be less than total PayloadSize since the
* size of reserved_payload_extension is supposed to be
* 8 * payloadSize - nEarlierBits - nPayloadZeroBits -1 which means the
* the current implementation will still skip all unnecessary bits correctly.
* In theory, we can have a more optimized implementation by skipping the
* data left in PayLoadSize without out individually checking for each bits,
* since the totoal size will be always less than payloadSize*/
while (nal_reader_has_more_data_in_payload (nr, payload_start_pos_bit,
payloadSize)) {
/* Skip the byte alignment bits */
if (!nal_reader_skip (nr, 1))
goto error;
while (!nal_reader_is_byte_aligned (nr)) {
if (!nal_reader_skip (nr, 1))
goto error;
}
}
return res;
error:
GST_WARNING ("error parsing \"Sei message\"");
return GST_H265_PARSER_ERROR;
}
/**
* gst_h265_slice_hdr_copy:
* @dst_slice: The destination #GstH265SliceHdr to copy into
* @src_slice: The source #GstH265SliceHdr to copy from
*
* Copies @src_slice into @dst_slice
*
* Returns: %TRUE if everything went fine, %FALSE otherwise
*/
gboolean
gst_h265_slice_hdr_copy (GstH265SliceHdr * dst_slice,
const GstH265SliceHdr * src_slice)
{
guint i;
g_return_val_if_fail (dst_slice != NULL, FALSE);
g_return_val_if_fail (src_slice != NULL, FALSE);
gst_h265_slice_hdr_free (dst_slice);
*dst_slice = *src_slice;
if (dst_slice->num_entry_point_offsets > 0) {
dst_slice->entry_point_offset_minus1 =
g_new0 (guint32, dst_slice->num_entry_point_offsets);
for (i = 0; i < dst_slice->num_entry_point_offsets; i++)
dst_slice->entry_point_offset_minus1[i] =
src_slice->entry_point_offset_minus1[i];
}
return TRUE;
}
/**
* gst_h265_slice_hdr_free:
* slice_hdr: The #GstH265SliceHdr to free
*
* Frees @slice_hdr fields.
*/
void
gst_h265_slice_hdr_free (GstH265SliceHdr * slice_hdr)
{
g_return_if_fail (slice_hdr != NULL);
if (slice_hdr->num_entry_point_offsets > 0)
g_free (slice_hdr->entry_point_offset_minus1);
slice_hdr->entry_point_offset_minus1 = 0;
}
/**
* gst_h265_sei_copy:
* @dst_sei: The destination #GstH265SEIMessage to copy into
* @src_sei: The source #GstH265SEIMessage to copy from
*
* Copies @src_sei into @dst_sei
*
* Returns: %TRUE if everything went fine, %FALSE otherwise
*/
gboolean
gst_h265_sei_copy (GstH265SEIMessage * dst_sei,
const GstH265SEIMessage * src_sei)
{
guint i;
g_return_val_if_fail (dst_sei != NULL, FALSE);
g_return_val_if_fail (src_sei != NULL, FALSE);
gst_h265_sei_free (dst_sei);
*dst_sei = *src_sei;
if (dst_sei->payloadType == GST_H265_SEI_PIC_TIMING) {
GstH265PicTiming *dst_pic_timing = &dst_sei->payload.pic_timing;
const GstH265PicTiming *src_pic_timing = &src_sei->payload.pic_timing;
if (dst_pic_timing->num_decoding_units_minus1 > 0) {
dst_pic_timing->num_nalus_in_du_minus1 =
g_new0 (guint32, (dst_pic_timing->num_decoding_units_minus1 + 1));
dst_pic_timing->du_cpb_removal_delay_increment_minus1 =
g_new0 (guint8, (dst_pic_timing->num_decoding_units_minus1 + 1));
for (i = 0; i <= dst_pic_timing->num_decoding_units_minus1; i++) {
dst_pic_timing->num_nalus_in_du_minus1[i] =
src_pic_timing->num_nalus_in_du_minus1[i];
dst_pic_timing->du_cpb_removal_delay_increment_minus1[i] =
src_pic_timing->du_cpb_removal_delay_increment_minus1[i];
}
}
} else if (dst_sei->payloadType == GST_H265_SEI_REGISTERED_USER_DATA) {
GstH265RegisteredUserData *dst_rud = &dst_sei->payload.registered_user_data;
const GstH265RegisteredUserData *src_rud =
&src_sei->payload.registered_user_data;
if (src_rud->size) {
dst_rud->data = g_malloc (src_rud->size);
memcpy ((guint8 *) dst_rud->data, src_rud->data, src_rud->size);
}
} else if (dst_sei->payloadType == GST_H265_SEI_USER_DATA_UNREGISTERED) {
GstH265UserDataUnregistered *dst_udu =
&dst_sei->payload.user_data_unregistered;
const GstH265UserDataUnregistered *src_udu =
&src_sei->payload.user_data_unregistered;
if (src_udu->size) {
dst_udu->data = g_malloc (src_udu->size);
memcpy ((guint8 *) dst_udu->data, src_udu->data, src_udu->size);
}
}
return TRUE;
}
/**
* gst_h265_sei_free:
* sei: The #GstH265SEIMessage to free
*
* Frees @sei fields.
*/
void
gst_h265_sei_free (GstH265SEIMessage * sei)
{
g_return_if_fail (sei != NULL);
if (sei->payloadType == GST_H265_SEI_PIC_TIMING) {
GstH265PicTiming *pic_timing = &sei->payload.pic_timing;
if (pic_timing->num_decoding_units_minus1 > 0) {
g_free (pic_timing->num_nalus_in_du_minus1);
g_free (pic_timing->du_cpb_removal_delay_increment_minus1);
}
pic_timing->num_nalus_in_du_minus1 = 0;
pic_timing->du_cpb_removal_delay_increment_minus1 = 0;
} else if (sei->payloadType == GST_H265_SEI_REGISTERED_USER_DATA) {
GstH265RegisteredUserData *rud = &sei->payload.registered_user_data;
g_free ((guint8 *) rud->data);
rud->data = NULL;
} else if (sei->payloadType == GST_H265_SEI_USER_DATA_UNREGISTERED) {
GstH265UserDataUnregistered *udu = &sei->payload.user_data_unregistered;
g_free ((guint8 *) udu->data);
udu->data = NULL;
}
}
/**
* gst_h265_parser_parse_sei:
* @nalparser: a #GstH265Parser
* @nalu: The `GST_H265_NAL_*_SEI` #GstH265NalUnit to parse
* @messages: The GArray of #GstH265SEIMessage to fill. The caller must free it when done.
*
* Parses @data, create and fills the @messages array.
*
* Returns: a #GstH265ParserResult
*/
GstH265ParserResult
gst_h265_parser_parse_sei (GstH265Parser * nalparser, GstH265NalUnit * nalu,
GArray ** messages)
{
NalReader nr;
GstH265SEIMessage sei;
GstH265ParserResult res;
GST_DEBUG ("parsing SEI nal");
nal_reader_init (&nr, nalu->data + nalu->offset + nalu->header_bytes,
nalu->size - nalu->header_bytes);
*messages = g_array_new (FALSE, FALSE, sizeof (GstH265SEIMessage));
g_array_set_clear_func (*messages, (GDestroyNotify) gst_h265_sei_free);
do {
res = gst_h265_parser_parse_sei_message (nalparser, nalu->type, &nr, &sei);
if (res == GST_H265_PARSER_OK)
g_array_append_val (*messages, sei);
else
break;
} while (nal_reader_has_more_data (&nr));
return res;
}
/**
* gst_h265_parser_update_vps:
* @parser: a #GstH265Parser
* @vps: (transfer none): a #GstH265VPS.
*
* Replace internal Video Parameter Set struct corresponding to id of @vps
* with @vps. @nalparser will mark @vps as last parsed vps.
*
* Returns: a #GstH265ParserResult
*
* Since: 1.18
*/
GstH265ParserResult
gst_h265_parser_update_vps (GstH265Parser * parser, GstH265VPS * vps)
{
g_return_val_if_fail (parser != NULL, GST_H265_PARSER_ERROR);
g_return_val_if_fail (vps != NULL, GST_H265_PARSER_ERROR);
g_return_val_if_fail (vps->id < GST_H265_MAX_VPS_COUNT,
GST_H265_PARSER_ERROR);
if (!vps->valid) {
GST_WARNING ("Cannot update with invalid VPS");
return GST_H265_PARSER_ERROR;
}
GST_DEBUG ("Updating video parameter set with id: %d", vps->id);
parser->vps[vps->id] = *vps;
parser->last_vps = &parser->vps[vps->id];
return GST_H265_PARSER_OK;
}
/**
* gst_h265_parser_update_sps:
* @parser: a #GstH265Parser
* @sps: (transfer none): a #GstH265SPS.
*
* Replace internal Sequence Parameter Set struct corresponding to id of @sps
* with @sps. @nalparser will mark @sps as last parsed sps.
*
* Returns: a #GstH265ParserResult
*
* Since: 1.18
*/
GstH265ParserResult
gst_h265_parser_update_sps (GstH265Parser * parser, GstH265SPS * sps)
{
g_return_val_if_fail (parser != NULL, GST_H265_PARSER_ERROR);
g_return_val_if_fail (sps != NULL, GST_H265_PARSER_ERROR);
g_return_val_if_fail (sps->id < GST_H265_MAX_SPS_COUNT,
GST_H265_PARSER_ERROR);
if (!sps->valid) {
GST_WARNING ("Cannot update with invalid SPS");
return GST_H265_PARSER_ERROR;
}
if (sps->vps) {
GstH265VPS *vps = gst_h265_parser_get_vps (parser, sps->vps->id);
if (!vps || vps != sps->vps) {
GST_WARNING ("Linked VPS is not identical to internal VPS");
return GST_H265_PARSER_BROKEN_LINK;
}
}
GST_DEBUG ("Updating sequence parameter set with id: %d", sps->id);
parser->sps[sps->id] = *sps;
parser->last_sps = &parser->sps[sps->id];
return GST_H265_PARSER_OK;
}
/**
* gst_h265_parser_update_pps:
* @parser: a #GstH265Parser
* @pps: (transfer none): a #GstH265PPS.
*
* Replace internal Sequence Parameter Set struct corresponding to id of @pps
* with @pps. @nalparser will mark @pps as last parsed sps.
*
* Returns: a #GstH265ParserResult
*
* Since: 1.18
*/
GstH265ParserResult
gst_h265_parser_update_pps (GstH265Parser * parser, GstH265PPS * pps)
{
GstH265SPS *sps;
g_return_val_if_fail (parser != NULL, GST_H265_PARSER_ERROR);
g_return_val_if_fail (pps != NULL, GST_H265_PARSER_ERROR);
g_return_val_if_fail (pps->id < GST_H265_MAX_PPS_COUNT,
GST_H265_PARSER_ERROR);
if (!pps->valid) {
GST_WARNING ("Cannot update with invalid PPS");
return GST_H265_PARSER_ERROR;
}
if (!pps->sps) {
GST_WARNING ("No linked SPS struct");
return GST_H265_PARSER_BROKEN_LINK;
}
sps = gst_h265_parser_get_sps (parser, pps->sps->id);
if (!sps || sps != pps->sps) {
GST_WARNING ("Linked SPS is not identical to internal SPS");
return GST_H265_PARSER_BROKEN_LINK;
}
GST_DEBUG ("Updating picture parameter set with id: %d", pps->id);
parser->pps[pps->id] = *pps;
parser->last_pps = &parser->pps[pps->id];
return GST_H265_PARSER_OK;
}
/**
* gst_h265_quant_matrix_4x4_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.6
*/
void
gst_h265_quant_matrix_4x4_get_zigzag_from_raster (guint8 out_quant[16],
const guint8 quant[16])
{
guint i;
g_return_if_fail (out_quant != quant);
for (i = 0; i < 16; i++)
out_quant[i] = quant[zigzag_4x4[i]];
}
/**
* gst_h265_quant_matrix_4x4_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.6
*/
void
gst_h265_quant_matrix_4x4_get_raster_from_zigzag (guint8 out_quant[16],
const guint8 quant[16])
{
guint i;
g_return_if_fail (out_quant != quant);
for (i = 0; i < 16; i++)
out_quant[zigzag_4x4[i]] = quant[i];
}
/**
* gst_h265_quant_matrix_8x8_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.6
*/
void
gst_h265_quant_matrix_8x8_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[zigzag_8x8[i]];
}
/**
* gst_h265_quant_matrix_8x8_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.6
*/
void
gst_h265_quant_matrix_8x8_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[zigzag_8x8[i]] = quant[i];
}
/**
* gst_h265_quant_matrix_4x4_get_uprightdiagonal_from_raster:
* @out_quant: (out): The resulting quantization matrix
* @quant: The source quantization matrix
*
* Converts quantization matrix @quant from raster scan order to
* uprightdiagonal 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.6
*/
void
gst_h265_quant_matrix_4x4_get_uprightdiagonal_from_raster (guint8 out_quant[16],
const guint8 quant[16])
{
guint i;
g_return_if_fail (out_quant != quant);
for (i = 0; i < 16; i++)
out_quant[i] = quant[uprightdiagonal_4x4[i]];
}
/**
* gst_h265_quant_matrix_4x4_get_raster_from_uprightdiagonal:
* @out_quant: (out): The resulting quantization matrix
* @quant: The source quantization matrix
*
* Converts quantization matrix @quant from uprightdiagonal 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.6
*/
void
gst_h265_quant_matrix_4x4_get_raster_from_uprightdiagonal (guint8 out_quant[16],
const guint8 quant[16])
{
guint i;
g_return_if_fail (out_quant != quant);
for (i = 0; i < 16; i++)
out_quant[uprightdiagonal_4x4[i]] = quant[i];
}
/**
* gst_h265_quant_matrix_8x8_get_uprightdiagonal_from_raster:
* @out_quant: (out): The resulting quantization matrix
* @quant: The source quantization matrix
*
* Converts quantization matrix @quant from raster scan order to
* uprightdiagonal 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.6
*/
void
gst_h265_quant_matrix_8x8_get_uprightdiagonal_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[uprightdiagonal_8x8[i]];
}
/**
* gst_h265_quant_matrix_8x8_get_raster_from_uprightdiagonal:
* @out_quant: (out): The resulting quantization matrix
* @quant: The source quantization matrix
*
* Converts quantization matrix @quant from uprightdiagonal 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.6
*/
void
gst_h265_quant_matrix_8x8_get_raster_from_uprightdiagonal (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[uprightdiagonal_8x8[i]] = quant[i];
}
typedef struct
{
GstH265Profile profile;
guint8 max_14bit_constraint_flag;
guint8 max_12bit_constraint_flag;
guint8 max_10bit_constraint_flag;
guint8 max_8bit_constraint_flag;
guint8 max_422chroma_constraint_flag;
guint8 max_420chroma_constraint_flag;
guint8 max_monochrome_constraint_flag;
guint8 intra_constraint_flag;
guint8 one_picture_only_constraint_flag;
gboolean lower_bit_rate_constraint_flag_set;
/* Tie breaker if more than one profiles are matching */
guint priority;
} H265ExtensionProfile;
typedef struct
{
H265ExtensionProfile *profile;
guint extra_constraints;
} H265ExtensionProfileMatch;
static gint
sort_fre_profile_matches (H265ExtensionProfileMatch * a,
H265ExtensionProfileMatch * b)
{
gint d;
d = a->extra_constraints - b->extra_constraints;
if (d)
return d;
return b->profile->priority - a->profile->priority;
}
static GstH265Profile
get_extension_profile (H265ExtensionProfile * profiles, guint num,
const GstH265ProfileTierLevel * ptl)
{
GstH265Profile result = GST_H265_PROFILE_INVALID;
guint i;
GList *matches = NULL;
for (i = 0; i < num; i++) {
H265ExtensionProfile p = profiles[i];
guint extra_constraints = 0;
H265ExtensionProfileMatch *m;
/* Filter out all the profiles having constraints not satisfied by @ptl.
* Then pick the one having the least extra constraints. This allow us
* to match the closest profile if bitstream contains not standard
* constraints. */
if (p.max_14bit_constraint_flag != ptl->max_14bit_constraint_flag) {
if (p.max_14bit_constraint_flag)
continue;
extra_constraints++;
}
if (p.max_12bit_constraint_flag != ptl->max_12bit_constraint_flag) {
if (p.max_12bit_constraint_flag)
continue;
extra_constraints++;
}
if (p.max_10bit_constraint_flag != ptl->max_10bit_constraint_flag) {
if (p.max_10bit_constraint_flag)
continue;
extra_constraints++;
}
if (p.max_8bit_constraint_flag != ptl->max_8bit_constraint_flag) {
if (p.max_8bit_constraint_flag)
continue;
extra_constraints++;
}
if (p.max_422chroma_constraint_flag != ptl->max_422chroma_constraint_flag) {
if (p.max_422chroma_constraint_flag)
continue;
extra_constraints++;
}
if (p.max_420chroma_constraint_flag != ptl->max_420chroma_constraint_flag) {
if (p.max_420chroma_constraint_flag)
continue;
extra_constraints++;
}
if (p.max_monochrome_constraint_flag != ptl->max_monochrome_constraint_flag) {
if (p.max_monochrome_constraint_flag)
continue;
extra_constraints++;
}
if (p.intra_constraint_flag != ptl->intra_constraint_flag) {
if (p.intra_constraint_flag)
continue;
extra_constraints++;
}
if (p.one_picture_only_constraint_flag !=
ptl->one_picture_only_constraint_flag) {
if (p.one_picture_only_constraint_flag)
continue;
extra_constraints++;
}
if (p.lower_bit_rate_constraint_flag_set
&& !ptl->lower_bit_rate_constraint_flag)
continue;
if (extra_constraints == 0) {
result = p.profile;
break;
}
m = g_new0 (H265ExtensionProfileMatch, 1);
m->profile = &profiles[i];
m->extra_constraints = extra_constraints;
matches = g_list_prepend (matches, m);
}
if (result == GST_H265_PROFILE_INVALID && matches) {
H265ExtensionProfileMatch *m;
matches = g_list_sort (matches, (GCompareFunc) sort_fre_profile_matches);
m = matches->data;
result = m->profile->profile;
GST_INFO ("Fail to find the profile matches all extensions bits,"
" select the closest %s with %d bit diff",
gst_h265_profile_to_string (result), m->extra_constraints);
}
if (matches)
g_list_free_full (matches, g_free);
return result;
}
static GstH265Profile
get_format_range_extension_profile (const GstH265ProfileTierLevel * ptl)
{
/* Profile idc: GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION
See Table A.2 for the definition of those formats */
static H265ExtensionProfile profiles[] = {
{GST_H265_PROFILE_MONOCHROME,
0, 1, 1, 1, 1, 1, 1, 0, 0, TRUE, 0},
{GST_H265_PROFILE_MONOCHROME_10,
0, 1, 1, 0, 1, 1, 1, 0, 0, TRUE, 1},
{GST_H265_PROFILE_MONOCHROME_12,
0, 1, 0, 0, 1, 1, 1, 0, 0, TRUE, 2},
{GST_H265_PROFILE_MONOCHROME_16,
0, 0, 0, 0, 1, 1, 1, 0, 0, TRUE, 3},
{GST_H265_PROFILE_MAIN_12,
0, 1, 0, 0, 1, 1, 0, 0, 0, TRUE, 4},
{GST_H265_PROFILE_MAIN_422_10,
0, 1, 1, 0, 1, 0, 0, 0, 0, TRUE, 5},
{GST_H265_PROFILE_MAIN_422_12,
0, 1, 0, 0, 1, 0, 0, 0, 0, TRUE, 6},
{GST_H265_PROFILE_MAIN_444,
0, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 7},
{GST_H265_PROFILE_MAIN_444_10,
0, 1, 1, 0, 0, 0, 0, 0, 0, TRUE, 8},
{GST_H265_PROFILE_MAIN_444_12,
0, 1, 0, 0, 0, 0, 0, 0, 0, TRUE, 9},
{GST_H265_PROFILE_MAIN_INTRA,
0, 1, 1, 1, 1, 1, 0, 1, 0, FALSE, 10},
{GST_H265_PROFILE_MAIN_10_INTRA,
0, 1, 1, 0, 1, 1, 0, 1, 0, FALSE, 11},
{GST_H265_PROFILE_MAIN_12_INTRA,
0, 1, 0, 0, 1, 1, 0, 1, 0, FALSE, 12},
{GST_H265_PROFILE_MAIN_422_10_INTRA,
0, 1, 1, 0, 1, 0, 0, 1, 0, FALSE, 13},
{GST_H265_PROFILE_MAIN_422_12_INTRA,
0, 1, 0, 0, 1, 0, 0, 1, 0, FALSE, 14},
{GST_H265_PROFILE_MAIN_444_INTRA,
0, 1, 1, 1, 0, 0, 0, 1, 0, FALSE, 15},
{GST_H265_PROFILE_MAIN_444_10_INTRA,
0, 1, 1, 0, 0, 0, 0, 1, 0, FALSE, 16},
{GST_H265_PROFILE_MAIN_444_12_INTRA,
0, 1, 0, 0, 0, 0, 0, 1, 0, FALSE, 17},
{GST_H265_PROFILE_MAIN_444_16_INTRA,
0, 0, 0, 0, 0, 0, 0, 1, 0, FALSE, 18},
{GST_H265_PROFILE_MAIN_444_STILL_PICTURE,
0, 1, 1, 1, 0, 0, 0, 1, 1, FALSE, 19},
{GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE,
0, 0, 0, 0, 0, 0, 0, 1, 1, FALSE, 20},
};
return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl);
}
static GstH265Profile
get_3d_profile (const GstH265ProfileTierLevel * ptl)
{
/* profile idc: GST_H265_PROFILE_IDC_3D_MAIN */
static H265ExtensionProfile profiles[] = {
{GST_H265_PROFILE_3D_MAIN,
0, 1, 1, 1, 1, 1, 0, 0, 0, TRUE, 0},
};
return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl);
}
static GstH265Profile
get_multiview_profile (const GstH265ProfileTierLevel * ptl)
{
static H265ExtensionProfile profiles[] = {
{GST_H265_PROFILE_MULTIVIEW_MAIN,
0, 1, 1, 1, 1, 1, 0, 0, 0, TRUE, 0},
};
return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl);
}
static GstH265Profile
get_scalable_profile (const GstH265ProfileTierLevel * ptl)
{
static H265ExtensionProfile profiles[] = {
{GST_H265_PROFILE_SCALABLE_MAIN,
0, 1, 1, 1, 1, 1, 0, 0, 0, TRUE, 0},
{GST_H265_PROFILE_SCALABLE_MAIN_10,
0, 1, 1, 0, 1, 1, 0, 0, 0, TRUE, 1},
};
return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl);
}
static GstH265Profile
get_high_throughput_profile (const GstH265ProfileTierLevel * ptl)
{
static H265ExtensionProfile profiles[] = {
{GST_H265_PROFILE_HIGH_THROUGHPUT_444,
1, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 0},
{GST_H265_PROFILE_HIGH_THROUGHPUT_444_10,
1, 1, 1, 0, 0, 0, 0, 0, 0, TRUE, 1},
{GST_H265_PROFILE_HIGH_THROUGHPUT_444_14,
1, 0, 0, 0, 0, 0, 0, 0, 0, TRUE, 2},
{GST_H265_PROFILE_HIGH_THROUGHPUT_444_16_INTRA,
0, 0, 0, 0, 0, 0, 0, 1, 0, FALSE, 3},
};
return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl);
}
static GstH265Profile
get_screen_content_coding_extensions_profile (const GstH265ProfileTierLevel *
ptl)
{
static H265ExtensionProfile profiles[] = {
{GST_H265_PROFILE_SCREEN_EXTENDED_MAIN,
1, 1, 1, 1, 1, 1, 0, 0, 0, TRUE, 0},
{GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_10,
1, 1, 1, 0, 1, 1, 0, 0, 0, TRUE, 1},
{GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444,
1, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 2},
{GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444_10,
1, 1, 1, 0, 0, 0, 0, 0, 0, TRUE, 3},
};
return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl);
}
static GstH265Profile
get_scalable_format_range_extensions_profile (const GstH265ProfileTierLevel *
ptl)
{
static H265ExtensionProfile profiles[] = {
{GST_H265_PROFILE_SCALABLE_MONOCHROME,
1, 1, 1, 1, 1, 1, 1, 0, 0, TRUE, 0},
{GST_H265_PROFILE_SCALABLE_MONOCHROME_12,
1, 1, 0, 0, 1, 1, 1, 0, 0, TRUE, 1},
{GST_H265_PROFILE_SCALABLE_MONOCHROME_16,
0, 0, 0, 0, 1, 1, 1, 0, 0, TRUE, 2},
{GST_H265_PROFILE_SCALABLE_MAIN_444,
1, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 3},
};
return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl);
}
static GstH265Profile
get_screen_content_coding_extensions_high_throughput_profile
(const GstH265ProfileTierLevel * ptl)
{
static H265ExtensionProfile profiles[] = {
{GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444,
1, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 0},
{GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_10,
1, 1, 1, 0, 0, 0, 0, 0, 0, TRUE, 1},
{GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_14,
1, 0, 0, 0, 0, 0, 0, 0, 0, TRUE, 2},
};
return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl);
}
static inline void
append_profile (GstH265Profile profiles[GST_H265_PROFILE_MAX], guint * idx,
GstH265Profile profile)
{
if (profile == GST_H265_PROFILE_INVALID)
return;
profiles[*idx] = profile;
(*idx)++;
}
/* *INDENT-OFF* */
struct h265_profiles_map
{
GstH265ProfileIDC profile_idc;
GstH265Profile (*get_profile) (const GstH265ProfileTierLevel *);
GstH265Profile profile;
};
/* *INDENT-ON* */
static const struct h265_profiles_map profiles_map[] = {
/* keep profile check in asc order */
{GST_H265_PROFILE_IDC_MAIN, NULL, GST_H265_PROFILE_MAIN},
{GST_H265_PROFILE_IDC_MAIN_10, NULL, GST_H265_PROFILE_MAIN_10},
{GST_H265_PROFILE_IDC_MAIN_STILL_PICTURE, NULL,
GST_H265_PROFILE_MAIN_STILL_PICTURE},
{GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
get_format_range_extension_profile, GST_H265_PROFILE_INVALID},
{GST_H265_PROFILE_IDC_HIGH_THROUGHPUT, get_high_throughput_profile,
GST_H265_PROFILE_INVALID},
{GST_H265_PROFILE_IDC_MULTIVIEW_MAIN, get_multiview_profile,
GST_H265_PROFILE_INVALID},
{GST_H265_PROFILE_IDC_SCALABLE_MAIN, get_scalable_profile,
GST_H265_PROFILE_INVALID},
{GST_H265_PROFILE_IDC_3D_MAIN, get_3d_profile, GST_H265_PROFILE_INVALID},
{GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING,
get_screen_content_coding_extensions_profile,
GST_H265_PROFILE_INVALID},
{GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION,
get_scalable_format_range_extensions_profile,
GST_H265_PROFILE_INVALID},
{GST_H265_PROFILE_IDC_HIGH_THROUGHPUT_SCREEN_CONTENT_CODING_EXTENSION,
get_screen_content_coding_extensions_high_throughput_profile,
GST_H265_PROFILE_INVALID},
};
static void
gst_h265_profile_tier_level_get_profiles (const GstH265ProfileTierLevel * ptl,
GstH265Profile profiles[GST_H265_PROFILE_MAX], guint * len)
{
guint i = 0, j;
/* First add profile idc */
for (j = 0; j < G_N_ELEMENTS (profiles_map); j++) {
if (ptl->profile_idc == profiles_map[j].profile_idc) {
if (profiles_map[j].get_profile)
append_profile (profiles, &i, profiles_map[j].get_profile (ptl));
else
profiles[i++] = profiles_map[j].profile;
break;
}
}
/* Later add compatibility flags */
for (j = 0; j < G_N_ELEMENTS (profiles_map); j++) {
if (i > 0 && ptl->profile_idc == profiles_map[j].profile_idc)
continue;
if (ptl->profile_compatibility_flag[profiles_map[j].profile_idc]) {
if (profiles_map[j].get_profile)
append_profile (profiles, &i, profiles_map[j].get_profile (ptl));
else
profiles[i++] = profiles_map[j].profile;
}
}
*len = i;
}
/**
* gst_h265_profile_tier_level_get_profile:
* @ptl: a #GstH265ProfileTierLevel
*
* Return the H265 profile defined in @ptl.
*
* Returns: a #GstH265Profile
* Since: 1.14
*/
GstH265Profile
gst_h265_profile_tier_level_get_profile (const GstH265ProfileTierLevel * ptl)
{
guint len = 0;
GstH265Profile profiles[GST_H265_PROFILE_MAX] = { GST_H265_PROFILE_INVALID, };
gst_h265_profile_tier_level_get_profiles (ptl, profiles, &len);
if (len > 0)
return profiles[0];
return GST_H265_PROFILE_INVALID;
}
/**
* gst_h265_profile_to_string:
* @profile: a #GstH265Profile
*
* Returns the descriptive name for the #GstH265Profile.
*
* Returns: (nullable): the name for @profile or %NULL on error
*
* Since: 1.18
*/
const gchar *
gst_h265_profile_to_string (GstH265Profile profile)
{
guint i;
if (profile == GST_H265_PROFILE_INVALID || profile == GST_H265_PROFILE_MAX)
return NULL;
for (i = 0; i < G_N_ELEMENTS (h265_profiles); i++) {
if (profile == h265_profiles[i].profile)
return h265_profiles[i].name;
}
return NULL;
}
/**
* gst_h265_profile_from_string:
* @string: the descriptive name for #GstH265Profile
*
* Returns a #GstH265Profile for the @string.
*
* Returns: the #GstH265Profile of @string or %GST_H265_PROFILE_INVALID on error
*
* Since: 1.18
*/
GstH265Profile
gst_h265_profile_from_string (const gchar * string)
{
guint i;
if (string == NULL)
return GST_H265_PROFILE_INVALID;
for (i = 0; i < G_N_ELEMENTS (h265_profiles); i++) {
if (g_strcmp0 (string, h265_profiles[i].name) == 0) {
return h265_profiles[i].profile;
}
}
return GST_H265_PROFILE_INVALID;
}
static gboolean
gst_h265_write_sei_registered_user_data (NalWriter * nw,
GstH265RegisteredUserData * rud)
{
WRITE_UINT8 (nw, rud->country_code, 8);
if (rud->country_code == 0xff)
WRITE_UINT8 (nw, rud->country_code_extension, 8);
WRITE_BYTES (nw, rud->data, rud->size);
return TRUE;
error:
return FALSE;
}
static gboolean
gst_h265_write_sei_user_data_unregistered (NalWriter * nw,
GstH265UserDataUnregistered * udu)
{
WRITE_BYTES (nw, udu->uuid, 16);
WRITE_BYTES (nw, udu->data, udu->size);
return TRUE;
error:
return FALSE;
}
static gboolean
gst_h265_write_sei_time_code (NalWriter * nw, GstH265TimeCode * tc)
{
gint i;
WRITE_UINT8 (nw, tc->num_clock_ts, 2);
for (i = 0; i < tc->num_clock_ts; i++) {
WRITE_UINT8 (nw, tc->clock_timestamp_flag[i], 1);
if (tc->clock_timestamp_flag[i]) {
WRITE_UINT8 (nw, tc->units_field_based_flag[i], 1);
WRITE_UINT8 (nw, tc->counting_type[i], 5);
WRITE_UINT8 (nw, tc->full_timestamp_flag[i], 1);
WRITE_UINT8 (nw, tc->discontinuity_flag[i], 1);
WRITE_UINT8 (nw, tc->cnt_dropped_flag[i], 1);
WRITE_UINT16 (nw, tc->n_frames[i], 9);
if (tc->full_timestamp_flag[i]) {
WRITE_UINT8 (nw, tc->seconds_value[i], 6);
WRITE_UINT8 (nw, tc->minutes_value[i], 6);
WRITE_UINT8 (nw, tc->hours_value[i], 5);
} else {
WRITE_UINT8 (nw, tc->seconds_flag[i], 1);
if (tc->seconds_flag[i]) {
WRITE_UINT8 (nw, tc->seconds_value[i], 6);
WRITE_UINT8 (nw, tc->minutes_flag[i], 1);
if (tc->minutes_flag[i]) {
WRITE_UINT8 (nw, tc->minutes_value[i], 6);
WRITE_UINT8 (nw, tc->hours_flag[i], 1);
if (tc->hours_flag[i]) {
WRITE_UINT8 (nw, tc->hours_value[i], 5);
}
}
}
}
}
WRITE_UINT8 (nw, tc->time_offset_length[i], 5);
if (tc->time_offset_length[i] > 0)
WRITE_UINT8 (nw, tc->time_offset_value[i], tc->time_offset_length[i]);
}
return TRUE;
error:
return FALSE;
}
static gboolean
gst_h265_write_sei_mastering_display_colour_volume (NalWriter * nw,
GstH265MasteringDisplayColourVolume * mdcv)
{
gint i;
for (i = 0; i < 3; i++) {
WRITE_UINT16 (nw, mdcv->display_primaries_x[i], 16);
WRITE_UINT16 (nw, mdcv->display_primaries_y[i], 16);
}
WRITE_UINT16 (nw, mdcv->white_point_x, 16);
WRITE_UINT16 (nw, mdcv->white_point_y, 16);
WRITE_UINT32 (nw, mdcv->max_display_mastering_luminance, 32);
WRITE_UINT32 (nw, mdcv->min_display_mastering_luminance, 32);
return TRUE;
error:
return FALSE;
}
static gboolean
gst_h265_write_sei_content_light_level_info (NalWriter * nw,
GstH265ContentLightLevel * cll)
{
WRITE_UINT16 (nw, cll->max_content_light_level, 16);
WRITE_UINT16 (nw, cll->max_pic_average_light_level, 16);
return TRUE;
error:
return FALSE;
}
static GstMemory *
gst_h265_create_sei_memory_internal (guint8 layer_id, guint8 temporal_id_plus1,
guint nal_prefix_size, gboolean packetized, GArray * messages)
{
NalWriter nw;
gint i;
gboolean have_written_data = FALSE;
nal_writer_init (&nw, nal_prefix_size, packetized);
if (messages->len == 0)
goto error;
GST_DEBUG ("Create SEI nal from array, len: %d", messages->len);
/* nal header */
/* forbidden_zero_bit */
WRITE_UINT8 (&nw, 0, 1);
/* nal_unit_type */
WRITE_UINT8 (&nw, GST_H265_NAL_PREFIX_SEI, 6);
/* nuh_layer_id */
WRITE_UINT8 (&nw, layer_id, 6);
/* nuh_temporal_id_plus1 */
WRITE_UINT8 (&nw, temporal_id_plus1, 3);
for (i = 0; i < messages->len; i++) {
GstH265SEIMessage *msg = &g_array_index (messages, GstH265SEIMessage, i);
guint32 payload_size_data = 0;
guint32 payload_size_in_bits = 0;
guint32 payload_type_data = msg->payloadType;
gboolean need_align = FALSE;
switch (payload_type_data) {
case GST_H265_SEI_REGISTERED_USER_DATA:{
GstH265RegisteredUserData *rud = &msg->payload.registered_user_data;
/* itu_t_t35_country_code: 8 bits */
payload_size_data = 1;
if (rud->country_code == 0xff) {
/* itu_t_t35_country_code_extension_byte */
payload_size_data++;
}
payload_size_data += rud->size;
break;
}
case GST_H265_SEI_USER_DATA_UNREGISTERED:{
GstH265UserDataUnregistered *udu = &msg->payload.user_data_unregistered;
payload_size_data = 16 + udu->size;
break;
}
case GST_H265_SEI_TIME_CODE:{
gint j;
GstH265TimeCode *tc = &msg->payload.time_code;
/* num_clock_ts: 2 bits */
payload_size_in_bits = 2;
for (j = 0; j < tc->num_clock_ts; j++) {
/* clock_timestamp_flag: 1 bit */
payload_size_in_bits += 1;
if (tc->clock_timestamp_flag[j]) {
/* units_field_based_flag: 1 bit
* counting_type: 5 bits
* full_timestamp_flag: 1 bit
* discontinuity_flag: 1 bit
* cnt_dropped_flag: 1 bit
* n_frames: 9 bit
*/
payload_size_in_bits += 18;
if (tc->full_timestamp_flag[j]) {
/* seconds_value: 6 bits
* minutes_value: 6 bits
* hours_value: 5 bits
*/
payload_size_in_bits += 17;
} else {
/* seconds_flag: 1 bit */
payload_size_in_bits += 1;
if (tc->seconds_flag[j]) {
/* seconds_value: 6 bits
* minutes_flag: 1 bit
*/
payload_size_in_bits += 7;
if (tc->minutes_flag[j]) {
/* minutes_value: 6 bits
* hours_flag: 1 bit
*/
payload_size_in_bits += 7;
if (tc->hours_flag[j]) {
/* hours_value: 5 bits */
payload_size_in_bits += 5;
}
}
}
}
/* time_offset_length: 5bits
* time_offset_value: time_offset_length bits
*/
payload_size_in_bits += (5 + tc->time_offset_length[j]);
}
}
payload_size_data = payload_size_in_bits >> 3;
if ((payload_size_in_bits & 0x7) != 0) {
GST_INFO ("Bits for Time Code SEI is not byte aligned");
payload_size_data++;
need_align = TRUE;
}
break;
}
case GST_H265_SEI_MASTERING_DISPLAY_COLOUR_VOLUME:
/* x, y 16 bits per RGB channel
* x, y 16 bits white point
* max, min luminance 32 bits
*
* (2 * 2 * 3) + (2 * 2) + (4 * 2) = 24 bytes
*/
payload_size_data = 24;
break;
case GST_H265_SEI_CONTENT_LIGHT_LEVEL:
/* maxCLL and maxFALL per 16 bits
*
* 2 * 2 = 4 bytes
*/
payload_size_data = 4;
break;
default:
break;
}
if (payload_size_data == 0) {
GST_FIXME ("Unsupported SEI type %d", msg->payloadType);
continue;
}
/* write payload type bytes */
while (payload_type_data >= 0xff) {
WRITE_UINT8 (&nw, 0xff, 8);
payload_type_data -= 0xff;
}
WRITE_UINT8 (&nw, payload_type_data, 8);
/* write payload size bytes */
while (payload_size_data >= 0xff) {
WRITE_UINT8 (&nw, 0xff, 8);
payload_size_data -= 0xff;
}
WRITE_UINT8 (&nw, payload_size_data, 8);
switch (msg->payloadType) {
case GST_H265_SEI_REGISTERED_USER_DATA:
GST_DEBUG ("Writing \"Registered user data\" done");
if (!gst_h265_write_sei_registered_user_data (&nw,
&msg->payload.registered_user_data)) {
GST_WARNING ("Failed to write \"Registered user data\"");
goto error;
}
have_written_data = TRUE;
break;
case GST_H265_SEI_USER_DATA_UNREGISTERED:
GST_DEBUG ("Writing \"Unregistered user data\" done");
if (!gst_h265_write_sei_user_data_unregistered (&nw,
&msg->payload.user_data_unregistered)) {
GST_WARNING ("Failed to write \"Unregistered user data\"");
goto error;
}
have_written_data = TRUE;
break;
case GST_H265_SEI_TIME_CODE:
GST_DEBUG ("Wrtiting \"Time code\"");
if (!gst_h265_write_sei_time_code (&nw, &msg->payload.time_code)) {
GST_WARNING ("Failed to write \"Time code\"");
goto error;
}
have_written_data = TRUE;
break;
case GST_H265_SEI_MASTERING_DISPLAY_COLOUR_VOLUME:
GST_DEBUG ("Wrtiting \"Mastering display colour volume\"");
if (!gst_h265_write_sei_mastering_display_colour_volume (&nw,
&msg->payload.mastering_display_colour_volume)) {
GST_WARNING ("Failed to write \"Mastering display colour volume\"");
goto error;
}
have_written_data = TRUE;
break;
case GST_H265_SEI_CONTENT_LIGHT_LEVEL:
GST_DEBUG ("Writing \"Content light level\" done");
if (!gst_h265_write_sei_content_light_level_info (&nw,
&msg->payload.content_light_level)) {
GST_WARNING ("Failed to write \"Content light level\"");
goto error;
}
have_written_data = TRUE;
break;
default:
break;
}
if (need_align && !nal_writer_do_rbsp_trailing_bits (&nw)) {
GST_WARNING ("Cannot insert traling bits");
goto error;
}
}
if (!have_written_data) {
GST_WARNING ("No written sei data");
goto error;
}
if (!nal_writer_do_rbsp_trailing_bits (&nw)) {
GST_WARNING ("Failed to insert rbsp trailing bits");
goto error;
}
return nal_writer_reset_and_get_memory (&nw);
error:
nal_writer_reset (&nw);
return NULL;
}
/**
* gst_h265_create_sei_memory:
* @layer_id: a nal unit layer id
* @temporal_id_plus1: a nal unit temporal identifier
* @start_code_prefix_length: a length of start code prefix, must be 3 or 4
* @messages: (transfer none): a GArray of #GstH265SEIMessage
*
* Creates raw byte-stream format (a.k.a Annex B type) SEI nal unit data
* from @messages
*
* Returns: a #GstMemory containing a PREFIX SEI nal unit
*
* Since: 1.18
*/
GstMemory *
gst_h265_create_sei_memory (guint8 layer_id, guint8 temporal_id_plus1,
guint8 start_code_prefix_length, GArray * messages)
{
g_return_val_if_fail (start_code_prefix_length == 3
|| start_code_prefix_length == 4, NULL);
g_return_val_if_fail (messages != NULL, NULL);
g_return_val_if_fail (messages->len > 0, NULL);
return gst_h265_create_sei_memory_internal (layer_id, temporal_id_plus1,
start_code_prefix_length, FALSE, messages);
}
/**
* gst_h265_create_sei_memory_hevc:
* @layer_id: a nal unit layer id
* @temporal_id_plus1: a nal unit temporal identifier
* @nal_length_size: a size of nal length field, allowed range is [1, 4]
* @messages: (transfer none): a GArray of #GstH265SEIMessage
*
* Creates raw packetized format SEI nal unit data from @messages
*
* Returns: a #GstMemory containing a PREFIX SEI nal unit
*
* Since: 1.18
*/
GstMemory *
gst_h265_create_sei_memory_hevc (guint8 layer_id, guint8 temporal_id_plus1,
guint8 nal_length_size, GArray * messages)
{
return gst_h265_create_sei_memory_internal (layer_id, temporal_id_plus1,
nal_length_size, TRUE, messages);
}
static GstBuffer *
gst_h265_parser_insert_sei_internal (GstH265Parser * parser,
guint8 nal_prefix_size, gboolean packetized, GstBuffer * au,
GstMemory * sei)
{
GstH265NalUnit nalu;
GstH265NalUnit sei_nalu;
GstMapInfo info;
GstMapInfo sei_info;
GstH265ParserResult pres;
guint offset = 0;
GstBuffer *new_buffer = NULL;
GstMemory *new_mem = NULL;
/* all SEI payload types supported by us need to have the identical
* temporal id to that of slice. Parse SEI first and we will
* update it if it's required */
if (!gst_memory_map (sei, &sei_info, GST_MAP_READ)) {
GST_ERROR ("Cannot map sei memory");
return NULL;
}
if (packetized) {
pres = gst_h265_parser_identify_nalu_hevc (parser,
sei_info.data, 0, sei_info.size, nal_prefix_size, &sei_nalu);
} else {
pres = gst_h265_parser_identify_nalu (parser,
sei_info.data, 0, sei_info.size, &sei_nalu);
}
gst_memory_unmap (sei, &sei_info);
if (pres != GST_H265_PARSER_OK && pres != GST_H265_PARSER_NO_NAL_END) {
GST_DEBUG ("Failed to identify sei nal unit, ret: %d", pres);
return NULL;
}
if (!gst_buffer_map (au, &info, GST_MAP_READ)) {
GST_ERROR ("Cannot map au buffer");
return NULL;
}
/* Find the offset of the first slice */
do {
if (packetized) {
pres = gst_h265_parser_identify_nalu_hevc (parser,
info.data, offset, info.size, nal_prefix_size, &nalu);
} else {
pres = gst_h265_parser_identify_nalu (parser,
info.data, offset, info.size, &nalu);
}
if (pres != GST_H265_PARSER_OK && pres != GST_H265_PARSER_NO_NAL_END) {
GST_DEBUG ("Failed to identify nal unit, ret: %d", pres);
gst_buffer_unmap (au, &info);
return NULL;
}
if (nalu.type <= GST_H265_NAL_SLICE_RASL_R
|| (nalu.type >= GST_H265_NAL_SLICE_BLA_W_LP
&& nalu.type <= GST_H265_NAL_SLICE_CRA_NUT)) {
GST_DEBUG ("Found slice nal type %d at offset %d", nalu.type,
nalu.sc_offset);
break;
}
offset = nalu.offset + nalu.size;
} while (pres == GST_H265_PARSER_OK);
gst_buffer_unmap (au, &info);
/* found the best position now, create new buffer */
new_buffer = gst_buffer_new ();
/* copy all metadata */
if (!gst_buffer_copy_into (new_buffer, au, GST_BUFFER_COPY_METADATA, 0, -1)) {
GST_ERROR ("Failed to copy metadata into new buffer");
gst_clear_buffer (&new_buffer);
goto out;
}
/* copy non-slice nal */
if (nalu.sc_offset > 0) {
if (!gst_buffer_copy_into (new_buffer, au,
GST_BUFFER_COPY_MEMORY, 0, nalu.sc_offset)) {
GST_ERROR ("Failed to copy buffer");
gst_clear_buffer (&new_buffer);
goto out;
}
}
/* check whether we need to update temporal id and layer id.
* If it's not matched to slice nalu, update it.
*/
if (sei_nalu.layer_id != nalu.layer_id || sei_nalu.temporal_id_plus1 !=
nalu.temporal_id_plus1) {
guint16 nalu_header;
guint16 layer_id_temporal_id = 0;
new_mem = gst_memory_copy (sei, 0, -1);
if (!gst_memory_map (new_mem, &sei_info, GST_MAP_READWRITE)) {
GST_ERROR ("Failed to map new sei memory");
gst_memory_unref (new_mem);
gst_clear_buffer (&new_buffer);
goto out;
}
nalu_header = GST_READ_UINT16_BE (sei_info.data + sei_nalu.offset);
/* clear bits 7 ~ 15
* NOTE:
* bit 0: forbidden_zero_bit
* bits 1 ~ 6: nalu type */
nalu_header &= 0xfe00;
layer_id_temporal_id = ((nalu.layer_id << 3) & 0x1f8);
layer_id_temporal_id |= (nalu.temporal_id_plus1 & 0x7);
nalu_header |= layer_id_temporal_id;
GST_WRITE_UINT16_BE (sei_info.data + sei_nalu.offset, nalu_header);
gst_memory_unmap (new_mem, &sei_info);
} else {
new_mem = gst_memory_ref (sei);
}
/* insert sei */
gst_buffer_append_memory (new_buffer, new_mem);
/* copy the rest */
if (!gst_buffer_copy_into (new_buffer, au,
GST_BUFFER_COPY_MEMORY, nalu.sc_offset, -1)) {
GST_ERROR ("Failed to copy buffer");
gst_clear_buffer (&new_buffer);
goto out;
}
out:
return new_buffer;
}
/**
* gst_h265_parser_insert_sei:
* @parser: a #GstH265Parser
* @au: (transfer none): a #GstBuffer containing AU data
* @sei: (transfer none): a #GstMemory containing a SEI nal
*
* Copy @au into new #GstBuffer and insert @sei into the #GstBuffer.
* The validation for completeness of @au and @sei is caller's responsibility.
* Both @au and @sei must be byte-stream formatted
*
* Returns: (transfer full) (nullable): a SEI inserted #GstBuffer or %NULL
* if cannot figure out proper position to insert a @sei
*
* Since: 1.18
*/
GstBuffer *
gst_h265_parser_insert_sei (GstH265Parser * parser, GstBuffer * au,
GstMemory * sei)
{
g_return_val_if_fail (parser != NULL, NULL);
g_return_val_if_fail (GST_IS_BUFFER (au), NULL);
g_return_val_if_fail (sei != NULL, NULL);
/* the size of start code prefix (3 or 4) is not matter since it will be
* scanned */
return gst_h265_parser_insert_sei_internal (parser, 4, FALSE, au, sei);
}
/**
* gst_h265_parser_insert_sei_hevc:
* @parser: a #GstH265Parser
* @nal_length_size: a size of nal length field, allowed range is [1, 4]
* @au: (transfer none): a #GstBuffer containing AU data
* @sei: (transfer none): a #GstMemory containing a SEI nal
*
* Copy @au into new #GstBuffer and insert @sei into the #GstBuffer.
* The validation for completeness of @au and @sei is caller's responsibility.
* Nal prefix type of both @au and @sei must be packetized, and
* also the size of nal length field must be identical to @nal_length_size
*
* Returns: (transfer full) (nullable): a SEI inserted #GstBuffer or %NULL
* if cannot figure out proper position to insert a @sei
*
* Since: 1.18
*/
GstBuffer *
gst_h265_parser_insert_sei_hevc (GstH265Parser * parser, guint8 nal_length_size,
GstBuffer * au, GstMemory * sei)
{
g_return_val_if_fail (parser != NULL, NULL);
g_return_val_if_fail (nal_length_size > 0 && nal_length_size < 5, NULL);
g_return_val_if_fail (GST_IS_BUFFER (au), NULL);
g_return_val_if_fail (sei != NULL, NULL);
return gst_h265_parser_insert_sei_internal (parser, nal_length_size, TRUE,
au, sei);
}
/**
* gst_h265_get_profile_from_sps:
* @sps: a #GstH265SPS
*
* Return the H265 profile from @sps.
*
* Returns: a #GstH265Profile
* Since: 1.20
*/
GstH265Profile
gst_h265_get_profile_from_sps (GstH265SPS * sps)
{
GstH265Profile profiles[GST_H265_PROFILE_MAX] = { GST_H265_PROFILE_INVALID, };
GstH265ProfileTierLevel tmp_ptl;
guint i, len = 0;
guint chroma_format_idc, bit_depth_luma, bit_depth_chroma;
g_return_val_if_fail (sps != NULL, GST_H265_PROFILE_INVALID);
tmp_ptl = sps->profile_tier_level;
chroma_format_idc = sps->chroma_format_idc;
bit_depth_luma = sps->bit_depth_luma_minus8 + 8;
bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8;
gst_h265_profile_tier_level_get_profiles (&sps->profile_tier_level, profiles,
&len);
for (i = 0; i < len && i < G_N_ELEMENTS (profiles); i++) {
switch (profiles[i]) {
case GST_H265_PROFILE_INVALID:
break;
case GST_H265_PROFILE_MAIN:
case GST_H265_PROFILE_MAIN_STILL_PICTURE:
/* A.3.2 or A.3.5 */
if (chroma_format_idc == 1
&& bit_depth_luma == 8 && bit_depth_chroma == 8)
return profiles[i];
break;
case GST_H265_PROFILE_MAIN_10:
/* A.3.3 */
if (chroma_format_idc == 1
&& bit_depth_luma >= 8 && bit_depth_luma <= 10
&& bit_depth_chroma >= 8 && bit_depth_chroma <= 10)
return profiles[i];
break;
default:
return profiles[i];
}
}
/* Invalid profile: */
/* Set the conformance indicators based on chroma_format_idc / bit_depth */
switch (chroma_format_idc) {
case 0:
tmp_ptl.max_monochrome_constraint_flag = 1;
tmp_ptl.max_420chroma_constraint_flag = 1;
tmp_ptl.max_422chroma_constraint_flag = 1;
break;
case 1:
tmp_ptl.max_monochrome_constraint_flag = 0;
tmp_ptl.max_420chroma_constraint_flag = 1;
tmp_ptl.max_422chroma_constraint_flag = 1;
break;
case 2:
tmp_ptl.max_monochrome_constraint_flag = 0;
tmp_ptl.max_420chroma_constraint_flag = 0;
tmp_ptl.max_422chroma_constraint_flag = 1;
break;
case 3:
tmp_ptl.max_monochrome_constraint_flag = 0;
tmp_ptl.max_420chroma_constraint_flag = 0;
tmp_ptl.max_422chroma_constraint_flag = 0;
break;
default:
g_assert_not_reached ();
break;
}
tmp_ptl.max_8bit_constraint_flag = 1;
tmp_ptl.max_10bit_constraint_flag = 1;
tmp_ptl.max_12bit_constraint_flag = 1;
tmp_ptl.max_14bit_constraint_flag = 1;
if (bit_depth_luma > 8 || bit_depth_chroma > 8)
tmp_ptl.max_8bit_constraint_flag = 0;
if (bit_depth_luma > 10 || bit_depth_chroma > 10)
tmp_ptl.max_10bit_constraint_flag = 0;
if (bit_depth_luma > 12 || bit_depth_chroma > 12)
tmp_ptl.max_12bit_constraint_flag = 0;
if (tmp_ptl.profile_idc == GST_H265_PROFILE_IDC_HIGH_THROUGHPUT
|| tmp_ptl.profile_idc == GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING
|| tmp_ptl.profile_idc ==
GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION
|| tmp_ptl.profile_idc ==
GST_H265_PROFILE_IDC_HIGH_THROUGHPUT_SCREEN_CONTENT_CODING_EXTENSION
|| tmp_ptl.profile_compatibility_flag[5]
|| tmp_ptl.profile_compatibility_flag[9]
|| tmp_ptl.profile_compatibility_flag[10]
|| tmp_ptl.profile_compatibility_flag[11]) {
if (bit_depth_luma > 14 || bit_depth_chroma > 14)
tmp_ptl.max_14bit_constraint_flag = 0;
} else {
tmp_ptl.max_14bit_constraint_flag = 0;
}
/* first profile of the synthetic ptl */
return gst_h265_profile_tier_level_get_profile (&tmp_ptl);
}
/* *INDENT-OFF* */
static void
gst_clear_h265_decoder_config_record_nalu_array (
GstH265DecoderConfigRecordNalUnitArray * array)
{
if (!array)
return;
if (array->nalu)
g_array_unref (array->nalu);
}
/* *INDENT-ON* */
/**
* gst_h265_decoder_config_record_free:
* @config: (nullable): a #GstH265DecoderConfigRecord data
*
* Free @config data
*
* Since: 1.24
*/
void
gst_h265_decoder_config_record_free (GstH265DecoderConfigRecord * config)
{
if (!config)
return;
if (config->nalu_array)
g_array_unref (config->nalu_array);
g_free (config);
}
static GstH265DecoderConfigRecord *
gst_h265_decoder_config_record_new (void)
{
GstH265DecoderConfigRecord *config;
config = g_new0 (GstH265DecoderConfigRecord, 1);
config->nalu_array = g_array_new (FALSE,
FALSE, sizeof (GstH265DecoderConfigRecordNalUnitArray));
g_array_set_clear_func (config->nalu_array,
(GDestroyNotify) gst_clear_h265_decoder_config_record_nalu_array);
return config;
}
/**
* gst_h265_parser_parse_decoder_config_record:
* @parser: a #GstH265Parser
* @data: the data to parse
* @size: the size of @data
* @config: (out): parsed #GstH265DecoderConfigRecord data
*
* Parses HEVCDecoderConfigurationRecord data and fill into @config.
* The caller must free @config via gst_h265_decoder_config_record_free()
*
* This method does not parse VPS, SPS and PPS and therefore the caller needs to
* parse each NAL unit via appropriate parsing method.
*
* Returns: a #GstH265ParserResult
*
* Since: 1.24
*/
GstH265ParserResult
gst_h265_parser_parse_decoder_config_record (GstH265Parser * parser,
const guint8 * data, gsize size, GstH265DecoderConfigRecord ** config)
{
GstH265DecoderConfigRecord *ret;
GstBitReader br;
GstH265ParserResult result = GST_H265_PARSER_OK;
guint i;
guint8 num_of_arrays;
g_return_val_if_fail (parser != NULL, GST_H265_PARSER_ERROR);
g_return_val_if_fail (data != NULL, GST_H265_PARSER_ERROR);
g_return_val_if_fail (config != NULL, GST_H265_PARSER_ERROR);
#define READ_CONFIG_UINT8(val, nbits) G_STMT_START { \
if (!gst_bit_reader_get_bits_uint8 (&br, (guint8 *) &val, nbits)) { \
GST_WARNING ("Failed to read " G_STRINGIFY (val)); \
result = GST_H265_PARSER_ERROR; \
goto error; \
} \
} G_STMT_END;
#define READ_CONFIG_UINT16(val, nbits) G_STMT_START { \
if (!gst_bit_reader_get_bits_uint16 (&br, &val, nbits)) { \
GST_WARNING ("Failed to read " G_STRINGIFY (val)); \
result = GST_H265_PARSER_ERROR; \
goto error; \
} \
} G_STMT_END;
#define SKIP_CONFIG_BITS(nbits) G_STMT_START { \
if (!gst_bit_reader_skip (&br, nbits)) { \
GST_WARNING ("Failed to skip %d bits", nbits); \
result = GST_H265_PARSER_ERROR; \
goto error; \
} \
} G_STMT_END;
*config = NULL;
if (size < 23) {
GST_WARNING ("Too small size hvcC");
return GST_H265_PARSER_ERROR;
}
gst_bit_reader_init (&br, data, size);
ret = gst_h265_decoder_config_record_new ();
READ_CONFIG_UINT8 (ret->configuration_version, 8);
if (ret->configuration_version != 1) {
GST_WARNING ("Wrong configurationVersion %d", ret->configuration_version);
/* Must be 1 but allows 0 for backward compatibility.
* See commit
* https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/63fee31a3f95021fa0bb2429c723f5356c9b3c4b */
if (ret->configuration_version != 0) {
result = GST_H265_PARSER_ERROR;
goto error;
}
}
READ_CONFIG_UINT8 (ret->general_profile_space, 2);
READ_CONFIG_UINT8 (ret->general_tier_flag, 1);
READ_CONFIG_UINT8 (ret->general_profile_idc, 5);
for (i = 0; i < 32; i++)
READ_CONFIG_UINT8 (ret->general_profile_compatibility_flags[i], 1);
/* 7.3.3 Profile, tier and level syntax */
READ_CONFIG_UINT8 (ret->general_progressive_source_flag, 1);
READ_CONFIG_UINT8 (ret->general_interlaced_source_flag, 1);
READ_CONFIG_UINT8 (ret->general_non_packed_constraint_flag, 1);
READ_CONFIG_UINT8 (ret->general_frame_only_constraint_flag, 1);
if (ret->general_profile_idc == 4 ||
ret->general_profile_compatibility_flags[4] ||
ret->general_profile_idc == 5 ||
ret->general_profile_compatibility_flags[5] ||
ret->general_profile_idc == 6 ||
ret->general_profile_compatibility_flags[6] ||
ret->general_profile_idc == 7 ||
ret->general_profile_compatibility_flags[7] ||
ret->general_profile_idc == 8 ||
ret->general_profile_compatibility_flags[8] ||
ret->general_profile_idc == 9 ||
ret->general_profile_compatibility_flags[9] ||
ret->general_profile_idc == 10 ||
ret->general_profile_compatibility_flags[10] ||
ret->general_profile_idc == 11 ||
ret->general_profile_compatibility_flags[11]) {
READ_CONFIG_UINT8 (ret->general_max_12bit_constraint_flag, 1);
READ_CONFIG_UINT8 (ret->general_max_10bit_constraint_flag, 1);
READ_CONFIG_UINT8 (ret->general_max_8bit_constraint_flag, 1);
READ_CONFIG_UINT8 (ret->general_max_422chroma_constraint_flag, 1);
READ_CONFIG_UINT8 (ret->general_max_420chroma_constraint_flag, 1);
READ_CONFIG_UINT8 (ret->general_max_monochrome_constraint_flag, 1);
READ_CONFIG_UINT8 (ret->general_intra_constraint_flag, 1);
READ_CONFIG_UINT8 (ret->general_one_picture_only_constraint_flag, 1);
READ_CONFIG_UINT8 (ret->general_lower_bit_rate_constraint_flag, 1);
if (ret->general_profile_idc == 5 ||
ret->general_profile_compatibility_flags[5] ||
ret->general_profile_idc == 9 ||
ret->general_profile_compatibility_flags[9] ||
ret->general_profile_idc == 10 ||
ret->general_profile_compatibility_flags[10] ||
ret->general_profile_idc == 11 ||
ret->general_profile_compatibility_flags[11]) {
READ_CONFIG_UINT8 (ret->general_max_14bit_constraint_flag, 1);
SKIP_CONFIG_BITS (33);
} else {
SKIP_CONFIG_BITS (34);
}
} else if (ret->general_profile_idc == 2 ||
ret->general_profile_compatibility_flags[2]) {
SKIP_CONFIG_BITS (7);
READ_CONFIG_UINT8 (ret->general_one_picture_only_constraint_flag, 1);
SKIP_CONFIG_BITS (35);
} else {
SKIP_CONFIG_BITS (43);
}
if (ret->general_profile_idc == 1 ||
ret->general_profile_compatibility_flags[1] ||
ret->general_profile_idc == 2 ||
ret->general_profile_compatibility_flags[2] ||
ret->general_profile_idc == 3 ||
ret->general_profile_compatibility_flags[3] ||
ret->general_profile_idc == 4 ||
ret->general_profile_compatibility_flags[4] ||
ret->general_profile_idc == 5 ||
ret->general_profile_compatibility_flags[5] ||
ret->general_profile_idc == 9 ||
ret->general_profile_compatibility_flags[9] ||
ret->general_profile_idc == 11 ||
ret->general_profile_compatibility_flags[11]) {
READ_CONFIG_UINT8 (ret->general_inbld_flag, 1);
} else {
SKIP_CONFIG_BITS (1);
}
g_assert (gst_bit_reader_get_pos (&br) == 12 * 8);
READ_CONFIG_UINT8 (ret->general_level_idc, 8);
SKIP_CONFIG_BITS (4);
READ_CONFIG_UINT16 (ret->min_spatial_segmentation_idc, 12);
SKIP_CONFIG_BITS (6);
READ_CONFIG_UINT8 (ret->parallelism_type, 2);
SKIP_CONFIG_BITS (6);
READ_CONFIG_UINT8 (ret->chroma_format_idc, 2);
SKIP_CONFIG_BITS (5);
READ_CONFIG_UINT8 (ret->bit_depth_luma_minus8, 3);
SKIP_CONFIG_BITS (5);
READ_CONFIG_UINT8 (ret->bit_depth_chroma_minus8, 3);
READ_CONFIG_UINT16 (ret->avg_frame_rate, 16);
READ_CONFIG_UINT8 (ret->constant_frame_rate, 2);
READ_CONFIG_UINT8 (ret->num_temporal_layers, 3);
READ_CONFIG_UINT8 (ret->temporal_id_nested, 1);
READ_CONFIG_UINT8 (ret->length_size_minus_one, 2);
if (ret->length_size_minus_one == 2) {
/* "length_size_minus_one + 1" should be 1, 2, or 4 */
GST_WARNING ("Wrong nal-length-size");
result = GST_H265_PARSER_ERROR;
goto error;
}
READ_CONFIG_UINT8 (num_of_arrays, 8);
g_assert (gst_bit_reader_get_pos (&br) == 23 * 8);
for (i = 0; i < num_of_arrays; i++) {
GstH265DecoderConfigRecordNalUnitArray array;
guint8 nalu_type;
GstH265NalUnit nalu;
guint16 num_nalu, j;
guint offset;
READ_CONFIG_UINT8 (array.array_completeness, 1);
SKIP_CONFIG_BITS (1);
READ_CONFIG_UINT8 (nalu_type, 6);
array.nal_unit_type = nalu_type;
READ_CONFIG_UINT16 (num_nalu, 16);
offset = gst_bit_reader_get_pos (&br) / 8;
array.nalu = g_array_sized_new (FALSE, FALSE, sizeof (GstH265NalUnit),
num_nalu);
for (j = 0; j < num_nalu; j++) {
result = gst_h265_parser_identify_nalu_hevc (parser, data, offset, size,
2, &nalu);
if (result != GST_H265_PARSER_OK) {
g_array_unref (array.nalu);
/* Ignores parsing error if this is the last nalu and not an essential
* nalu for decoding */
if (i + 1 == num_of_arrays && j + 1 == num_nalu &&
nalu_type != GST_H265_NAL_VPS && nalu_type != GST_H265_NAL_SPS &&
nalu_type != GST_H265_NAL_PPS) {
GST_WARNING ("Couldn't parse the last nalu, type %d at array %d / %d",
nalu_type, i, j);
goto out;
}
goto error;
}
g_array_append_val (array.nalu, nalu);
offset = nalu.offset + nalu.size;
}
g_array_append_val (ret->nalu_array, array);
if (i != num_of_arrays - 1 && !gst_bit_reader_set_pos (&br, offset * 8)) {
GST_WARNING ("Not enough byte for NAL reading");
result = GST_H265_PARSER_ERROR;
goto error;
}
}
out:
*config = ret;
return GST_H265_PARSER_OK;
error:
{
gst_h265_decoder_config_record_free (ret);
return result;
}
#undef READ_CONFIG_UINT8
#undef READ_CONFIG_UINT16
#undef SKIP_CONFIG_BITS
}