mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
ce09ceb106
Add an API to create raw SEI nal unit. This would be useful in case an user want to create SEI nal data and inject the SEI nal data into bitstream.
3931 lines
123 KiB
C
3931 lines
123 KiB
C
/* 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);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
GST_WARNING ("error parsing \"VUI Parameters\"");
|
|
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 \"Prediction weight table\"");
|
|
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_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_slice_new0 (GstH265Parser);
|
|
|
|
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_slice_free (GstH265Parser, 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;
|
|
}
|
|
|
|
if (offset + off1 == size - 1) {
|
|
GST_DEBUG ("Missing data to identify nal unit");
|
|
|
|
return GST_H265_PARSER_ERROR;
|
|
}
|
|
|
|
nalu->sc_offset = offset + off1;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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));
|
|
|
|
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 (size < 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_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. */
|
|
nal_reader_skip (&nr, 1);
|
|
}
|
|
}
|
|
|
|
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;
|
|
GstH265VPS *vps;
|
|
guint8 vps_id;
|
|
guint i;
|
|
guint subwc[] = { 1, 2, 2, 1, 1 };
|
|
guint subhc[] = { 1, 2, 1, 1, 1 };
|
|
GstH265VUIParams *vui = NULL;
|
|
|
|
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, vps_id, 4);
|
|
vps = gst_h265_parser_get_vps (parser, vps_id);
|
|
if (!vps) {
|
|
GST_DEBUG ("couldn't find associated video parameter set with id: %d",
|
|
vps_id);
|
|
}
|
|
sps->vps = vps;
|
|
|
|
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;
|
|
vui = &sps->vui_params;
|
|
}
|
|
|
|
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_extension_5bits, 5);
|
|
}
|
|
|
|
if (sps->sps_range_extension_flag) {
|
|
READ_UINT8 (&nr,
|
|
sps->sps_extnsion_params.transform_skip_rotation_enabled_flag, 1);
|
|
READ_UINT8 (&nr,
|
|
sps->sps_extnsion_params.transform_skip_context_enabled_flag, 1);
|
|
READ_UINT8 (&nr, sps->sps_extnsion_params.implicit_rdpcm_enabled_flag, 1);
|
|
READ_UINT8 (&nr, sps->sps_extnsion_params.explicit_rdpcm_enabled_flag, 1);
|
|
READ_UINT8 (&nr,
|
|
sps->sps_extnsion_params.extended_precision_processing_flag, 1);
|
|
READ_UINT8 (&nr, sps->sps_extnsion_params.intra_smoothing_disabled_flag, 1);
|
|
READ_UINT8 (&nr,
|
|
sps->sps_extnsion_params.high_precision_offsets_enabled_flag, 1);
|
|
READ_UINT8 (&nr,
|
|
sps->sps_extnsion_params.persistent_rice_adaptation_enabled_flag, 1);
|
|
READ_UINT8 (&nr,
|
|
sps->sps_extnsion_params.cabac_bypass_alignment_enabled_flag, 1);
|
|
}
|
|
|
|
/* 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;
|
|
|
|
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 (parse_vui_params && 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");
|
|
}
|
|
|
|
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)
|
|
{
|
|
NalReader nr;
|
|
GstH265SPS *sps;
|
|
gint sps_id;
|
|
gint qp_bd_offset;
|
|
guint32 CtbSizeY, MinCbLog2SizeY, CtbLog2SizeY, MaxBitDepthY, MaxBitDepthC;
|
|
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, 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;
|
|
}
|
|
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);
|
|
|
|
/* 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);
|
|
READ_SE_ALLOWED (&nr, pps->init_qp_minus26, -(26 + qp_bd_offset), 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,
|
|
sps->log2_diff_max_min_luma_coding_block_size);
|
|
|
|
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) {
|
|
READ_UE_ALLOWED (&nr, pps->num_tile_columns_minus1, 0, 19);
|
|
READ_UE_ALLOWED (&nr, pps->num_tile_rows_minus1, 0, 21);
|
|
|
|
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;
|
|
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 (&nr, &pps->scaling_list, TRUE))
|
|
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_extension_5bits, 5);
|
|
}
|
|
|
|
if (pps->pps_range_extension_flag) {
|
|
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);
|
|
}
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
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;
|
|
}
|
|
|
|
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++)
|
|
nal_reader_skip (&nr, 1);
|
|
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);
|
|
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;
|
|
} 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;
|
|
|
|
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]);
|
|
}
|
|
}
|
|
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);
|
|
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);
|
|
}
|
|
|
|
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_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_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);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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_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;
|
|
GstH265ProfileIDC profile_idc;
|
|
|
|
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;
|
|
} FormatRangeExtensionProfile;
|
|
|
|
typedef struct
|
|
{
|
|
FormatRangeExtensionProfile *profile;
|
|
guint extra_constraints;
|
|
} FormatRangeExtensionProfileMatch;
|
|
|
|
static gint
|
|
sort_fre_profile_matches (FormatRangeExtensionProfileMatch * a,
|
|
FormatRangeExtensionProfileMatch * b)
|
|
{
|
|
gint d;
|
|
|
|
d = a->extra_constraints - b->extra_constraints;
|
|
if (d)
|
|
return d;
|
|
|
|
return b->profile->priority - a->profile->priority;
|
|
}
|
|
|
|
static GstH265Profile
|
|
get_format_range_extension_profile (GstH265ProfileTierLevel * ptl)
|
|
{
|
|
/* See Table A.2 for the definition of those formats */
|
|
FormatRangeExtensionProfile profiles[] = {
|
|
{GST_H265_PROFILE_MONOCHROME, GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 1, 1, 1, 1, 1, 0, 0, TRUE, 0},
|
|
{GST_H265_PROFILE_MONOCHROME_10,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 1, 0, 1, 1, 1, 0, 0, TRUE, 1},
|
|
{GST_H265_PROFILE_MONOCHROME_12,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 0, 0, 1, 1, 1, 0, 0, TRUE, 2},
|
|
{GST_H265_PROFILE_MONOCHROME_16,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 0, 0, 0, 1, 1, 1, 0, 0, TRUE, 3},
|
|
{GST_H265_PROFILE_MAIN_12, GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 0, 0, 1, 1, 0, 0, 0, TRUE, 4},
|
|
{GST_H265_PROFILE_MAIN_422_10, GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 1, 0, 1, 0, 0, 0, 0, TRUE, 5},
|
|
{GST_H265_PROFILE_MAIN_422_12, GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 0, 0, 1, 0, 0, 0, 0, TRUE, 6},
|
|
{GST_H265_PROFILE_MAIN_444, GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 7},
|
|
{GST_H265_PROFILE_MAIN_444_10, GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 1, 0, 0, 0, 0, 0, 0, TRUE, 8},
|
|
{GST_H265_PROFILE_MAIN_444_12, GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 0, 0, 0, 0, 0, 0, 0, TRUE, 9},
|
|
{GST_H265_PROFILE_MAIN_INTRA, GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 1, 1, 1, 1, 0, 1, 0, FALSE, 10},
|
|
{GST_H265_PROFILE_MAIN_10_INTRA,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 1, 0, 1, 1, 0, 1, 0, FALSE, 11},
|
|
{GST_H265_PROFILE_MAIN_12_INTRA,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 0, 0, 1, 1, 0, 1, 0, FALSE, 12},
|
|
{GST_H265_PROFILE_MAIN_422_10_INTRA,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 1, 0, 1, 0, 0, 1, 0, FALSE, 13},
|
|
{GST_H265_PROFILE_MAIN_422_12_INTRA,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 0, 0, 1, 0, 0, 1, 0, FALSE, 14},
|
|
{GST_H265_PROFILE_MAIN_444_INTRA,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 1, 1, 0, 0, 0, 1, 0, FALSE, 15},
|
|
{GST_H265_PROFILE_MAIN_444_10_INTRA,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 1, 0, 0, 0, 0, 1, 0, FALSE, 16},
|
|
{GST_H265_PROFILE_MAIN_444_12_INTRA,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 0, 0, 0, 0, 0, 1, 0, FALSE, 17},
|
|
{GST_H265_PROFILE_MAIN_444_16_INTRA,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 0, 0, 0, 0, 0, 0, 1, 0, FALSE, 18},
|
|
{GST_H265_PROFILE_MAIN_444_STILL_PICTURE,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 1, 1, 1, 0, 0, 0, 1, 1, FALSE, 19},
|
|
{GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE,
|
|
GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION,
|
|
0, 0, 0, 0, 0, 0, 0, 1, 1, FALSE, 20},
|
|
|
|
/* High Througput */
|
|
{GST_H265_PROFILE_HIGH_THROUGHPUT_444, GST_H265_PROFILE_IDC_HIGH_THROUGHPUT,
|
|
1, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 21},
|
|
{GST_H265_PROFILE_HIGH_THROUGHPUT_444_10,
|
|
GST_H265_PROFILE_IDC_HIGH_THROUGHPUT,
|
|
1, 1, 1, 0, 0, 0, 0, 0, 0, TRUE, 22},
|
|
{GST_H265_PROFILE_HIGH_THROUGHPUT_444_14,
|
|
GST_H265_PROFILE_IDC_HIGH_THROUGHPUT,
|
|
1, 0, 0, 0, 0, 0, 0, 0, 0, TRUE, 23},
|
|
{GST_H265_PROFILE_HIGH_THROUGHPUT_444_16_INTRA,
|
|
GST_H265_PROFILE_IDC_HIGH_THROUGHPUT,
|
|
0, 0, 0, 0, 0, 0, 0, 1, 0, FALSE, 24},
|
|
|
|
/* Screen content coding */
|
|
{GST_H265_PROFILE_SCREEN_EXTENDED_MAIN,
|
|
GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING,
|
|
1, 1, 1, 1, 1, 1, 0, 0, 0, TRUE, 25},
|
|
{GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_10,
|
|
GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING,
|
|
1, 1, 1, 0, 1, 1, 0, 0, 0, TRUE, 26},
|
|
{GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444,
|
|
GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING,
|
|
1, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 27},
|
|
{GST_H265_PROFILE_SCREEN_EXTENDED_MAIN_444_10,
|
|
GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING,
|
|
1, 1, 1, 0, 0, 0, 0, 0, 0, TRUE, 28},
|
|
/* identical to screen-extended-main-444 */
|
|
{GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444,
|
|
GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING,
|
|
1, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 29},
|
|
/* identical to screen-extended-main-444-10 */
|
|
{GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_10,
|
|
GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING,
|
|
1, 1, 1, 0, 0, 0, 0, 0, 0, TRUE, 30},
|
|
{GST_H265_PROFILE_SCREEN_EXTENDED_HIGH_THROUGHPUT_444_14,
|
|
GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING,
|
|
1, 0, 0, 0, 0, 0, 0, 0, 0, TRUE, 31},
|
|
|
|
/* Multiview Main */
|
|
{GST_H265_PROFILE_MULTIVIEW_MAIN, GST_H265_PROFILE_IDC_MULTIVIEW_MAIN,
|
|
0, 1, 1, 1, 1, 1, 0, 0, 0, TRUE, 32},
|
|
|
|
/* Scalable Main */
|
|
{GST_H265_PROFILE_SCALABLE_MAIN, GST_H265_PROFILE_IDC_SCALABLE_MAIN,
|
|
0, 1, 1, 1, 1, 1, 0, 0, 0, TRUE, 33},
|
|
{GST_H265_PROFILE_SCALABLE_MAIN_10, GST_H265_PROFILE_IDC_SCALABLE_MAIN,
|
|
0, 1, 1, 0, 1, 1, 0, 0, 0, TRUE, 34},
|
|
|
|
/* Scalable format range extensions */
|
|
{GST_H265_PROFILE_SCALABLE_MONOCHROME,
|
|
GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION,
|
|
1, 1, 1, 1, 1, 1, 1, 0, 0, TRUE, 35},
|
|
{GST_H265_PROFILE_SCALABLE_MONOCHROME_12,
|
|
GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION,
|
|
1, 1, 0, 0, 1, 1, 1, 0, 0, TRUE, 36},
|
|
{GST_H265_PROFILE_SCALABLE_MONOCHROME_16,
|
|
GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION,
|
|
0, 0, 0, 0, 1, 1, 1, 0, 0, TRUE, 37},
|
|
{GST_H265_PROFILE_SCALABLE_MAIN_444,
|
|
GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION,
|
|
1, 1, 1, 1, 0, 0, 0, 0, 0, TRUE, 38},
|
|
|
|
/* 3D Main */
|
|
{GST_H265_PROFILE_3D_MAIN, GST_H265_PROFILE_IDC_3D_MAIN,
|
|
0, 1, 1, 1, 1, 1, 0, 0, 0, TRUE, 39},
|
|
};
|
|
GstH265Profile result = GST_H265_PROFILE_INVALID;
|
|
guint i;
|
|
GList *matches = NULL;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (profiles); i++) {
|
|
FormatRangeExtensionProfile p = profiles[i];
|
|
guint extra_constraints = 0;
|
|
FormatRangeExtensionProfileMatch *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 closet 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 &&
|
|
(p.profile_idc == ptl->profile_idc
|
|
|| ptl->profile_compatibility_flag[ptl->profile_idc])) {
|
|
result = p.profile;
|
|
break;
|
|
}
|
|
|
|
m = g_new0 (FormatRangeExtensionProfileMatch, 1);
|
|
m->profile = &profiles[i];
|
|
m->extra_constraints = extra_constraints;
|
|
matches = g_list_prepend (matches, m);
|
|
}
|
|
|
|
if (result == GST_H265_PROFILE_INVALID && matches) {
|
|
FormatRangeExtensionProfileMatch *m;
|
|
|
|
matches = g_list_sort (matches, (GCompareFunc) sort_fre_profile_matches);
|
|
m = matches->data;
|
|
result = m->profile->profile;
|
|
}
|
|
|
|
if (matches)
|
|
g_list_free_full (matches, g_free);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 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 (GstH265ProfileTierLevel * ptl)
|
|
{
|
|
if (ptl->profile_idc == GST_H265_PROFILE_IDC_MAIN
|
|
|| ptl->profile_compatibility_flag[1])
|
|
return GST_H265_PROFILE_MAIN;
|
|
|
|
if (ptl->profile_idc == GST_H265_PROFILE_IDC_MAIN_10
|
|
|| ptl->profile_compatibility_flag[2])
|
|
return GST_H265_PROFILE_MAIN_10;
|
|
|
|
if (ptl->profile_idc == GST_H265_PROFILE_IDC_MAIN_STILL_PICTURE
|
|
|| ptl->profile_compatibility_flag[3])
|
|
return GST_H265_PROFILE_MAIN_STILL_PICTURE;
|
|
|
|
return get_format_range_extension_profile (ptl);
|
|
}
|
|
|
|
/**
|
|
* 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_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_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_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_TRAIL_N
|
|
&& 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: (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: (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);
|
|
}
|