/* GStreamer * Copyright (C) 2015 Intel Corporation * Author: Sreerenj Balachandran * Copyright (C) 2019 Seungha Yang * Copyright (C) 2025 Seungha Yang * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gsth265reorder.h" #include "gsth264reorder.h" #include #include GST_DEBUG_CATEGORY_STATIC (gst_h265_reorder_debug); #define GST_CAT_DEFAULT gst_h265_reorder_debug struct _GstH265Reorder { GstObject parent; gboolean need_reorder; gint width; gint height; guint8 conformance_window_flag; gint crop_rect_width; gint crop_rect_height; gint crop_rect_x; gint crop_rect_y; gint fps_n; gint fps_d; guint nal_length_size; gboolean is_hevc; GstH265Parser *parser; GstH265Dpb *dpb; guint8 field_seq_flag; guint8 progressive_source_flag; guint8 interlaced_source_flag; GstH265SEIPicStructType cur_pic_struct; guint8 cur_source_scan_type; guint8 cur_duplicate_flag; gboolean no_output_of_prior_pics_flag; /* vps/sps/pps of the current slice */ const GstH265VPS *active_vps; const GstH265SPS *active_sps; const GstH265PPS *active_pps; guint32 SpsMaxLatencyPictures; GstH265Picture *current_picture; GstVideoCodecFrame *current_frame; /* Slice (slice header + nalu) currently being processed/decoded */ GstH265Slice current_slice; GstH265Slice prev_slice; GstH265Slice prev_independent_slice; GstH265Picture *RefPicSetStCurrBefore[16]; GstH265Picture *RefPicSetStCurrAfter[16]; GstH265Picture *RefPicSetStFoll[16]; GstH265Picture *RefPicSetLtCurr[16]; GstH265Picture *RefPicSetLtFoll[16]; guint NumPocStCurrBefore; guint NumPocStCurrAfter; guint NumPocStFoll; guint NumPocLtCurr; guint NumPocLtFoll; guint NumPicTotalCurr; gint32 poc; // PicOrderCntVal gint32 poc_msb; // PicOrderCntMsb gint32 poc_lsb; // pic_order_cnt_lsb (from slice_header()) gint32 prev_poc_msb; // prevPicOrderCntMsb gint32 prev_poc_lsb; // prevPicOrderCntLsb gint32 prev_tid0pic_poc_lsb; gint32 prev_tid0pic_poc_msb; gint32 PocStCurrBefore[16]; gint32 PocStCurrAfter[16]; gint32 PocStFoll[16]; gint32 PocLtCurr[16]; gint32 PocLtFoll[16]; /* PicOrderCount of the previously outputted frame */ gint last_output_poc; gboolean associated_irap_NoRaslOutputFlag; gboolean new_bitstream; gboolean prev_nal_is_eos; GArray *nalu; /* Split packetized data into actual nal chunks (for malformed stream) */ GArray *split_nalu; GArray *au_nalus; GPtrArray *frame_queue; GPtrArray *output_queue; guint32 system_num; guint32 present_num; GstClockTime latency; }; typedef struct { union { GstH265SPS sps; GstH265Slice slice; } unit; gboolean is_slice; } GstH265ReorderNalUnit; static void gst_h265_reorder_finalize (GObject * object); static gboolean gst_h265_reorder_start_current_picture (GstH265Reorder * self); #define gst_h265_reorder_parent_class parent_class G_DEFINE_TYPE (GstH265Reorder, gst_h265_reorder, GST_TYPE_OBJECT); static void gst_h265_reorder_class_init (GstH265ReorderClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gst_h265_reorder_finalize; GST_DEBUG_CATEGORY_INIT (gst_h265_reorder_debug, "h265reorder", 0, "h265reorder"); } static void gst_h265_reorder_clear_nalu (GstH265ReorderNalUnit * nalu) { if (!nalu) return; if (nalu->is_slice) gst_h265_slice_hdr_free (&nalu->unit.slice.header); memset (nalu, 0, sizeof (GstH265ReorderNalUnit)); } static void gst_h265_reorder_init (GstH265Reorder * self) { self->parser = gst_h265_parser_new (); self->dpb = gst_h265_dpb_new (); self->frame_queue = g_ptr_array_new_with_free_func ( (GDestroyNotify) gst_video_codec_frame_unref); self->output_queue = g_ptr_array_new_with_free_func ( (GDestroyNotify) gst_video_codec_frame_unref); self->nalu = g_array_sized_new (FALSE, TRUE, sizeof (GstH265ReorderNalUnit), 8); g_array_set_clear_func (self->nalu, (GDestroyNotify) gst_h265_reorder_clear_nalu); self->split_nalu = g_array_new (FALSE, FALSE, sizeof (GstH265NalUnit)); self->au_nalus = g_array_new (FALSE, FALSE, sizeof (GstH265NalUnit)); self->fps_n = 25; self->fps_d = 1; } static void gst_h265_reorder_clear_ref_pic_sets (GstH265Reorder * self) { guint i; for (i = 0; i < 16; i++) { gst_clear_h265_picture (&self->RefPicSetLtCurr[i]); gst_clear_h265_picture (&self->RefPicSetLtFoll[i]); gst_clear_h265_picture (&self->RefPicSetStCurrBefore[i]); gst_clear_h265_picture (&self->RefPicSetStCurrAfter[i]); gst_clear_h265_picture (&self->RefPicSetStFoll[i]); } } static void gst_h265_reorder_finalize (GObject * object) { GstH265Reorder *self = GST_H265_REORDER (object); gst_h265_parser_free (self->parser); g_ptr_array_unref (self->frame_queue); g_ptr_array_unref (self->output_queue); g_array_unref (self->nalu); g_array_unref (self->split_nalu); g_array_unref (self->au_nalus); gst_h265_reorder_clear_ref_pic_sets (self); gst_h265_dpb_free (self->dpb); G_OBJECT_CLASS (parent_class)->finalize (object); } static gboolean gst_h265_reorder_is_crop_rect_changed (GstH265Reorder * self, GstH265SPS * sps) { if (self->conformance_window_flag != sps->conformance_window_flag) return TRUE; if (self->crop_rect_width != sps->crop_rect_width) return TRUE; if (self->crop_rect_height != sps->crop_rect_height) return TRUE; if (self->crop_rect_x != sps->crop_rect_x) return TRUE; if (self->crop_rect_y != sps->crop_rect_y) return TRUE; return FALSE; } typedef struct { const gchar *level_name; guint8 level_idc; guint32 MaxLumaPs; } GstH265LevelLimits; /* *INDENT-OFF* */ /* Table A.8 - General tier and level limits */ static const GstH265LevelLimits level_limits[] = { /* level idc MaxLumaPs */ { "1", GST_H265_LEVEL_L1, 36864 }, { "2", GST_H265_LEVEL_L2, 122880 }, { "2.1", GST_H265_LEVEL_L2_1, 245760 }, { "3", GST_H265_LEVEL_L3, 552960 }, { "3.1", GST_H265_LEVEL_L3_1, 983040 }, { "4", GST_H265_LEVEL_L4, 2228224 }, { "4.1", GST_H265_LEVEL_L4_1, 2228224 }, { "5", GST_H265_LEVEL_L5, 8912896 }, { "5.1", GST_H265_LEVEL_L5_1, 8912896 }, { "5.2", GST_H265_LEVEL_L5_2, 8912896 }, { "6", GST_H265_LEVEL_L6, 35651584 }, { "6.1", GST_H265_LEVEL_L6_1, 35651584 }, { "6.2", GST_H265_LEVEL_L6_2, 35651584 }, }; /* *INDENT-ON* */ static gint gst_h265_reorder_get_max_dpb_size_from_sps (GstH265Reorder * self, GstH265SPS * sps) { guint i; guint PicSizeInSamplesY; /* Default is the worst case level 6.2 */ guint32 MaxLumaPS = G_MAXUINT32; gint MaxDpbPicBuf = 6; gint max_dpb_size; /* A.4.2, maxDpbPicBuf is equal to 6 for all profiles where the value of * sps_curr_pic_ref_enabled_flag is required to be equal to 0 and 7 for all * profiles where the value of sps_curr_pic_ref_enabled_flag is not required * to be equal to 0 */ if (sps->sps_scc_extension_flag) { /* sps_curr_pic_ref_enabled_flag could be non-zero only if profile is SCC */ MaxDpbPicBuf = 7; } /* Unknown level */ if (sps->profile_tier_level.level_idc == 0) return 16; PicSizeInSamplesY = sps->width * sps->height; for (i = 0; i < G_N_ELEMENTS (level_limits); i++) { if (sps->profile_tier_level.level_idc <= level_limits[i].level_idc) { if (PicSizeInSamplesY <= level_limits[i].MaxLumaPs) { MaxLumaPS = level_limits[i].MaxLumaPs; } else { GST_DEBUG_OBJECT (self, "%u (%dx%d) exceeds allowed max luma sample for level \"%s\" %u", PicSizeInSamplesY, sps->width, sps->height, level_limits[i].level_name, level_limits[i].MaxLumaPs); } break; } } /* Unknown level */ if (MaxLumaPS == G_MAXUINT32) return 16; /* A.4.2 */ if (PicSizeInSamplesY <= (MaxLumaPS >> 2)) max_dpb_size = MaxDpbPicBuf * 4; else if (PicSizeInSamplesY <= (MaxLumaPS >> 1)) max_dpb_size = MaxDpbPicBuf * 2; else if (PicSizeInSamplesY <= ((3 * MaxLumaPS) >> 2)) max_dpb_size = (MaxDpbPicBuf * 4) / 3; else max_dpb_size = MaxDpbPicBuf; max_dpb_size = MIN (max_dpb_size, 16); /* MaxDpbSize is not an actual maximum required buffer size. * Instead, it indicates upper bound for other syntax elements, such as * sps_max_dec_pic_buffering_minus1. If this bitstream can satisfy * the requirement, use this as our dpb size */ if (sps->max_dec_pic_buffering_minus1[sps->max_sub_layers_minus1] + 1 <= max_dpb_size) { GST_DEBUG_OBJECT (self, "max_dec_pic_buffering_minus1 %d < MaxDpbSize %d", sps->max_dec_pic_buffering_minus1[sps->max_sub_layers_minus1], max_dpb_size); max_dpb_size = sps->max_dec_pic_buffering_minus1[sps->max_sub_layers_minus1] + 1; } else { /* not reliable values, use 16 */ max_dpb_size = 16; } return max_dpb_size; } static gboolean gst_h265_reorder_process_sps (GstH265Reorder * self, GstH265SPS * sps) { gint max_dpb_size; gint prev_max_dpb_size; guint8 field_seq_flag = 0; guint8 progressive_source_flag = 0; guint8 interlaced_source_flag = 0; guint frames_delay; max_dpb_size = gst_h265_reorder_get_max_dpb_size_from_sps (self, sps); if (sps->vui_parameters_present_flag) field_seq_flag = sps->vui_params.field_seq_flag; progressive_source_flag = sps->profile_tier_level.progressive_source_flag; interlaced_source_flag = sps->profile_tier_level.interlaced_source_flag; prev_max_dpb_size = gst_h265_dpb_get_max_num_pics (self->dpb); if (self->width != sps->width || self->height != sps->height || prev_max_dpb_size != max_dpb_size || self->field_seq_flag != field_seq_flag || self->progressive_source_flag != progressive_source_flag || self->interlaced_source_flag != interlaced_source_flag || gst_h265_reorder_is_crop_rect_changed (self, sps)) { GST_DEBUG_OBJECT (self, "SPS updated, resolution: %dx%d -> %dx%d, dpb size: %d -> %d, " "field_seq_flag: %d -> %d, progressive_source_flag: %d -> %d, " "interlaced_source_flag: %d -> %d", self->width, self->height, sps->width, sps->height, prev_max_dpb_size, max_dpb_size, self->field_seq_flag, field_seq_flag, self->progressive_source_flag, progressive_source_flag, self->interlaced_source_flag, interlaced_source_flag); gst_h265_reorder_drain (self); self->width = sps->width; self->height = sps->height; self->conformance_window_flag = sps->conformance_window_flag; self->crop_rect_width = sps->crop_rect_width; self->crop_rect_height = sps->crop_rect_height; self->crop_rect_x = sps->crop_rect_x; self->crop_rect_y = sps->crop_rect_y; self->field_seq_flag = field_seq_flag; self->progressive_source_flag = progressive_source_flag; self->interlaced_source_flag = interlaced_source_flag; gst_h265_dpb_set_max_num_pics (self->dpb, max_dpb_size); GST_DEBUG_OBJECT (self, "Set DPB max size %d", max_dpb_size); } if (sps->max_latency_increase_plus1[sps->max_sub_layers_minus1]) { self->SpsMaxLatencyPictures = sps->max_num_reorder_pics[sps->max_sub_layers_minus1] + sps->max_latency_increase_plus1[sps->max_sub_layers_minus1] - 1; } else { self->SpsMaxLatencyPictures = 0; } frames_delay = sps->max_num_reorder_pics[sps->max_sub_layers_minus1]; self->latency = gst_util_uint64_scale_int (frames_delay * GST_SECOND, self->fps_d, self->fps_n); return TRUE; } static GstH265ParserResult gst_h265_reorder_parse_sei (GstH265Reorder * self, GstH265NalUnit * nalu) { GstH265ParserResult pres; GArray *messages = NULL; guint i; pres = gst_h265_parser_parse_sei (self->parser, nalu, &messages); if (pres != GST_H265_PARSER_OK) { GST_WARNING_OBJECT (self, "Failed to parse SEI, result %d", pres); /* XXX: Ignore error from SEI parsing, it might be malformed bitstream, * or our fault. But shouldn't be critical */ g_clear_pointer (&messages, g_array_unref); return GST_H265_PARSER_OK; } for (i = 0; i < messages->len; i++) { GstH265SEIMessage *sei = &g_array_index (messages, GstH265SEIMessage, i); switch (sei->payloadType) { case GST_H265_SEI_PIC_TIMING: self->cur_pic_struct = sei->payload.pic_timing.pic_struct; self->cur_source_scan_type = sei->payload.pic_timing.source_scan_type; self->cur_duplicate_flag = sei->payload.pic_timing.duplicate_flag; GST_TRACE_OBJECT (self, "Picture Timing SEI, pic_struct: %d, source_scan_type: %d, " "duplicate_flag: %d", self->cur_pic_struct, self->cur_source_scan_type, self->cur_duplicate_flag); break; default: break; } } g_array_free (messages, TRUE); GST_LOG_OBJECT (self, "SEI parsed"); return GST_H265_PARSER_OK; } static gboolean gst_h265_reorder_preprocess_slice (GstH265Reorder * self, GstH265Slice * slice) { const GstH265SliceHdr *slice_hdr = &slice->header; if (self->current_picture && slice_hdr->first_slice_segment_in_pic_flag) { GST_WARNING_OBJECT (self, "Current picture is not finished but slice header has " "first_slice_segment_in_pic_flag"); return FALSE; } return TRUE; } static gboolean gst_h265_reorder_process_slice (GstH265Reorder * self, GstH265Slice * slice) { self->current_slice = *slice; if (self->current_slice.header.dependent_slice_segment_flag) { GstH265SliceHdr *slice_hdr = &self->current_slice.header; GstH265SliceHdr *indep_slice_hdr = &self->prev_independent_slice.header; memcpy (&slice_hdr->type, &indep_slice_hdr->type, G_STRUCT_OFFSET (GstH265SliceHdr, num_entry_point_offsets) - G_STRUCT_OFFSET (GstH265SliceHdr, type)); } else { self->prev_independent_slice = self->current_slice; memset (&self->prev_independent_slice.nalu, 0, sizeof (GstH265NalUnit)); } if (!gst_h265_reorder_preprocess_slice (self, &self->current_slice)) return FALSE; /* The used SPS may not be the latest parsed one, make * sure we have updated it before decode the frame */ if (!gst_h265_reorder_process_sps (self, self->current_slice.header.pps->sps)) { GST_WARNING_OBJECT (self, "Failed to process sps"); return FALSE; } self->active_pps = self->current_slice.header.pps; self->active_sps = self->active_pps->sps; if (!self->current_picture) { GstH265Picture *picture; g_assert (self->current_frame); picture = gst_h265_picture_new (); /* This allows accessing the frame from the picture. */ GST_CODEC_PICTURE_FRAME_NUMBER (picture) = self->current_frame->system_frame_number; self->current_picture = picture; if (!gst_h265_reorder_start_current_picture (self)) { GST_WARNING_OBJECT (self, "start picture failed"); return FALSE; } } return TRUE; } static GstH265ParserResult gst_h265_reorder_parse_slice (GstH265Reorder * self, GstH265NalUnit * nalu) { GstH265ParserResult pres; GstH265Slice slice; GstH265ReorderNalUnit decoder_nalu; memset (&slice, 0, sizeof (GstH265Slice)); pres = gst_h265_parser_parse_slice_hdr (self->parser, nalu, &slice.header); if (pres != GST_H265_PARSER_OK) return pres; slice.nalu = *nalu; if (nalu->type >= GST_H265_NAL_SLICE_BLA_W_LP && nalu->type <= GST_H265_NAL_SLICE_CRA_NUT) { slice.rap_pic_flag = TRUE; } /* NoRaslOutputFlag == 1 if the current picture is * 1) an IDR picture * 2) a BLA picture * 3) a CRA picture that is the first access unit in the bitstream * 4) first picture that follows an end of sequence NAL unit in decoding order * 5) has HandleCraAsBlaFlag == 1 (set by external means, so not considering ) */ if (GST_H265_IS_NAL_TYPE_IDR (nalu->type) || GST_H265_IS_NAL_TYPE_BLA (nalu->type) || (GST_H265_IS_NAL_TYPE_CRA (nalu->type) && self->new_bitstream) || self->prev_nal_is_eos) { slice.no_rasl_output_flag = TRUE; } if (GST_H265_IS_NAL_TYPE_IRAP (nalu->type)) { slice.intra_pic_flag = TRUE; if (slice.no_rasl_output_flag && !self->new_bitstream) { /* C 3.2 */ slice.clear_dpb = TRUE; if (nalu->type == GST_H265_NAL_SLICE_CRA_NUT) { slice.no_output_of_prior_pics_flag = TRUE; } else { slice.no_output_of_prior_pics_flag = slice.header.no_output_of_prior_pics_flag; } } } if (slice.no_output_of_prior_pics_flag) self->no_output_of_prior_pics_flag = TRUE; decoder_nalu.unit.slice = slice; decoder_nalu.is_slice = TRUE; g_array_append_val (self->nalu, decoder_nalu); return GST_H265_PARSER_OK; } static GstH265ParserResult gst_h265_reorder_parse_nalu (GstH265Reorder * self, GstH265NalUnit * nalu) { GstH265VPS vps; GstH265SPS sps; GstH265PPS pps; GstH265ParserResult ret = GST_H265_PARSER_OK; GstH265ReorderNalUnit decoder_nalu; GST_LOG_OBJECT (self, "Parsed nal type: %d, offset %d, size %d", nalu->type, nalu->offset, nalu->size); switch (nalu->type) { case GST_H265_NAL_VPS: ret = gst_h265_parser_parse_vps (self->parser, nalu, &vps); break; case GST_H265_NAL_SPS: ret = gst_h265_parser_parse_sps (self->parser, nalu, &sps, TRUE); if (ret != GST_H265_PARSER_OK) break; memset (&decoder_nalu, 0, sizeof (GstH265ReorderNalUnit)); decoder_nalu.unit.sps = sps; g_array_append_val (self->nalu, decoder_nalu); break; case GST_H265_NAL_PPS: ret = gst_h265_parser_parse_pps (self->parser, nalu, &pps); break; case GST_H265_NAL_PREFIX_SEI: case GST_H265_NAL_SUFFIX_SEI: ret = gst_h265_reorder_parse_sei (self, nalu); break; case GST_H265_NAL_SLICE_TRAIL_N: case GST_H265_NAL_SLICE_TRAIL_R: case GST_H265_NAL_SLICE_TSA_N: case GST_H265_NAL_SLICE_TSA_R: case GST_H265_NAL_SLICE_STSA_N: case GST_H265_NAL_SLICE_STSA_R: case GST_H265_NAL_SLICE_RADL_N: case GST_H265_NAL_SLICE_RADL_R: case GST_H265_NAL_SLICE_RASL_N: case GST_H265_NAL_SLICE_RASL_R: case GST_H265_NAL_SLICE_BLA_W_LP: case GST_H265_NAL_SLICE_BLA_W_RADL: case GST_H265_NAL_SLICE_BLA_N_LP: case GST_H265_NAL_SLICE_IDR_W_RADL: case GST_H265_NAL_SLICE_IDR_N_LP: case GST_H265_NAL_SLICE_CRA_NUT: ret = gst_h265_reorder_parse_slice (self, nalu); self->new_bitstream = FALSE; self->prev_nal_is_eos = FALSE; break; case GST_H265_NAL_EOB: self->new_bitstream = TRUE; break; case GST_H265_NAL_EOS: self->prev_nal_is_eos = TRUE; break; default: break; } return ret; } static gboolean gst_h265_reorder_decode_nalu (GstH265Reorder * self, GstH265ReorderNalUnit * nalu) { if (nalu->is_slice) return gst_h265_reorder_process_slice (self, &nalu->unit.slice); return TRUE; } static gboolean gst_h265_reorder_parse_codec_data (GstH265Reorder * self, const guint8 * data, gsize size) { GstH265Parser *parser = self->parser; GstH265ParserResult pres; gboolean ret = FALSE; GstH265VPS vps; GstH265SPS sps; GstH265PPS pps; GstH265DecoderConfigRecord *config = NULL; guint i, j; pres = gst_h265_parser_parse_decoder_config_record (parser, data, size, &config); if (pres != GST_H265_PARSER_OK) { GST_WARNING_OBJECT (self, "Failed to parse hvcC data"); return FALSE; } self->nal_length_size = config->length_size_minus_one + 1; GST_DEBUG_OBJECT (self, "nal length size %u", self->nal_length_size); for (i = 0; i < config->nalu_array->len; i++) { GstH265DecoderConfigRecordNalUnitArray *array = &g_array_index (config->nalu_array, GstH265DecoderConfigRecordNalUnitArray, i); for (j = 0; j < array->nalu->len; j++) { GstH265NalUnit *nalu = &g_array_index (array->nalu, GstH265NalUnit, j); switch (nalu->type) { case GST_H265_NAL_VPS: pres = gst_h265_parser_parse_vps (parser, nalu, &vps); if (pres != GST_H265_PARSER_OK) { GST_WARNING_OBJECT (self, "Failed to parse VPS"); goto out; } break; case GST_H265_NAL_SPS: pres = gst_h265_parser_parse_sps (parser, nalu, &sps, TRUE); if (pres != GST_H265_PARSER_OK) { GST_WARNING_OBJECT (self, "Failed to parse SPS"); goto out; } break; case GST_H265_NAL_PPS: pres = gst_h265_parser_parse_pps (parser, nalu, &pps); if (pres != GST_H265_PARSER_OK) { GST_WARNING_OBJECT (self, "Failed to parse PPS"); goto out; } break; default: break; } } } ret = TRUE; out: gst_h265_decoder_config_record_free (config); return ret; } gboolean gst_h265_reorder_set_caps (GstH265Reorder * self, GstCaps * caps, GstClockTime * latency) { GstStructure *s; const gchar *str; const GValue *codec_data; gboolean ret = TRUE; gint fps_n, fps_d; GST_DEBUG_OBJECT (self, "Set caps %" GST_PTR_FORMAT, caps); self->nal_length_size = 4; self->is_hevc = FALSE; s = gst_caps_get_structure (caps, 0); str = gst_structure_get_string (s, "stream-format"); if (str && (g_strcmp0 (str, "hvc1") == 0 || g_strcmp0 (str, "hev1") == 0)) self->is_hevc = TRUE; if (gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d) && fps_n > 0 && fps_d > 0) { self->fps_n = fps_n; self->fps_d = fps_d; } else { self->fps_n = 25; self->fps_d = 1; } codec_data = gst_structure_get_value (s, "codec_data"); if (codec_data && G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) { GstBuffer *buf = gst_value_get_buffer (codec_data); GstMapInfo info; if (gst_buffer_map (buf, &info, GST_MAP_READ)) { ret = gst_h265_reorder_parse_codec_data (self, info.data, info.size); gst_buffer_unmap (buf, &info); } else { GST_ERROR_OBJECT (self, "Couldn't map codec data"); ret = FALSE; } } if (self->need_reorder) *latency = self->latency; else *latency = 0; return ret; } static gboolean gst_h265_reorder_fill_picture_from_slice (GstH265Reorder * self, const GstH265Slice * slice, GstH265Picture * picture) { const GstH265SliceHdr *slice_hdr = &slice->header; const GstH265NalUnit *nalu = &slice->nalu; picture->RapPicFlag = slice->rap_pic_flag; picture->NoRaslOutputFlag = slice->no_rasl_output_flag; picture->IntraPicFlag = slice->intra_pic_flag; picture->NoOutputOfPriorPicsFlag = slice->no_output_of_prior_pics_flag; if (picture->IntraPicFlag) { self->associated_irap_NoRaslOutputFlag = picture->NoRaslOutputFlag; } if (GST_H265_IS_NAL_TYPE_RASL (nalu->type) && self->associated_irap_NoRaslOutputFlag) { picture->output_flag = FALSE; } else { picture->output_flag = slice_hdr->pic_output_flag; } return TRUE; } #define RSV_VCL_N10 10 #define RSV_VCL_N12 12 #define RSV_VCL_N14 14 static gboolean nal_is_ref (guint8 nal_type) { gboolean ret = FALSE; switch (nal_type) { case GST_H265_NAL_SLICE_TRAIL_N: case GST_H265_NAL_SLICE_TSA_N: case GST_H265_NAL_SLICE_STSA_N: case GST_H265_NAL_SLICE_RADL_N: case GST_H265_NAL_SLICE_RASL_N: case RSV_VCL_N10: case RSV_VCL_N12: case RSV_VCL_N14: ret = FALSE; break; default: ret = TRUE; break; } return ret; } static gboolean gst_h265_reorder_calculate_poc (GstH265Reorder * self, const GstH265Slice * slice, GstH265Picture * picture) { const GstH265SliceHdr *slice_hdr = &slice->header; const GstH265NalUnit *nalu = &slice->nalu; const GstH265SPS *sps = self->active_sps; gint32 MaxPicOrderCntLsb = 1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4); gboolean is_irap; self->prev_poc_lsb = self->poc_lsb; self->prev_poc_msb = self->poc_msb; is_irap = GST_H265_IS_NAL_TYPE_IRAP (nalu->type); if (!(is_irap && picture->NoRaslOutputFlag)) { self->prev_poc_lsb = self->prev_tid0pic_poc_lsb; self->prev_poc_msb = self->prev_tid0pic_poc_msb; } /* Finding PicOrderCntMsb */ if (is_irap && picture->NoRaslOutputFlag) { self->poc_msb = 0; } else { /* (8-1) */ if ((slice_hdr->pic_order_cnt_lsb < self->prev_poc_lsb) && ((self->prev_poc_lsb - slice_hdr->pic_order_cnt_lsb) >= (MaxPicOrderCntLsb / 2))) self->poc_msb = self->prev_poc_msb + MaxPicOrderCntLsb; else if ((slice_hdr->pic_order_cnt_lsb > self->prev_poc_lsb) && ((slice_hdr->pic_order_cnt_lsb - self->prev_poc_lsb) > (MaxPicOrderCntLsb / 2))) self->poc_msb = self->prev_poc_msb - MaxPicOrderCntLsb; else self->poc_msb = self->prev_poc_msb; } /* (8-2) */ self->poc = picture->pic_order_cnt = self->poc_msb + slice_hdr->pic_order_cnt_lsb; self->poc_lsb = picture->pic_order_cnt_lsb = slice_hdr->pic_order_cnt_lsb; if (GST_H265_IS_NAL_TYPE_IDR (nalu->type)) { picture->pic_order_cnt = 0; picture->pic_order_cnt_lsb = 0; self->poc_lsb = 0; self->poc_msb = 0; self->prev_poc_lsb = 0; self->prev_poc_msb = 0; self->prev_tid0pic_poc_lsb = 0; self->prev_tid0pic_poc_msb = 0; } GST_LOG_OBJECT (self, "PicOrderCntVal %d, (lsb %d)", picture->pic_order_cnt, picture->pic_order_cnt_lsb); if (nalu->temporal_id_plus1 == 1 && !GST_H265_IS_NAL_TYPE_RASL (nalu->type) && !GST_H265_IS_NAL_TYPE_RADL (nalu->type) && nal_is_ref (nalu->type)) { self->prev_tid0pic_poc_lsb = slice_hdr->pic_order_cnt_lsb; self->prev_tid0pic_poc_msb = self->poc_msb; } return TRUE; } static gboolean gst_h265_reorder_init_current_picture (GstH265Reorder * self) { if (!gst_h265_reorder_fill_picture_from_slice (self, &self->current_slice, self->current_picture)) { return FALSE; } if (!gst_h265_reorder_calculate_poc (self, &self->current_slice, self->current_picture)) return FALSE; /* Use picture struct parsed from picture timing SEI */ self->current_picture->pic_struct = self->cur_pic_struct; self->current_picture->source_scan_type = self->cur_source_scan_type; self->current_picture->duplicate_flag = self->cur_duplicate_flag; return TRUE; } static gboolean has_entry_in_rps (GstH265Picture * dpb_pic, GstH265Picture ** rps_list, guint rps_list_length) { guint i; if (!dpb_pic || !rps_list || !rps_list_length) return FALSE; for (i = 0; i < rps_list_length; i++) { if (rps_list[i] && rps_list[i]->pic_order_cnt == dpb_pic->pic_order_cnt) return TRUE; } return FALSE; } static void gst_h265_reorder_derive_and_mark_rps (GstH265Reorder * self, GstH265Picture * picture, gint32 * CurrDeltaPocMsbPresentFlag, gint32 * FollDeltaPocMsbPresentFlag) { guint i; GArray *dpb_array; gst_h265_reorder_clear_ref_pic_sets (self); /* (8-6) */ for (i = 0; i < self->NumPocLtCurr; i++) { if (!CurrDeltaPocMsbPresentFlag[i]) { self->RefPicSetLtCurr[i] = gst_h265_dpb_get_ref_by_poc_lsb (self->dpb, self->PocLtCurr[i]); } else { self->RefPicSetLtCurr[i] = gst_h265_dpb_get_ref_by_poc (self->dpb, self->PocLtCurr[i]); } } for (i = 0; i < self->NumPocLtFoll; i++) { if (!FollDeltaPocMsbPresentFlag[i]) { self->RefPicSetLtFoll[i] = gst_h265_dpb_get_ref_by_poc_lsb (self->dpb, self->PocLtFoll[i]); } else { self->RefPicSetLtFoll[i] = gst_h265_dpb_get_ref_by_poc (self->dpb, self->PocLtFoll[i]); } } /* Mark all ref pics in RefPicSetLtCurr and RefPicSetLtFol as long_term_refs */ for (i = 0; i < self->NumPocLtCurr; i++) { if (self->RefPicSetLtCurr[i]) { self->RefPicSetLtCurr[i]->ref = TRUE; self->RefPicSetLtCurr[i]->long_term = TRUE; } } for (i = 0; i < self->NumPocLtFoll; i++) { if (self->RefPicSetLtFoll[i]) { self->RefPicSetLtFoll[i]->ref = TRUE; self->RefPicSetLtFoll[i]->long_term = TRUE; } } /* (8-7) */ for (i = 0; i < self->NumPocStCurrBefore; i++) { self->RefPicSetStCurrBefore[i] = gst_h265_dpb_get_short_ref_by_poc (self->dpb, self->PocStCurrBefore[i]); } for (i = 0; i < self->NumPocStCurrAfter; i++) { self->RefPicSetStCurrAfter[i] = gst_h265_dpb_get_short_ref_by_poc (self->dpb, self->PocStCurrAfter[i]); } for (i = 0; i < self->NumPocStFoll; i++) { self->RefPicSetStFoll[i] = gst_h265_dpb_get_short_ref_by_poc (self->dpb, self->PocStFoll[i]); } /* Mark all dpb pics not beloging to RefPicSet*[] as unused for ref */ dpb_array = gst_h265_dpb_get_pictures_all (self->dpb); for (i = 0; i < dpb_array->len; i++) { GstH265Picture *dpb_pic = g_array_index (dpb_array, GstH265Picture *, i); if (dpb_pic && !has_entry_in_rps (dpb_pic, self->RefPicSetLtCurr, self->NumPocLtCurr) && !has_entry_in_rps (dpb_pic, self->RefPicSetLtFoll, self->NumPocLtFoll) && !has_entry_in_rps (dpb_pic, self->RefPicSetStCurrAfter, self->NumPocStCurrAfter) && !has_entry_in_rps (dpb_pic, self->RefPicSetStCurrBefore, self->NumPocStCurrBefore) && !has_entry_in_rps (dpb_pic, self->RefPicSetStFoll, self->NumPocStFoll)) { GST_LOG_OBJECT (self, "Mark Picture %p (poc %d) as non-ref", dpb_pic, dpb_pic->pic_order_cnt); dpb_pic->ref = FALSE; dpb_pic->long_term = FALSE; } } g_array_unref (dpb_array); } static gboolean gst_h265_reorder_prepare_rps (GstH265Reorder * self, const GstH265Slice * slice, GstH265Picture * picture) { gint32 CurrDeltaPocMsbPresentFlag[16] = { 0, }; gint32 FollDeltaPocMsbPresentFlag[16] = { 0, }; const GstH265SliceHdr *slice_hdr = &slice->header; const GstH265NalUnit *nalu = &slice->nalu; const GstH265SPS *sps = self->active_sps; guint32 MaxPicOrderCntLsb = 1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4); gint i, j, k; /* if it is an irap pic, set all ref pics in dpb as unused for ref */ if (GST_H265_IS_NAL_TYPE_IRAP (nalu->type) && picture->NoRaslOutputFlag) { GST_DEBUG_OBJECT (self, "Mark all pictures in DPB as non-ref"); gst_h265_dpb_mark_all_non_ref (self->dpb); } /* Reset everything for IDR */ if (GST_H265_IS_NAL_TYPE_IDR (nalu->type)) { memset (self->PocStCurrBefore, 0, sizeof (self->PocStCurrBefore)); memset (self->PocStCurrAfter, 0, sizeof (self->PocStCurrAfter)); memset (self->PocStFoll, 0, sizeof (self->PocStFoll)); memset (self->PocLtCurr, 0, sizeof (self->PocLtCurr)); memset (self->PocLtFoll, 0, sizeof (self->PocLtFoll)); self->NumPocStCurrBefore = self->NumPocStCurrAfter = self->NumPocStFoll = 0; self->NumPocLtCurr = self->NumPocLtFoll = 0; } else { const GstH265ShortTermRefPicSet *stRefPic = NULL; gint32 num_lt_pics, pocLt; gint32 PocLsbLt[16] = { 0, }; gint32 UsedByCurrPicLt[16] = { 0, }; gint32 DeltaPocMsbCycleLt[16] = { 0, }; gint numtotalcurr = 0; /* this is based on CurrRpsIdx described in spec */ if (!slice_hdr->short_term_ref_pic_set_sps_flag) stRefPic = &slice_hdr->short_term_ref_pic_sets; else if (sps->num_short_term_ref_pic_sets) stRefPic = &sps->short_term_ref_pic_set[slice_hdr->short_term_ref_pic_set_idx]; if (stRefPic == NULL) return FALSE; GST_LOG_OBJECT (self, "NumDeltaPocs: %d, NumNegativePics: %d, NumPositivePics %d", stRefPic->NumDeltaPocs, stRefPic->NumNegativePics, stRefPic->NumPositivePics); for (i = 0, j = 0, k = 0; i < stRefPic->NumNegativePics; i++) { if (stRefPic->UsedByCurrPicS0[i]) { self->PocStCurrBefore[j++] = picture->pic_order_cnt + stRefPic->DeltaPocS0[i]; numtotalcurr++; } else self->PocStFoll[k++] = picture->pic_order_cnt + stRefPic->DeltaPocS0[i]; } self->NumPocStCurrBefore = j; for (i = 0, j = 0; i < stRefPic->NumPositivePics; i++) { if (stRefPic->UsedByCurrPicS1[i]) { self->PocStCurrAfter[j++] = picture->pic_order_cnt + stRefPic->DeltaPocS1[i]; numtotalcurr++; } else self->PocStFoll[k++] = picture->pic_order_cnt + stRefPic->DeltaPocS1[i]; } self->NumPocStCurrAfter = j; self->NumPocStFoll = k; num_lt_pics = slice_hdr->num_long_term_sps + slice_hdr->num_long_term_pics; /* The variables PocLsbLt[i] and UsedByCurrPicLt[i] are derived as follows: */ for (i = 0; i < num_lt_pics; i++) { if (i < slice_hdr->num_long_term_sps) { PocLsbLt[i] = sps->lt_ref_pic_poc_lsb_sps[slice_hdr->lt_idx_sps[i]]; UsedByCurrPicLt[i] = sps->used_by_curr_pic_lt_sps_flag[slice_hdr->lt_idx_sps[i]]; } else { PocLsbLt[i] = slice_hdr->poc_lsb_lt[i]; UsedByCurrPicLt[i] = slice_hdr->used_by_curr_pic_lt_flag[i]; } if (UsedByCurrPicLt[i]) numtotalcurr++; } self->NumPicTotalCurr = numtotalcurr; /* The variable DeltaPocMsbCycleLt[i] is derived as follows: (7-38) */ for (i = 0; i < num_lt_pics; i++) { if (i == 0 || i == slice_hdr->num_long_term_sps) DeltaPocMsbCycleLt[i] = slice_hdr->delta_poc_msb_cycle_lt[i]; else DeltaPocMsbCycleLt[i] = slice_hdr->delta_poc_msb_cycle_lt[i] + DeltaPocMsbCycleLt[i - 1]; } /* (8-5) */ for (i = 0, j = 0, k = 0; i < num_lt_pics; i++) { pocLt = PocLsbLt[i]; if (slice_hdr->delta_poc_msb_present_flag[i]) pocLt += picture->pic_order_cnt - DeltaPocMsbCycleLt[i] * MaxPicOrderCntLsb - slice_hdr->pic_order_cnt_lsb; if (UsedByCurrPicLt[i]) { self->PocLtCurr[j] = pocLt; CurrDeltaPocMsbPresentFlag[j++] = slice_hdr->delta_poc_msb_present_flag[i]; } else { self->PocLtFoll[k] = pocLt; FollDeltaPocMsbPresentFlag[k++] = slice_hdr->delta_poc_msb_present_flag[i]; } } self->NumPocLtCurr = j; self->NumPocLtFoll = k; } GST_LOG_OBJECT (self, "NumPocStCurrBefore: %d", self->NumPocStCurrBefore); GST_LOG_OBJECT (self, "NumPocStCurrAfter: %d", self->NumPocStCurrAfter); GST_LOG_OBJECT (self, "NumPocStFoll: %d", self->NumPocStFoll); GST_LOG_OBJECT (self, "NumPocLtCurr: %d", self->NumPocLtCurr); GST_LOG_OBJECT (self, "NumPocLtFoll: %d", self->NumPocLtFoll); GST_LOG_OBJECT (self, "NumPicTotalCurr: %d", self->NumPicTotalCurr); /* the derivation process for the RPS and the picture marking */ gst_h265_reorder_derive_and_mark_rps (self, picture, CurrDeltaPocMsbPresentFlag, FollDeltaPocMsbPresentFlag); return TRUE; } static void gst_h265_reorder_set_output_buffer (GstH265Reorder * self, guint frame_num) { gsize i, j; for (i = 0; i < self->frame_queue->len; i++) { GstVideoCodecFrame *frame = g_ptr_array_index (self->frame_queue, i); if (frame->system_frame_number != frame_num) continue; /* Copy frame at present index to */ if (!frame->output_buffer) { GST_LOG_OBJECT (self, "decoding order: %u, display order: %u", frame_num, self->present_num); frame->presentation_frame_number = self->present_num; self->present_num++; for (j = 0; j < self->frame_queue->len; j++) { GstVideoCodecFrame *other_frame = g_ptr_array_index (self->frame_queue, j); if (other_frame->system_frame_number == frame->presentation_frame_number) { frame->output_buffer = gst_buffer_ref (other_frame->input_buffer); return; } } } break; } } static void gst_h265_reorder_output_picture (GstH265Reorder * self, GstH265Picture * picture) { guint frame_num = GST_CODEC_PICTURE_FRAME_NUMBER (picture); gst_h265_reorder_set_output_buffer (self, frame_num); gst_h265_picture_unref (picture); /* Move completed frames to output queue */ while (self->frame_queue->len > 0) { GstVideoCodecFrame *frame = g_ptr_array_index (self->frame_queue, 0); if (!frame->output_buffer) break; frame = g_ptr_array_steal_index (self->frame_queue, 0); g_ptr_array_add (self->output_queue, frame); } } GstH265Reorder * gst_h265_reorder_new (gboolean need_reorder) { GstH265Reorder *self = g_object_new (GST_TYPE_H265_REORDER, NULL); gst_object_ref_sink (self); self->need_reorder = need_reorder; return self; } void gst_h265_reorder_drain (GstH265Reorder * reorder) { GstH265Picture *picture; while ((picture = gst_h265_dpb_bump (reorder->dpb, TRUE)) != NULL) { gst_h265_reorder_output_picture (reorder, picture); } gst_h265_dpb_clear (reorder->dpb); /* Frame queue should be empty or holding only current frame */ while (reorder->frame_queue->len > 0) { GstVideoCodecFrame *frame = g_ptr_array_index (reorder->frame_queue, 0); if (frame == reorder->current_frame) break; GST_WARNING_OBJECT (reorder, "Remaining frame after drain %" GST_PTR_FORMAT, frame->input_buffer); /* Move to output queue anyway */ frame->output_buffer = gst_buffer_ref (frame->input_buffer); frame = g_ptr_array_steal_index (reorder->frame_queue, 0); g_ptr_array_add (reorder->output_queue, frame); } /* presentation number */ if (reorder->current_frame) reorder->present_num = reorder->current_frame->system_frame_number; else reorder->present_num = reorder->system_num; } /* C.5.2.2 */ static gboolean gst_h265_reorder_dpb_init (GstH265Reorder * self, const GstH265Slice * slice, GstH265Picture * picture) { const GstH265SPS *sps = self->active_sps; GstH265Picture *to_output; /* C 3.2 */ if (slice->clear_dpb) { /* Ignores NoOutputOfPriorPicsFlag and drain all */ gst_h265_reorder_drain (self); } else { /* TODO: According to 7.4.3.3.3, TwoVersionsOfCurrDecPicFlag * should be considered. * * NOTE: (See 8.1.3) if TwoVersionsOfCurrDecPicFlag is 1, * current picture requires two picture buffers allocated in DPB storage, * one is decoded picture *after* in-loop filter, and the other is * decoded picture *before* in-loop filter, so that current picture * can be used as a reference of the current picture * (e.g., intra block copy method in SCC). * Here TwoVersionsOfCurrDecPicFlag takes effect in order to ensure * at least two empty DPB buffer before starting current picture decoding. * * However, two DPB picture allocation is not implemented * in current baseclass (which would imply that we are doing reference * picture management wrongly in case of SCC). * Let's ignore TwoVersionsOfCurrDecPicFlag for now */ guint max_dec_pic_buffering = sps->max_dec_pic_buffering_minus1[sps->max_sub_layers_minus1] + 1; gst_h265_dpb_delete_unused (self->dpb); while (gst_h265_dpb_needs_bump (self->dpb, sps->max_num_reorder_pics[sps->max_sub_layers_minus1], self->SpsMaxLatencyPictures, max_dec_pic_buffering)) { to_output = gst_h265_dpb_bump (self->dpb, FALSE); /* Something wrong... */ if (!to_output) { GST_WARNING_OBJECT (self, "Bumping is needed but no picture to output"); break; } gst_h265_reorder_output_picture (self, to_output); } } return TRUE; } static gboolean gst_h265_reorder_start_current_picture (GstH265Reorder * self) { g_assert (self->current_picture != NULL); g_assert (self->active_sps != NULL); g_assert (self->active_pps != NULL); if (!gst_h265_reorder_init_current_picture (self)) return FALSE; /* Drop all RASL pictures having NoRaslOutputFlag is TRUE for the * associated IRAP picture */ if (GST_H265_IS_NAL_TYPE_RASL (self->current_slice.nalu.type) && self->associated_irap_NoRaslOutputFlag) { GST_DEBUG_OBJECT (self, "Ignores associated_irap_NoRaslOutputFlag"); } if (!gst_h265_reorder_prepare_rps (self, &self->current_slice, self->current_picture)) { GST_WARNING_OBJECT (self, "Failed to prepare ref pic set"); gst_clear_h265_picture (&self->current_picture); return FALSE; } if (!gst_h265_reorder_dpb_init (self, &self->current_slice, self->current_picture)) { GST_WARNING_OBJECT (self, "Failed to init dpb"); gst_clear_h265_picture (&self->current_picture); return FALSE; } return TRUE; } static void gst_h265_reorder_finish_picture (GstH265Reorder * self, GstH265Picture * picture) { const GstH265SPS *sps = self->active_sps; GST_LOG_OBJECT (self, "Finishing picture %p (poc %d), entries in DPB %d", picture, picture->pic_order_cnt, gst_h265_dpb_get_size (self->dpb)); gst_h265_dpb_delete_unused (self->dpb); /* gst_h265_dpb_add() will take care of pic_latency_cnt increment and * reference picture marking for this picture */ gst_h265_dpb_add (self->dpb, picture); /* NOTE: As per C.5.2.2, bumping by sps_max_dec_pic_buffering_minus1 is * applied only for the output and removal of pictures from the DPB before * the decoding of the current picture. So pass zero here */ while (gst_h265_dpb_needs_bump (self->dpb, sps->max_num_reorder_pics[sps->max_sub_layers_minus1], self->SpsMaxLatencyPictures, 0)) { GstH265Picture *to_output = gst_h265_dpb_bump (self->dpb, FALSE); /* Something wrong... */ if (!to_output) { GST_WARNING_OBJECT (self, "Bumping is needed but no picture to output"); break; } gst_h265_reorder_output_picture (self, to_output); } } static void gst_h265_reorder_reset_frame_state (GstH265Reorder * self) { /* Clear picture struct information */ self->cur_pic_struct = GST_H265_SEI_PIC_STRUCT_FRAME; self->cur_source_scan_type = 2; self->cur_duplicate_flag = 0; self->no_output_of_prior_pics_flag = FALSE; self->current_frame = NULL; g_array_set_size (self->nalu, 0); } static GstBuffer * gst_h265_reorder_remove_caption_sei (GstH265Reorder * self, GstBuffer * buffer) { GstH265ParserResult pres = GST_H265_PARSER_OK; GstMapInfo map; GstH265NalUnit nalu; guint i; gboolean have_sei = FALSE; GstBuffer *new_buf; g_array_set_size (self->au_nalus, 0); gst_buffer_map (buffer, &map, GST_MAP_READ); if (self->is_hevc) { guint offset = 0; gsize consumed = 0; guint i; do { pres = gst_h265_parser_identify_and_split_nalu_hevc (self->parser, map.data, offset, map.size, self->nal_length_size, self->split_nalu, &consumed); if (pres != GST_H265_PARSER_OK) break; for (i = 0; i < self->split_nalu->len; i++) { nalu = g_array_index (self->split_nalu, GstH265NalUnit, i); g_array_append_val (self->au_nalus, nalu); } offset += consumed; } while (pres == GST_H265_PARSER_OK); } else { pres = gst_h265_parser_identify_nalu (self->parser, map.data, 0, map.size, &nalu); if (pres == GST_H265_PARSER_NO_NAL_END) pres = GST_H265_PARSER_OK; while (pres == GST_H265_PARSER_OK) { g_array_append_val (self->au_nalus, nalu); pres = gst_h265_parser_identify_nalu (self->parser, map.data, nalu.offset + nalu.size, map.size, &nalu); if (pres == GST_H265_PARSER_NO_NAL_END) pres = GST_H265_PARSER_OK; } } /* Fast scan without parsing */ for (i = 0; i < self->au_nalus->len; i++) { GstH265NalUnit *nl = &g_array_index (self->au_nalus, GstH265NalUnit, i); switch (nl->type) { case GST_H265_NAL_VPS: { GstH265VPS vps; gst_h265_parser_parse_vps (self->parser, nl, &vps); break; } case GST_H265_NAL_SPS: { GstH265SPS sps; gst_h265_parser_parse_sps (self->parser, nl, &sps, TRUE); break; } case GST_H265_NAL_PREFIX_SEI: case GST_H265_NAL_SUFFIX_SEI: have_sei = TRUE; break; default: break; } } if (!have_sei) { GST_LOG_OBJECT (self, "Buffer without SEI, %" GST_PTR_FORMAT, buffer); gst_buffer_unmap (buffer, &map); g_array_set_size (self->au_nalus, 0); return gst_buffer_ref (buffer); } new_buf = gst_buffer_new (); gst_buffer_copy_into (new_buf, buffer, GST_BUFFER_COPY_METADATA, 0, -1); for (i = 0; i < self->au_nalus->len; i++) { GstH265NalUnit *nl = &g_array_index (self->au_nalus, GstH265NalUnit, i); GstMemory *mem = NULL; if (nl->type == GST_H265_NAL_PREFIX_SEI || nl->type == GST_H265_NAL_SUFFIX_SEI) { GArray *msg = NULL; gint j; gst_h265_parser_parse_sei (self->parser, nl, &msg); gboolean have_caption_sei = FALSE; for (j = 0; j < (gint) msg->len; j++) { GstH265SEIMessage *sei = &g_array_index (msg, GstH265SEIMessage, j); GstH265RegisteredUserData *rud; if (sei->payloadType != GST_H265_SEI_REGISTERED_USER_DATA) continue; rud = &sei->payload.registered_user_data; if (!gst_h264_reorder_is_cea708_sei (rud->country_code, rud->data, rud->size)) { continue; } GST_LOG_OBJECT (self, "Found CEA708 caption SEI"); have_caption_sei = TRUE; g_array_remove_index (msg, j); j--; } if (have_caption_sei) { if (msg->len > 0) { /* Creates new SEI memory */ if (self->is_hevc) { mem = gst_h265_create_sei_memory_hevc (nl->layer_id, nl->temporal_id_plus1, self->nal_length_size, msg); } else { mem = gst_h265_create_sei_memory (nl->layer_id, nl->temporal_id_plus1, 4, msg); } if (!mem) GST_ERROR_OBJECT (self, "Couldn't create SEI memory"); else gst_buffer_append_memory (new_buf, mem); } } else { gsize size = nl->size + (nl->offset - nl->sc_offset); gpointer *data = g_memdup2 (nl->data + nl->sc_offset, size); mem = gst_memory_new_wrapped (0, data, size, 0, size, data, g_free); gst_buffer_append_memory (new_buf, mem); } g_array_unref (msg); } else { gsize size = nl->size + (nl->offset - nl->sc_offset); gpointer *data = g_memdup2 (nl->data + nl->sc_offset, size); mem = gst_memory_new_wrapped (0, data, size, 0, size, data, g_free); gst_buffer_append_memory (new_buf, mem); } } gst_buffer_unmap (buffer, &map); g_array_set_size (self->au_nalus, 0); return new_buf; } gboolean gst_h265_reorder_push (GstH265Reorder * reorder, GstVideoCodecFrame * frame, GstClockTime * latency) { GstBuffer *in_buf; GstH265NalUnit nalu; GstH265ParserResult pres = GST_H265_PARSER_OK; GstMapInfo map; gboolean decode_ret = TRUE; guint i; gst_h265_reorder_reset_frame_state (reorder); frame->system_frame_number = reorder->system_num; frame->decode_frame_number = reorder->system_num; GST_LOG_OBJECT (reorder, "Push frame %u, frame queue size: %u, output queue size %u", frame->system_frame_number, reorder->frame_queue->len, reorder->output_queue->len); in_buf = gst_h265_reorder_remove_caption_sei (reorder, frame->input_buffer); if (in_buf) { gst_buffer_unref (frame->input_buffer); frame->input_buffer = in_buf; } else { in_buf = frame->input_buffer; } reorder->system_num++; if (!reorder->need_reorder) { g_ptr_array_add (reorder->output_queue, frame); *latency = 0; return TRUE; } g_ptr_array_add (reorder->frame_queue, frame); reorder->current_frame = frame; gst_buffer_map (in_buf, &map, GST_MAP_READ); if (reorder->is_hevc) { guint offset = 0; gsize consumed = 0; do { pres = gst_h265_parser_identify_and_split_nalu_hevc (reorder->parser, map.data, offset, map.size, reorder->nal_length_size, reorder->split_nalu, &consumed); if (pres != GST_H265_PARSER_OK) break; for (i = 0; i < reorder->split_nalu->len; i++) { GstH265NalUnit *nl = &g_array_index (reorder->split_nalu, GstH265NalUnit, i); pres = gst_h265_reorder_parse_nalu (reorder, nl); if (pres != GST_H265_PARSER_OK) break; } if (pres != GST_H265_PARSER_OK) break; offset += consumed; } while (pres == GST_H265_PARSER_OK); } else { pres = gst_h265_parser_identify_nalu (reorder->parser, map.data, 0, map.size, &nalu); if (pres == GST_H265_PARSER_NO_NAL_END) pres = GST_H265_PARSER_OK; while (pres == GST_H265_PARSER_OK) { pres = gst_h265_reorder_parse_nalu (reorder, &nalu); if (pres != GST_H265_PARSER_OK) break; pres = gst_h265_parser_identify_nalu (reorder->parser, map.data, nalu.offset + nalu.size, map.size, &nalu); if (pres == GST_H265_PARSER_NO_NAL_END) pres = GST_H265_PARSER_OK; } } for (i = 0; i < reorder->nalu->len && decode_ret; i++) { GstH265ReorderNalUnit *decoder_nalu = &g_array_index (reorder->nalu, GstH265ReorderNalUnit, i); decode_ret = gst_h265_reorder_decode_nalu (reorder, decoder_nalu); } gst_buffer_unmap (in_buf, &map); gst_h265_reorder_reset_frame_state (reorder); if (!decode_ret) { GST_ERROR_OBJECT (reorder, "Couldn't decode frame"); gst_clear_h265_picture (&reorder->current_picture); reorder->current_frame = NULL; g_ptr_array_remove (reorder->frame_queue, frame); reorder->system_num--; return FALSE; } if (!reorder->current_picture) { GST_DEBUG_OBJECT (reorder, "AU buffer without slice data, current frame %u", frame->system_frame_number); g_ptr_array_remove (reorder->frame_queue, frame); reorder->current_frame = NULL; reorder->system_num--; return FALSE; } gst_h265_reorder_finish_picture (reorder, reorder->current_picture); reorder->current_picture = NULL; reorder->current_frame = NULL; *latency = reorder->latency; return TRUE; } GstVideoCodecFrame * gst_h265_reorder_pop (GstH265Reorder * reorder) { if (!reorder->output_queue->len) { GST_LOG_OBJECT (reorder, "Empty output queue, frames queue size %u", reorder->frame_queue->len); return NULL; } return g_ptr_array_steal_index (reorder->output_queue, 0); } guint gst_h265_reorder_get_num_buffered (GstH265Reorder * reorder) { return reorder->frame_queue->len + reorder->output_queue->len; } GstBuffer * gst_h265_reorder_insert_sei (GstH265Reorder * reorder, GstBuffer * au, GArray * sei) { GstMemory *mem; GstBuffer *new_buf; if (reorder->is_hevc) mem = gst_h265_create_sei_memory_hevc (0, 1, reorder->nal_length_size, sei); else mem = gst_h265_create_sei_memory (0, 1, 4, sei); if (!mem) { GST_ERROR_OBJECT (reorder, "Couldn't create SEI memory"); return NULL; } if (reorder->is_hevc) { new_buf = gst_h265_parser_insert_sei_hevc (reorder->parser, reorder->nal_length_size, au, mem); } else { new_buf = gst_h265_parser_insert_sei (reorder->parser, au, mem); } gst_memory_unref (mem); return new_buf; }