static GF_Err gf_import_hevc()

in src/media_tools/media_import.c [5549:6450]


static GF_Err gf_import_hevc(GF_MediaImporter *import)
{
#ifdef GPAC_DISABLE_HEVC
	return GF_NOT_SUPPORTED;
#else
	Bool detect_fps;
	u64 nal_start, nal_end, total_size;
	u32 i, nal_size, track, trackID, di, cur_samp, nb_i, nb_idr, nb_p, nb_b, nb_sp, nb_si, nb_sei, max_w, max_h, max_total_delay;
	s32 idx, sei_recovery_frame_count;
	u64 duration;
	GF_Err e;
	FILE *mdia;
	HEVCState hevc;
	GF_AVCConfigSlot *slc;
	GF_HEVCConfig *hevc_cfg, *shvc_cfg, *dst_cfg;
	GF_HEVCParamArray *spss, *ppss, *vpss;
	GF_BitStream *bs;
	GF_BitStream *sample_data;
	Bool flush_sample, flush_next_sample, is_empty_sample, sample_is_rap, sample_has_islice, is_islice, first_nal, slice_is_ref, has_cts_offset, is_paff, set_subsamples, slice_force_ref;
	u32 ref_frame, timescale, copy_size, size_length, dts_inc;
	s32 last_poc, max_last_poc, max_last_b_poc, poc_diff, prev_last_poc, min_poc, poc_shift;
	Bool first_hevc;
	u32 use_opengop_gdr = 0;
	u8 layer_ids[64];

	Double FPS;
	char *buffer;
	u32 max_size = 4096;

	if (import->flags & GF_IMPORT_PROBE_ONLY) {
		import->nb_tracks = 1;
		import->tk_info[0].track_num = 1;
		import->tk_info[0].type = GF_ISOM_MEDIA_VISUAL;
		import->tk_info[0].flags = GF_IMPORT_OVERRIDE_FPS | GF_IMPORT_FORCE_PACKED;
		return GF_OK;
	}

	set_subsamples = (import->flags & GF_IMPORT_SET_SUBSAMPLES) ? GF_TRUE : GF_FALSE;

	mdia = gf_fopen(import->in_name, "rb");
	if (!mdia) return gf_import_message(import, GF_URL_ERROR, "Cannot find file %s", import->in_name);

	detect_fps = GF_TRUE;
	FPS = (Double) import->video_fps;
	if (!FPS) {
		FPS = GF_IMPORT_DEFAULT_FPS;
	} else {
		if (import->video_fps == GF_IMPORT_AUTO_FPS) {
			import->video_fps = GF_IMPORT_DEFAULT_FPS;	/*fps=auto is handled as auto-detection is h264*/
		} else {
			/*fps is forced by the caller*/
			detect_fps = GF_FALSE;
		}
	}
	get_video_timing(FPS, &timescale, &dts_inc);

	poc_diff = 0;

restart_import:

	memset(&hevc, 0, sizeof(HEVCState));
	hevc.sps_active_idx = -1;
	dst_cfg = hevc_cfg = gf_odf_hevc_cfg_new();
	shvc_cfg = gf_odf_hevc_cfg_new();
	shvc_cfg->complete_representation = GF_TRUE;
	shvc_cfg->non_hevc_base_layer = GF_FALSE;
	buffer = (char*)gf_malloc(sizeof(char) * max_size);
	sample_data = NULL;
	first_hevc = GF_TRUE;
	sei_recovery_frame_count = -1;
	spss = ppss = vpss = NULL;

	bs = gf_bs_from_file(mdia, GF_BITSTREAM_READ);
	if (!gf_media_nalu_is_start_code(bs)) {
		e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Cannot find HEVC start code");
		goto exit;
	}

	/*NALU size packing disabled*/
	if (!(import->flags & GF_IMPORT_FORCE_PACKED)) size_length = 32;
	/*if import in edit mode, use smallest NAL size and adjust on the fly*/
	else if (gf_isom_get_mode(import->dest)!=GF_ISOM_OPEN_WRITE) size_length = 8;
	else size_length = 32;

	trackID = 0;
	e = GF_OK;
	if (import->esd) trackID = import->esd->ESID;

	track = gf_isom_new_track(import->dest, trackID, GF_ISOM_MEDIA_VISUAL, timescale);
	if (!track) {
		e = gf_isom_last_error(import->dest);
		goto exit;
	}
	gf_isom_set_track_enabled(import->dest, track, 1);
	if (import->esd && !import->esd->ESID) import->esd->ESID = gf_isom_get_track_id(import->dest, track);
	import->final_trackID = gf_isom_get_track_id(import->dest, track);
	if (import->esd && import->esd->dependsOnESID) {
		gf_isom_set_track_reference(import->dest, track, GF_ISOM_REF_DECODE, import->esd->dependsOnESID);
	}

	e = gf_isom_hevc_config_new(import->dest, track, hevc_cfg, NULL, NULL, &di);
	if (e) goto exit;

	gf_isom_set_nalu_extract_mode(import->dest, track, GF_ISOM_NALU_EXTRACT_INSPECT);
	memset(layer_ids, 0, sizeof(u8)*64);

	sample_data = NULL;
	sample_is_rap = GF_FALSE;
	sample_has_islice = GF_FALSE;
	cur_samp = 0;
	is_paff = GF_FALSE;
	total_size = gf_bs_get_size(bs);
	nal_start = gf_bs_get_position(bs);
	duration = (u64) ( ((Double)import->duration) * timescale / 1000.0);

	nb_i = nb_idr = nb_p = nb_b = nb_sp = nb_si = nb_sei = 0;
	max_w = max_h = 0;
	first_nal = GF_TRUE;
	ref_frame = 0;
	last_poc = max_last_poc = max_last_b_poc = prev_last_poc = 0;
	max_total_delay = 0;

	gf_isom_set_cts_packing(import->dest, track, GF_TRUE);
	has_cts_offset = GF_FALSE;
	min_poc = 0;
	poc_shift = 0;
	flush_next_sample = GF_FALSE;
	is_empty_sample = GF_TRUE;

	while (gf_bs_available(bs)) {
		s32 res;
		Bool force_shvc = GF_FALSE;
		GF_HEVCConfig *prev_cfg;
		u8 nal_unit_type, temporal_id, layer_id;
		Bool skip_nal, add_sps, is_slice, has_vcl_nal;
		u32 nal_and_trailing_size;

		has_vcl_nal = GF_FALSE;
		nal_and_trailing_size = nal_size = gf_media_nalu_next_start_code_bs(bs);
		if (!(import->flags & GF_IMPORT_KEEP_TRAILING)) {
			nal_size = gf_media_nalu_payload_end_bs(bs);
		}


		if (nal_size>max_size) {
			buffer = (char*)gf_realloc(buffer, sizeof(char)*nal_size);
			max_size = nal_size;
		}

		/*read the file, and work on a memory buffer*/
		gf_bs_read_data(bs, buffer, nal_size);

		gf_bs_seek(bs, nal_start);

		res = gf_media_hevc_parse_nalu(bs, &hevc, &nal_unit_type, &temporal_id, &layer_id);

		if (layer_id && (import->flags & GF_IMPORT_SVC_NONE)) {
			goto next_nal;
		}

		is_islice = GF_FALSE;

		prev_cfg = dst_cfg;
		//todo check layer type, for now only scalable (not 3D etc) ...
		if (import->flags & GF_IMPORT_SVC_EXPLICIT) {
			dst_cfg = shvc_cfg;
			force_shvc = GF_TRUE;
		} else
			dst_cfg = layer_id ? shvc_cfg : hevc_cfg;

		if (prev_cfg != dst_cfg) {
			vpss = get_hevc_param_array(dst_cfg, GF_HEVC_NALU_VID_PARAM);
			spss = get_hevc_param_array(dst_cfg, GF_HEVC_NALU_SEQ_PARAM);
			ppss = get_hevc_param_array(dst_cfg, GF_HEVC_NALU_PIC_PARAM);
		}

		skip_nal = GF_FALSE;
		copy_size = flush_sample = GF_FALSE;
		is_slice = GF_FALSE;

		switch (res) {
		case 1:
			flush_sample = GF_TRUE;
			break;
		case -1:
			gf_import_message(import, GF_OK, "Waring: Error parsing NAL unit");
			skip_nal = GF_TRUE;
			break;
		case -2:
			skip_nal = GF_TRUE;
			break;
		default:
			break;
		}

		if (! layer_id && flush_next_sample && (nal_unit_type!=GF_HEVC_NALU_SEI_SUFFIX)) {
			flush_next_sample = GF_FALSE;
			flush_sample = GF_TRUE;
		}

		switch (nal_unit_type) {
		case GF_HEVC_NALU_VID_PARAM:
			idx = gf_media_hevc_read_vps(buffer, nal_size , &hevc);
			if (idx<0) {
				e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing Picture Param");
				goto exit;
			}
			/*if we get twice the same VPS put in the the bitstream and set array_completeness to 0 ...*/
			if (hevc.vps[idx].state == 2) {
				if (hevc.vps[idx].crc != gf_crc_32(buffer, nal_size)) {
					copy_size = nal_size;
					has_vcl_nal = GF_TRUE;
					assert(vpss);
					vpss->array_completeness = 0;
				}
			}

			if (hevc.vps[idx].state==1) {
				hevc.vps[idx].state = 2;
				hevc.vps[idx].crc = gf_crc_32(buffer, nal_size);

				dst_cfg->avgFrameRate = hevc.vps[idx].rates[0].avg_pic_rate;
				dst_cfg->constantFrameRate = hevc.vps[idx].rates[0].constand_pic_rate_idc;
				dst_cfg->numTemporalLayers = hevc.vps[idx].max_sub_layers;
				dst_cfg->temporalIdNested = hevc.vps[idx].temporal_id_nesting;
				//TODO set scalability mask

				if (!vpss) {
					GF_SAFEALLOC(vpss, GF_HEVCParamArray);
					vpss->nalus = gf_list_new();
					gf_list_add(dst_cfg->param_array, vpss);
					vpss->array_completeness = 1;
					vpss->type = GF_HEVC_NALU_VID_PARAM;
				}

				if (import->flags & GF_IMPORT_FORCE_XPS_INBAND) {
					vpss->array_completeness = 0;
					copy_size = nal_size;
				} else {
					slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
					slc->size = nal_size;
					slc->id = idx;
					slc->data = (char*)gf_malloc(sizeof(char)*slc->size);
					memcpy(slc->data, buffer, sizeof(char)*slc->size);

					gf_list_add(vpss->nalus, slc);
				}
			}

			if (import->flags & GF_IMPORT_FORCE_XPS_INBAND) {
				copy_size = nal_size;
			}

			break;
		case GF_HEVC_NALU_SEQ_PARAM:
			idx = gf_media_hevc_read_sps(buffer, nal_size, &hevc);
			if (idx<0) {
				e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing SeqInfo");
				break;
			}
			add_sps = GF_FALSE;
			if ((hevc.sps[idx].state & AVC_SPS_PARSED) && !(hevc.sps[idx].state & AVC_SPS_DECLARED)) {
				hevc.sps[idx].state |= AVC_SPS_DECLARED;
				add_sps = GF_TRUE;
				hevc.sps[idx].crc = gf_crc_32(buffer, nal_size);
			}

			/*if we get twice the same SPS put it in the bitstream and set array_completeness to 0 ...*/
			else if (hevc.sps[idx].state & AVC_SPS_DECLARED) {
				if (hevc.sps[idx].crc != gf_crc_32(buffer, nal_size)) {
					copy_size = nal_size;
					has_vcl_nal = GF_TRUE;
					assert(spss);
					spss->array_completeness = 0;
				}
			}

			if (add_sps) {
				dst_cfg->configurationVersion = 1;
				dst_cfg->profile_space = hevc.sps[idx].ptl.profile_space;
				dst_cfg->tier_flag = hevc.sps[idx].ptl.tier_flag;
				dst_cfg->profile_idc = hevc.sps[idx].ptl.profile_idc;
				dst_cfg->general_profile_compatibility_flags = hevc.sps[idx].ptl.profile_compatibility_flag;
				dst_cfg->progressive_source_flag = hevc.sps[idx].ptl.general_progressive_source_flag;
				dst_cfg->interlaced_source_flag = hevc.sps[idx].ptl.general_interlaced_source_flag;
				dst_cfg->non_packed_constraint_flag = hevc.sps[idx].ptl.general_non_packed_constraint_flag;
				dst_cfg->frame_only_constraint_flag = hevc.sps[idx].ptl.general_frame_only_constraint_flag;

				dst_cfg->constraint_indicator_flags = hevc.sps[idx].ptl.general_reserved_44bits;
				dst_cfg->level_idc = hevc.sps[idx].ptl.level_idc;

				dst_cfg->chromaFormat = hevc.sps[idx].chroma_format_idc;
				dst_cfg->luma_bit_depth = hevc.sps[idx].bit_depth_luma;
				dst_cfg->chroma_bit_depth = hevc.sps[idx].bit_depth_chroma;

				if (!spss) {
					GF_SAFEALLOC(spss, GF_HEVCParamArray);
					spss->nalus = gf_list_new();
					gf_list_add(dst_cfg->param_array, spss);
					spss->array_completeness = 1;
					spss->type = GF_HEVC_NALU_SEQ_PARAM;
				}

				/*disable frame rate scan, most bitstreams have wrong values there*/
				if (detect_fps && hevc.sps[idx].has_timing_info
				        /*if detected FPS is greater than 1000, assume wrong timing info*/
				        && (hevc.sps[idx].time_scale <= 1000*hevc.sps[idx].num_units_in_tick)
				   ) {
					timescale = hevc.sps[idx].time_scale;
					dts_inc =   hevc.sps[idx].num_units_in_tick;
					FPS = (Double)timescale / dts_inc;
					detect_fps = GF_FALSE;
					gf_isom_remove_track(import->dest, track);
					if (sample_data) gf_bs_del(sample_data);
					gf_odf_hevc_cfg_del(hevc_cfg);
					hevc_cfg = NULL;
					gf_odf_hevc_cfg_del(shvc_cfg);
					shvc_cfg = NULL;
					gf_free(buffer);
					buffer = NULL;
					gf_bs_del(bs);
					bs = NULL;
					gf_fseek(mdia, 0, SEEK_SET);
					goto restart_import;
				}

				if (import->flags & GF_IMPORT_FORCE_XPS_INBAND) {
					spss->array_completeness = 0;
					copy_size = nal_size;
				} else {
					slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
					slc->size = nal_size;
					slc->id = idx;
					slc->data = (char*)gf_malloc(sizeof(char)*slc->size);
					memcpy(slc->data, buffer, sizeof(char)*slc->size);
					gf_list_add(spss->nalus, slc);
				}

				if (first_hevc) {
					first_hevc = GF_FALSE;
					gf_import_message(import, GF_OK, "HEVC import - frame size %d x %d at %02.3f FPS", hevc.sps[idx].width, hevc.sps[idx].height, FPS);
				} else {
					gf_import_message(import, GF_OK, "SHVC detected - %d x %d at %02.3f FPS", hevc.sps[idx].width, hevc.sps[idx].height, FPS);
				}

				//width and height only for base layer if HEVC
				if ((force_shvc || (dst_cfg==hevc_cfg)) && (max_w <= hevc.sps[idx].width) && (max_h <= hevc.sps[idx].height)) {
					max_w = hevc.sps[idx].width;
					max_h = hevc.sps[idx].height;
				}
			}
			if (import->flags & GF_IMPORT_FORCE_XPS_INBAND) {
				copy_size = nal_size;
			} 
			break;

		case GF_HEVC_NALU_PIC_PARAM:
			idx = gf_media_hevc_read_pps(buffer, nal_size, &hevc);
			if (idx<0) {
				e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing Picture Param");
				goto exit;
			}
			/*if we get twice the same PPS put it in the bitstream and set array_completeness to 0 ...*/
			if (hevc.pps[idx].state == 2) {
				if (hevc.pps[idx].crc != gf_crc_32(buffer, nal_size)) {
					copy_size = nal_size;
					has_vcl_nal = GF_TRUE;
					assert(ppss);
					ppss->array_completeness = 0;
				}
			}

			if (hevc.pps[idx].state==1) {
				hevc.pps[idx].state = 2;
				hevc.pps[idx].crc = gf_crc_32(buffer, nal_size);

				if (!ppss) {
					GF_SAFEALLOC(ppss, GF_HEVCParamArray);
					ppss->nalus = gf_list_new();
					gf_list_add(dst_cfg->param_array, ppss);
					ppss->array_completeness = 1;
					ppss->type = GF_HEVC_NALU_PIC_PARAM;
				}

				if (import->flags & GF_IMPORT_FORCE_XPS_INBAND) {
					ppss->array_completeness = 0;
					copy_size = nal_size;
				} else {
					slc = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
					slc->size = nal_size;
					slc->id = idx;
					slc->data = (char*)gf_malloc(sizeof(char)*slc->size);
					memcpy(slc->data, buffer, sizeof(char)*slc->size);

					gf_list_add(ppss->nalus, slc);
				}
			}
			if (import->flags & GF_IMPORT_FORCE_XPS_INBAND) {
				copy_size = nal_size;
			} 

			break;
		case GF_HEVC_NALU_SEI_SUFFIX:
			if (!layer_id) flush_next_sample = GF_TRUE;
			if (hevc.sps_active_idx != -1) {
				copy_size = nal_size;
				if (copy_size)
					nb_sei++;
			}
            break;
		case GF_HEVC_NALU_SEI_PREFIX:
			if (hevc.sps_active_idx != -1) {
				copy_size = nal_size;
				if (copy_size) {
					nb_sei++;
					flush_sample = GF_TRUE;
				}
			}
			break;

		/*slice_segment_layer_rbsp*/
		case GF_HEVC_NALU_SLICE_STSA_N:
		case GF_HEVC_NALU_SLICE_STSA_R:
		case GF_HEVC_NALU_SLICE_RADL_R:
		case GF_HEVC_NALU_SLICE_RASL_R:
		case GF_HEVC_NALU_SLICE_RADL_N:
		case GF_HEVC_NALU_SLICE_RASL_N:
		case GF_HEVC_NALU_SLICE_TRAIL_N:
		case GF_HEVC_NALU_SLICE_TRAIL_R:
		case GF_HEVC_NALU_SLICE_TSA_N:
		case GF_HEVC_NALU_SLICE_TSA_R:
		case GF_HEVC_NALU_SLICE_BLA_W_LP:
		case GF_HEVC_NALU_SLICE_BLA_W_DLP:
		case GF_HEVC_NALU_SLICE_BLA_N_LP:
		case GF_HEVC_NALU_SLICE_IDR_W_DLP:
		case GF_HEVC_NALU_SLICE_IDR_N_LP:
		case GF_HEVC_NALU_SLICE_CRA:
			is_slice = GF_TRUE;
			/*			if ((hevc.s_info.slice_segment_address<=100) || (hevc.s_info.slice_segment_address>=200))
							skip_nal = 1;
						if (!hevc.s_info.slice_segment_address)
							skip_nal = 0;
			*/
			if (! skip_nal) {
				copy_size = nal_size;
				has_vcl_nal = GF_TRUE;
				switch (hevc.s_info.slice_type) {
				case GF_HEVC_TYPE_P:
					nb_p++;
					break;
				case GF_HEVC_TYPE_I:
					nb_i++;
					is_islice = GF_TRUE;
					break;
				case GF_HEVC_TYPE_B:
					nb_b++;
					break;
				}
			}
			break;

		/*remove*/
		case GF_HEVC_NALU_ACCESS_UNIT:
		case GF_HEVC_NALU_FILLER_DATA:
		case GF_HEVC_NALU_END_OF_SEQ:
		case GF_HEVC_NALU_END_OF_STREAM:
			break;

		default:
			gf_import_message(import, GF_OK, "WARNING: NAL Unit type %d not handled - adding", nal_unit_type);
			copy_size = nal_size;
			break;
		}

		if (!nal_size) break;

		if (flush_sample && is_empty_sample)
			flush_sample = GF_FALSE;

		if (flush_sample && sample_data) {
			GF_ISOSample *samp = gf_isom_sample_new();
			samp->DTS = (u64)dts_inc*cur_samp;
			samp->IsRAP = sample_is_rap ? RAP : RAP_NO;
			if (!sample_is_rap) {
				if (sample_has_islice && (import->flags & GF_IMPORT_FORCE_SYNC) && (sei_recovery_frame_count==0)) {
					samp->IsRAP = RAP;
					if (!use_opengop_gdr) {
						use_opengop_gdr = 1;
						GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[HEVC Import] Forcing non-IDR samples with I slices to be marked as sync points - resulting file will not be ISO conformant\n"));
					}
				}
			}
			gf_bs_get_content(sample_data, &samp->data, &samp->dataLength);
			gf_bs_del(sample_data);
			sample_data = NULL;

			/*CTS recomuting is much trickier than with MPEG-4 ASP due to b-slice used as references - we therefore
			store the POC as the CTS offset and update the whole table at the end*/
			samp->CTS_Offset = last_poc - poc_shift;
			assert(last_poc >= poc_shift);
			e = gf_isom_add_sample(import->dest, track, di, samp);
			if (e) goto exit;

			cur_samp++;

			/*write sampleGroups info*/
			if (!samp->IsRAP && ((sei_recovery_frame_count>=0) || sample_has_islice) ) {
				/*generic GDR*/
				if (sei_recovery_frame_count >= 0) {
					if (!use_opengop_gdr) use_opengop_gdr = 1;
					e = gf_isom_set_sample_roll_group(import->dest, track, cur_samp, (s16) sei_recovery_frame_count);
				}
				/*open-GOP*/
				else if (sample_has_islice) {
					if (!use_opengop_gdr) use_opengop_gdr = 2;
					e = gf_isom_set_sample_rap_group(import->dest, track, cur_samp, 0);
				}
				if (e) goto exit;
			}

			gf_isom_sample_del(&samp);
			gf_set_progress("Importing HEVC", (u32) (nal_start/1024), (u32) (total_size/1024) );
			first_nal = GF_TRUE;

			if (min_poc > last_poc)
				min_poc = last_poc;

			sample_has_islice = GF_FALSE;
			sei_recovery_frame_count = -1;
			is_empty_sample = GF_TRUE;
		}

		if (copy_size) {
			if (is_islice)
				sample_has_islice = GF_TRUE;

			if ((size_length<32) && ( (u32) (1<<size_length)-1 < copy_size)) {
				u32 diff_size = 8;
				while ((size_length<32) && ( (u32) (1<<(size_length+diff_size))-1 < copy_size)) diff_size+=8;
				/*only 8bits, 16bits and 32 bits*/
				if (size_length+diff_size == 24) diff_size+=8;

				gf_import_message(import, GF_OK, "Adjusting HEVC SizeLength to %d bits", size_length+diff_size);
				gf_media_avc_rewrite_samples(import->dest, track, size_length, size_length+diff_size);

				/*rewrite current sample*/
				if (sample_data) {
					char *sd;
					u32 sd_l;
					GF_BitStream *prev_sd;
					gf_bs_get_content(sample_data, &sd, &sd_l);
					gf_bs_del(sample_data);
					sample_data = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
					prev_sd = gf_bs_new(sd, sd_l, GF_BITSTREAM_READ);
					while (gf_bs_available(prev_sd)) {
						char *buf;
						u32 s = gf_bs_read_int(prev_sd, size_length);
						gf_bs_write_int(sample_data, s, size_length+diff_size);
						buf = (char*)gf_malloc(sizeof(char)*s);
						gf_bs_read_data(prev_sd, buf, s);
						gf_bs_write_data(sample_data, buf, s);
						gf_free(buf);
					}
					gf_bs_del(prev_sd);
					gf_free(sd);
				}
				size_length+=diff_size;

			}
			if (!sample_data) sample_data = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
			gf_bs_write_int(sample_data, copy_size, size_length);
			gf_bs_write_data(sample_data, buffer, copy_size);

			if (set_subsamples) {
				/* use the res and priority value of last prefix NALU */
				gf_isom_add_subsample(import->dest, track, cur_samp+1, copy_size+size_length/8, 0, 0, GF_FALSE);
			}

			if (has_vcl_nal) {
				is_empty_sample = GF_FALSE;
			}
			layer_ids[layer_id] = 1;


			//fixme with latest SHVC syntax
			if (!layer_id && is_slice) {
				slice_is_ref = gf_media_hevc_slice_is_IDR(&hevc);
				if (slice_is_ref)
					nb_idr++;
				slice_force_ref = GF_FALSE;

				/*we only indicate TRUE IDRs for sync samples (cf AVC file format spec).
				SEI recovery should be used to build sampleToGroup & RollRecovery tables*/
				if (first_nal) {
					first_nal = GF_FALSE;
					if (hevc.sei.recovery_point.valid || (import->flags & GF_IMPORT_FORCE_SYNC)) {
						Bool bIntraSlice = gf_media_hevc_slice_is_intra(&hevc);
						assert(hevc.s_info.nal_unit_type!=GF_AVC_NALU_IDR_SLICE || bIntraSlice);

						sei_recovery_frame_count = hevc.sei.recovery_point.frame_cnt;

						/*we allow to mark I-frames as sync on open-GOPs (with sei_recovery_frame_count=0) when forcing sync even when the SEI RP is not available*/
						if (!hevc.sei.recovery_point.valid && bIntraSlice) {
							sei_recovery_frame_count = 0;
							if (use_opengop_gdr == 1) {
								use_opengop_gdr = 2; /*avoid message flooding*/
								GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[HEVC Import] No valid SEI Recovery Point found although needed - forcing\n"));
							}
						}
						hevc.sei.recovery_point.valid = 0;
						if (bIntraSlice && (import->flags & GF_IMPORT_FORCE_SYNC) && (sei_recovery_frame_count==0))
							slice_force_ref = GF_TRUE;
					}
					sample_is_rap = gf_media_hevc_slice_is_IDR(&hevc);
				}

				if (hevc.s_info.poc<poc_shift) {
					u32 j;
					if (ref_frame) {
						for (j=ref_frame; j<=cur_samp; j++) {
							GF_ISOSample *samp = gf_isom_get_sample_info(import->dest, track, j, NULL, NULL);
							if (!samp) break;
							samp->CTS_Offset += poc_shift;
							samp->CTS_Offset -= hevc.s_info.poc;
							gf_isom_modify_cts_offset(import->dest, track, j, samp->CTS_Offset);
							gf_isom_sample_del(&samp);
						}
					}
					poc_shift = hevc.s_info.poc;
				}

				/*if #pics, compute smallest POC increase*/
				if (hevc.s_info.poc != last_poc) {
					if (!poc_diff || (poc_diff > abs(hevc.s_info.poc-last_poc))) {
						poc_diff = abs(hevc.s_info.poc - last_poc);/*ideally we would need to start the parsing again as poc_diff helps computing max_total_delay*/
					}
					last_poc = hevc.s_info.poc;
				}

				/*ref slice, reset poc*/
				if (slice_is_ref) {
					ref_frame = cur_samp+1;
					max_last_poc = last_poc = max_last_b_poc = 0;
					poc_shift = 0;
				}
				/*forced ref slice*/
				else if (slice_force_ref) {
					ref_frame = cur_samp+1;
					/*adjust POC shift as sample will now be marked as sync, so wo must store poc as if IDR (eg POC=0) for our CTS offset computing to be correct*/
					poc_shift = hevc.s_info.poc;
				}
				/*strictly less - this is a new P slice*/
				else if (max_last_poc<last_poc) {
					max_last_b_poc = 0;
					prev_last_poc = max_last_poc;
					max_last_poc = last_poc;
				}
				/*stricly greater*/
				else if (max_last_poc>last_poc) {
					/*need to store TS offsets*/
					has_cts_offset = GF_TRUE;
					switch (hevc.s_info.slice_type) {
					case GF_AVC_TYPE_B:
					case GF_AVC_TYPE2_B:
						if (!max_last_b_poc) {
							max_last_b_poc = last_poc;
						}
						/*if same poc than last max, this is a B-slice*/
						else if (last_poc>max_last_b_poc) {
							max_last_b_poc = last_poc;
						}
						/*otherwise we had a B-slice reference: do nothing*/

						break;
					}
				}

				/*compute max delay (applicable when B slice are present)*/
				if (ref_frame && poc_diff && (s32)(cur_samp-(ref_frame-1)-last_poc/poc_diff)>(s32)max_total_delay) {
					max_total_delay = cur_samp - (ref_frame-1) - last_poc/poc_diff;
				}
			}
		}

next_nal:
		gf_bs_align(bs);
		nal_end = gf_bs_get_position(bs);
		assert(nal_start <= nal_end);
		assert(nal_end <= nal_start + nal_and_trailing_size);
		if (nal_end != nal_start + nal_and_trailing_size)
			gf_bs_seek(bs, nal_start + nal_and_trailing_size);

		if (!gf_bs_available(bs)) break;
		if (duration && (dts_inc*cur_samp > duration)) break;
		if (import->flags & GF_IMPORT_DO_ABORT) break;

		/*consume next start code*/
		nal_start = gf_media_nalu_next_start_code_bs(bs);
		if (nal_start) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[hevc] invalid nal_size (%u)? Skipping "LLU" bytes to reach next start code\n", nal_size, nal_start));
			gf_bs_skip_bytes(bs, nal_start);
		}
		nal_start = gf_media_nalu_is_start_code(bs);
		if (!nal_start) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[hevc] error: no start code found ("LLU" bytes read out of "LLU") - leaving\n", gf_bs_get_position(bs), gf_bs_get_size(bs)));
			break;
		}
		nal_start = gf_bs_get_position(bs);
	}

	/*final flush*/
	if (sample_data) {
		GF_ISOSample *samp = gf_isom_sample_new();
		samp->DTS = (u64)dts_inc*cur_samp;
		samp->IsRAP = sample_is_rap ? RAP : RAP_NO;
		if (!sample_is_rap && sample_has_islice && (import->flags & GF_IMPORT_FORCE_SYNC)) {
			samp->IsRAP = RAP;
		}
		/*we store the frame order (based on the POC) as the CTS offset and update the whole table at the end*/
		samp->CTS_Offset = last_poc - poc_shift;
		gf_bs_get_content(sample_data, &samp->data, &samp->dataLength);
		gf_bs_del(sample_data);
		sample_data = NULL;
		e = gf_isom_add_sample(import->dest, track, di, samp);
		if (e) goto exit;
		gf_isom_sample_del(&samp);
		gf_set_progress("Importing HEVC", (u32) cur_samp, cur_samp+1);
		cur_samp++;
	}


	/*recompute all CTS offsets*/
	if (has_cts_offset) {
		u32 last_cts_samp;
		u64 last_dts, max_cts, min_cts;
		if (!poc_diff) poc_diff = 1;
		/*no b-frame references, no need to cope with negative poc*/
		if (!max_total_delay) {
			min_poc=0;
			max_total_delay = 1;
		}
		cur_samp = gf_isom_get_sample_count(import->dest, track);
		min_poc *= -1;
		last_dts = 0;
		max_cts = 0;
		min_cts = (u64) -1;
		last_cts_samp = 0;

		for (i=0; i<cur_samp; i++) {
			u64 cts;
			/*not using descIdx and data_offset will only fecth DTS, CTS and RAP which is all we need*/
			GF_ISOSample *samp = gf_isom_get_sample_info(import->dest, track, i+1, NULL, NULL);
			/*poc re-init (RAP and POC to 0, otherwise that's SEI recovery), update base DTS*/
			if (samp->IsRAP /*&& !samp->CTS_Offset*/)
				last_dts = samp->DTS * (1+is_paff);

			/*CTS offset is frame POC (refers to last IDR)*/
			cts = (min_poc + (s32) samp->CTS_Offset) * dts_inc/poc_diff + (u32) last_dts;

			/*if PAFF, 2 pictures (eg poc) <=> 1 aggregated frame (eg sample), divide by 2*/
			if (is_paff) {
				cts /= 2;
				/*in some cases the poc is not on the top field - if that is the case, round up*/
				if (cts%dts_inc) {
					cts = ((cts/dts_inc)+1)*dts_inc;
				}
			}

			/*B-frames offset*/
			cts += (u32) (max_total_delay*dts_inc);

			samp->CTS_Offset = (u32) (cts - samp->DTS);

			if (max_cts < samp->DTS + samp->CTS_Offset) {
				max_cts = samp->DTS + samp->CTS_Offset;
				last_cts_samp = i;
			}
			if (min_cts > samp->DTS + samp->CTS_Offset) {
				min_cts = samp->DTS + samp->CTS_Offset;
			}

			/*this should never happen, however some streams seem to do weird POC increases (cf sorenson streams, last 2 frames),
			this should hopefully take care of some bugs and ensure proper CTS...*/
			if ((s32)samp->CTS_Offset<0) {
				u32 j, k;
				samp->CTS_Offset = 0;
				gf_isom_modify_cts_offset(import->dest, track, i+1, samp->CTS_Offset);
				for (j=last_cts_samp; j<i; j++) {
					GF_ISOSample *asamp = gf_isom_get_sample_info(import->dest, track, j+1, NULL, NULL);
					for (k=j+1; k<=i; k++) {
						GF_ISOSample *bsamp = gf_isom_get_sample_info(import->dest, track, k+1, NULL, NULL);
						if (asamp->CTS_Offset+asamp->DTS==bsamp->CTS_Offset+bsamp->DTS) {
							max_cts += dts_inc;
							bsamp->CTS_Offset = (u32) (max_cts - bsamp->DTS);
							gf_isom_modify_cts_offset(import->dest, track, k+1, bsamp->CTS_Offset);
						}
						gf_isom_sample_del(&bsamp);
					}
					gf_isom_sample_del(&asamp);
				}
				max_cts = samp->DTS + samp->CTS_Offset;
			} else {
				gf_isom_modify_cts_offset(import->dest, track, i+1, samp->CTS_Offset);
			}
			gf_isom_sample_del(&samp);
		}
		/*and repack table*/
		gf_isom_set_cts_packing(import->dest, track, GF_FALSE);

		if (!(import->flags & GF_IMPORT_NO_EDIT_LIST) && min_cts) {
			last_dts = max_cts - min_cts + gf_isom_get_sample_duration(import->dest, track, gf_isom_get_sample_count(import->dest, track) );
			last_dts *= gf_isom_get_timescale(import->dest);
			last_dts /= gf_isom_get_media_timescale(import->dest, track);
			gf_isom_set_edit_segment(import->dest, track, 0, last_dts, min_cts, GF_ISOM_EDIT_NORMAL);
		}
	} else {
		gf_isom_remove_cts_info(import->dest, track);
	}

	gf_set_progress("Importing HEVC", (u32) cur_samp, cur_samp);

	gf_isom_set_visual_info(import->dest, track, di, max_w, max_h);
	hevc_cfg->nal_unit_size = shvc_cfg->nal_unit_size = size_length/8;

	shvc_cfg->num_layers = 0;
	for (i=1; i<64; i++) {
		if (layer_ids[i])
			shvc_cfg->num_layers ++;
	}


	if (import->flags & GF_IMPORT_FORCE_XPS_INBAND) {
		hevc_set_parall_type(hevc_cfg);
		gf_isom_hevc_config_update(import->dest, track, 1, hevc_cfg);
		gf_isom_hevc_set_inband_config(import->dest, track, 1);
	} else if (gf_list_count(hevc_cfg->param_array) || !gf_list_count(shvc_cfg->param_array) ) {
		hevc_set_parall_type(hevc_cfg);
		gf_isom_hevc_config_update(import->dest, track, 1, hevc_cfg);
		if (gf_list_count(shvc_cfg->param_array)) {
			hevc_set_parall_type(shvc_cfg);

			shvc_cfg->avgFrameRate = hevc_cfg->avgFrameRate;
			shvc_cfg->constantFrameRate = hevc_cfg->constantFrameRate;
			shvc_cfg->numTemporalLayers = hevc_cfg->numTemporalLayers;
			shvc_cfg->temporalIdNested = hevc_cfg->temporalIdNested;

			gf_isom_shvc_config_update(import->dest, track, 1, shvc_cfg, GF_TRUE);
		}
	} else {
		hevc_set_parall_type(shvc_cfg);
		gf_isom_shvc_config_update(import->dest, track, 1, shvc_cfg, GF_FALSE);
	}

	gf_media_update_par(import->dest, track);
	gf_media_update_bitrate(import->dest, track);

//	gf_isom_set_pl_indication(import->dest, GF_ISOM_PL_VISUAL, 0x15);
	gf_isom_set_brand_info(import->dest, GF_ISOM_BRAND_ISO4, 1);
	gf_isom_modify_alternate_brand(import->dest, GF_ISOM_BRAND_ISOM, 0);
	gf_isom_modify_alternate_brand(import->dest, GF_4CC('h','v','c','1'), 1);

	if (!vpss && !ppss && !spss) {
		e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Import results: No SPS or PPS found in the bitstream ! Nothing imported\n");
	} else {
		if (nb_sp || nb_si) {
			gf_import_message(import, GF_OK, "HEVC Import results: %d samples - Slices: %d I %d P %d B %d SP %d SI - %d SEI - %d IDR",
			                  cur_samp, nb_i, nb_p, nb_b, nb_sp, nb_si, nb_sei, nb_idr);
		} else {
			gf_import_message(import, GF_OK, "HEVC Import results: %d samples - Slices: %d I %d P %d B - %d SEI - %d IDR",
			                  cur_samp, nb_i, nb_p, nb_b, nb_sei, nb_idr);
		}

		if (max_total_delay>1) {
			gf_import_message(import, GF_OK, "Stream uses forward prediction - stream CTS offset: %d frames", max_total_delay);
		}
	}

	if (use_opengop_gdr==2) {
		gf_import_message(import, GF_OK, "OpenGOP detected - adjusting file brand");
		gf_isom_modify_alternate_brand(import->dest, GF_4CC('i', 's', 'o', '6'), 1);
	}

	/*rewrite ESD*/
	if (import->esd) {
		if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig*) gf_odf_desc_new(GF_ODF_SLC_TAG);
		import->esd->slConfig->predefined = 2;
		import->esd->slConfig->timestampResolution = timescale;
		if (import->esd->decoderConfig) gf_odf_desc_del((GF_Descriptor *)import->esd->decoderConfig);
		import->esd->decoderConfig = gf_isom_get_decoder_config(import->dest, track, 1);
		gf_isom_change_mpeg4_description(import->dest, track, 1, import->esd);
	}

exit:
	if (sample_data) gf_bs_del(sample_data);
	gf_odf_hevc_cfg_del(hevc_cfg);
	gf_odf_hevc_cfg_del(shvc_cfg);
	gf_free(buffer);
	gf_bs_del(bs);
	gf_fclose(mdia);
	return e;
#endif //GPAC_DISABLE_HEVC
}