static GF_Err gf_import_avc_h264()

in src/media_tools/media_import.c [4545:5496]


static GF_Err gf_import_avc_h264(GF_MediaImporter *import)
{
	u64 nal_start, nal_end, total_size;
	u32 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;
	u8 nal_type;
	GF_Err e;
	FILE *mdia;
	AVCState avc;
	GF_AVCConfigSlot *slc;
	GF_AVCConfig *avccfg, *svccfg, *dstcfg;
	GF_BitStream *bs;
	GF_BitStream *sample_data;
	Bool flush_sample, sample_is_rap, sample_has_islice, is_islice, first_nal, slice_is_ref, has_cts_offset, detect_fps, 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_avc;
	u32 use_opengop_gdr = 0;
	u32 last_svc_sps;
	u32 prev_nalu_prefix_size, res_prev_nalu_prefix;
	u8 priority_prev_nalu_prefix;
	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
			detect_fps = GF_FALSE;								/*fps is forced by the caller*/
	}
	get_video_timing(FPS, &timescale, &dts_inc);

	poc_diff = 0;


restart_import:

	memset(&avc, 0, sizeof(AVCState));
	avc.sps_active_idx = -1;
	avccfg = gf_odf_avc_cfg_new();
	svccfg = gf_odf_avc_cfg_new();
	/*we don't handle split import (one track / layer)*/
	svccfg->complete_representation = 1;
	buffer = (char*)gf_malloc(sizeof(char) * max_size);
	sample_data = NULL;
	first_avc = GF_TRUE;
	last_svc_sps = 0;
	sei_recovery_frame_count = -1;

	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 H264 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_avc_config_new(import->dest, track, avccfg, NULL, NULL, &di);
	if (e) goto exit;

	gf_isom_set_nalu_extract_mode(import->dest, track, GF_ISOM_NALU_EXTRACT_INSPECT);

	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;
	prev_nalu_prefix_size = 0;
	res_prev_nalu_prefix = 0;
	priority_prev_nalu_prefix = 0;

	while (gf_bs_available(bs)) {
		u8 nal_hdr, skip_nal, is_subseq, add_sps;
		u32 nal_and_trailing_size;

		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);
		nal_hdr = gf_bs_read_u8(bs);
		nal_type = nal_hdr & 0x1F;

		is_subseq = 0;
		skip_nal = 0;
		copy_size = flush_sample = GF_FALSE;
		is_islice = GF_FALSE;

		if (nal_type == GF_AVC_NALU_SVC_SUBSEQ_PARAM || nal_type == GF_AVC_NALU_SVC_PREFIX_NALU || nal_type == GF_AVC_NALU_SVC_SLICE) {
			avc.is_svc = GF_TRUE;
		}

		switch (gf_media_avc_parse_nalu(bs, nal_hdr, &avc)) {
		case 1:
			flush_sample = GF_TRUE;
			break;
		case -1:
			gf_import_message(import, GF_OK, "Waring: Error parsing NAL unit");
			skip_nal = 1;
			break;
		case -2:
			skip_nal = 1;
			break;
		default:
			break;
		}
		switch (nal_type) {
		case GF_AVC_NALU_SVC_SUBSEQ_PARAM:
			is_subseq = 1;
		case GF_AVC_NALU_SEQ_PARAM:
			idx = gf_media_avc_read_sps(buffer, nal_size, &avc, is_subseq, NULL);
			if (idx<0) {
				if (avc.sps[0].profile_idc) {
					GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("Error parsing SeqInfo"));
				} else {
					e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing SeqInfo");
					goto exit;
				}
			}
			add_sps = 0;
			dstcfg = (import->flags & GF_IMPORT_SVC_EXPLICIT) ? svccfg : avccfg;
			if (is_subseq) {
				if ((avc.sps[idx].state & AVC_SUBSPS_PARSED) && !(avc.sps[idx].state & AVC_SUBSPS_DECLARED)) {
					avc.sps[idx].state |= AVC_SUBSPS_DECLARED;
					add_sps = 1;
				}
				dstcfg = svccfg;
				if (import->flags & GF_IMPORT_SVC_NONE) {
					add_sps = 0;
					skip_nal = 1;
				}
			} else {
				if ((avc.sps[idx].state & AVC_SPS_PARSED) && !(avc.sps[idx].state & AVC_SPS_DECLARED)) {
					avc.sps[idx].state |= AVC_SPS_DECLARED;
					add_sps = 1;
				}
			}
			/*some streams are not really nice and reuse sps idx with differnet parameters (typically
			when concatenated bitstreams). Since we cannot put two SPS with the same idx in the decoder config, we keep them in the
			video bitstream*/
			if (avc.sps[idx].state & AVC_SUBSPS_DECLARED) {
				if (import->flags & GF_IMPORT_SVC_NONE) {
					copy_size = 0;
				} else {
					copy_size = nal_size;
				}
			}

			//always keep NAL
			if (import->flags & GF_IMPORT_FORCE_XPS_INBAND) {
				copy_size = nal_size;
			}

			//first declaration of SPS,
			if (add_sps) {
				dstcfg->configurationVersion = 1;
				dstcfg->profile_compatibility = avc.sps[idx].prof_compat;
				dstcfg->AVCProfileIndication = avc.sps[idx].profile_idc;
				dstcfg->AVCLevelIndication = avc.sps[idx].level_idc;
				dstcfg->chroma_format = avc.sps[idx].chroma_format;
				dstcfg->luma_bit_depth = 8 + avc.sps[idx].luma_bit_depth_m8;
				dstcfg->chroma_bit_depth = 8 + avc.sps[idx].chroma_bit_depth_m8;


				if (import->flags & GF_IMPORT_FORCE_XPS_INBAND) {
					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(dstcfg->sequenceParameterSets, slc);
				}

				/*disable frame rate scan, most bitstreams have wrong values there*/
				if (detect_fps && avc.sps[idx].vui.timing_info_present_flag
				        /*if detected FPS is greater than 1000, assume wrong timing info*/
				        && (avc.sps[idx].vui.time_scale <= 1000*avc.sps[idx].vui.num_units_in_tick)
				   ) {
					/*ISO/IEC 14496-10 n11084 Table E-6*/
					/* not used :				u8 DeltaTfiDivisorTable[] = {1,1,1,2,2,2,2,3,3,4,6}; */
					u8 DeltaTfiDivisorIdx;
					if (!avc.sps[idx].vui.pic_struct_present_flag) {
						DeltaTfiDivisorIdx = 1 + (1-avc.s_info.field_pic_flag);
					} else {
						if (!avc.sei.pic_timing.pic_struct)
							DeltaTfiDivisorIdx = 2;
						else if (avc.sei.pic_timing.pic_struct == 8)
							DeltaTfiDivisorIdx = 6;
						else
							DeltaTfiDivisorIdx = (avc.sei.pic_timing.pic_struct+1) / 2;
					}
					timescale = 2 * avc.sps[idx].vui.time_scale;
					dts_inc =   2 * avc.sps[idx].vui.num_units_in_tick * DeltaTfiDivisorIdx;
					FPS = (Double)timescale / dts_inc;
					detect_fps = GF_FALSE;

					if (avc.sps[idx].vui.fixed_frame_rate_flag)
						GF_LOG(GF_LOG_INFO, GF_LOG_CODING, ("[avc-h264] Possible Variable Frame Rate: VUI \"fixed_frame_rate_flag\" absent.\n"));

					gf_isom_remove_track(import->dest, track);
					if (sample_data) gf_bs_del(sample_data);
					gf_odf_avc_cfg_del(avccfg);
					avccfg = NULL;
					gf_odf_avc_cfg_del(svccfg);
					svccfg = NULL;
					gf_free(buffer);
					buffer = NULL;
					gf_bs_del(bs);
					bs = NULL;
					gf_fseek(mdia, 0, SEEK_SET);
					goto restart_import;
				}

				if (is_subseq) {
					if (last_svc_sps<(u32) idx) {
						if (import->flags & GF_IMPORT_SVC_EXPLICIT) {
							gf_import_message(import, GF_OK, "SVC-H264 import - frame size %d x %d at %02.3f FPS", avc.sps[idx].width, avc.sps[idx].height, FPS);
						} else {
							gf_import_message(import, GF_OK, "SVC Detected - SSPS ID %d - frame size %d x %d", idx-GF_SVC_SSPS_ID_SHIFT, avc.sps[idx].width, avc.sps[idx].height);
						}
						last_svc_sps = idx;
					}
					/* prevent from adding the subseq PS to the samples */
					copy_size = 0;
				} else {
					if (first_avc) {
						first_avc = GF_FALSE;
						if (!(import->flags & GF_IMPORT_SVC_EXPLICIT)) {
							gf_import_message(import, GF_OK, "AVC-H264 import - frame size %d x %d at %02.3f FPS", avc.sps[idx].width, avc.sps[idx].height, FPS);
						}
					}
				}
				if (!is_subseq || (import->flags & GF_IMPORT_SVC_EXPLICIT)) {
					if ((max_w <= avc.sps[idx].width) && (max_h <= avc.sps[idx].height)) {
						max_w = avc.sps[idx].width;
						max_h = avc.sps[idx].height;
					}
				}
			}
			break;
		case GF_AVC_NALU_PIC_PARAM:
			idx = gf_media_avc_read_pps(buffer, nal_size, &avc);
			if (idx<0) {
				e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing Picture Param");
				goto exit;
			}
			/*some streams are not really nice and reuse sps idx with differnet parameters (typically
			when concatenated bitstreams). Since we cannot put two SPS with the same idx in the decoder config, we keep them in the
			video bitstream*/
			if (avc.pps[idx].status == 2) {
				copy_size = nal_size;
			}

			//always keep NAL
			if (import->flags & GF_IMPORT_FORCE_XPS_INBAND) {
				copy_size = nal_size;
			} else {
				if (avc.pps[idx].status==1) {
					avc.pps[idx].status = 2;
					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);
					dstcfg = (import->flags & GF_IMPORT_SVC_EXPLICIT) ? svccfg : avccfg;

					/* by default, we put all PPS in the base AVC layer,
					  they will be moved to the SVC layer upon analysis of SVC slice. */
					dstcfg = avccfg;

					if (import->flags & GF_IMPORT_SVC_EXPLICIT)
						dstcfg = svccfg;

					gf_list_add(dstcfg->pictureParameterSets, slc);
				}
			}
			break;
		case GF_AVC_NALU_SEI:
			if (avc.sps_active_idx != -1) {
				copy_size = gf_media_avc_reformat_sei(buffer, nal_size, &avc);
				if (copy_size)
					nb_sei++;
			}
			break;

		case GF_AVC_NALU_NON_IDR_SLICE:
		case GF_AVC_NALU_DP_A_SLICE:
		case GF_AVC_NALU_DP_B_SLICE:
		case GF_AVC_NALU_DP_C_SLICE:
		case GF_AVC_NALU_IDR_SLICE:
			if (! skip_nal) {
				copy_size = nal_size;
				switch (avc.s_info.slice_type) {
				case GF_AVC_TYPE_P:
				case GF_AVC_TYPE2_P:
					nb_p++;
					break;
				case GF_AVC_TYPE_I:
				case GF_AVC_TYPE2_I:
					nb_i++;
					is_islice = GF_TRUE;
					break;
				case GF_AVC_TYPE_B:
				case GF_AVC_TYPE2_B:
					nb_b++;
					break;
				case GF_AVC_TYPE_SP:
				case GF_AVC_TYPE2_SP:
					nb_sp++;
					break;
				case GF_AVC_TYPE_SI:
				case GF_AVC_TYPE2_SI:
					nb_si++;
					break;
				}
			}
			break;

		/*remove*/
		case GF_AVC_NALU_ACCESS_UNIT:
		case GF_AVC_NALU_FILLER_DATA:
		case GF_AVC_NALU_END_OF_SEQ:
		case GF_AVC_NALU_END_OF_STREAM:
			break;

		case GF_AVC_NALU_SVC_PREFIX_NALU:
			if (import->flags & GF_IMPORT_SVC_NONE) {
				copy_size = 0;
				break;
			}
			assert(prev_nalu_prefix_size==0);
			copy_size = nal_size;
			break;
		case GF_AVC_NALU_SVC_SLICE:
		{
			u32 i;
			for (i = 0; i < gf_list_count(avccfg->pictureParameterSets); i ++) {
				slc = (GF_AVCConfigSlot*)gf_list_get(avccfg->pictureParameterSets, i);
				if (avc.s_info.pps->id == slc->id) {
					/* This PPS is used by an SVC NAL unit, it should be moved to the SVC Config Record) */
					gf_list_rem(avccfg->pictureParameterSets, i);
					i--;
					gf_list_add(svccfg->pictureParameterSets, slc);
				}
			}
		}
		if (import->flags & GF_IMPORT_SVC_NONE) {
			skip_nal = 0;
			copy_size = 0;
			break;
		}
		if (! skip_nal) {
			copy_size = nal_size;
			switch (avc.s_info.slice_type) {
			case GF_AVC_TYPE_P:
			case GF_AVC_TYPE2_P:
				avc.s_info.sps->nb_ep++;
				break;
			case GF_AVC_TYPE_I:
			case GF_AVC_TYPE2_I:
				avc.s_info.sps->nb_ei++;
				break;
			case GF_AVC_TYPE_B:
			case GF_AVC_TYPE2_B:
				avc.s_info.sps->nb_eb++;
				break;
			}
		}
		break;

		case GF_AVC_NALU_SEQ_PARAM_EXT:
			idx = gf_media_avc_read_sps_ext(buffer, nal_size);
			if (idx<0) {
				e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Error parsing Sequence Param Extension");
				goto exit;
			}

			if (! (avc.sps[idx].state & AVC_SPS_EXT_DECLARED)) {
				avc.sps[idx].state |= AVC_SPS_EXT_DECLARED;

				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);

				if (!avccfg->sequenceParameterSetExtensions)
					avccfg->sequenceParameterSetExtensions = gf_list_new();

				gf_list_add(avccfg->sequenceParameterSetExtensions, slc);
			}
			break;

		case GF_AVC_NALU_SLICE_AUX:

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

		if (!nal_size) break;

		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, ("[AVC 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;

			if (prev_nalu_prefix_size) {
				u32 size, reserved, nb_subs;
				u8 priority;
				Bool discardable;

				samp->dataLength -= size_length/8 + prev_nalu_prefix_size;

				if (set_subsamples) {
					/* determine the number of subsamples */
					nb_subs = gf_isom_sample_has_subsamples(import->dest, track, cur_samp+1);
					if (nb_subs) {
						/* fetch size, priority, reserved and discardable info for last subsample */
						gf_isom_sample_get_subsample(import->dest, track, cur_samp+1, nb_subs, &size, &priority, &reserved, &discardable);

						/*remove last subsample entry!*/
						gf_isom_add_subsample(import->dest, track, cur_samp+1, 0, 0, 0, GF_FALSE);
					}
				}

				/*rewrite last NALU prefix at the beginning of next sample*/
				sample_data = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
				gf_bs_write_data(sample_data, samp->data + samp->dataLength, size_length/8 + prev_nalu_prefix_size);

				if (set_subsamples) {
					/*add subsample entry to next sample*/
					gf_isom_add_subsample(import->dest, track, cur_samp+2, size_length/8 + prev_nalu_prefix_size, priority, reserved, discardable);
				}

				prev_nalu_prefix_size = 0;
			}
			/*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 AVC-H264", (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;
		}

		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 AVC 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);

			/*fixme - we need finer grain for priority*/
			if ((nal_type==GF_AVC_NALU_SVC_PREFIX_NALU) || (nal_type==GF_AVC_NALU_SVC_SLICE)) {
				u32 res = 0;
				u8 prio;
				unsigned char *p = (unsigned char *) buffer;
				res |= (p[0] & 0x60) ? 0x80000000 : 0; // RefPicFlag
				res |= 0 ? 0x40000000 : 0;             // RedPicFlag TODO: not supported, would require to parse NAL unit payload
				res |= (1<=nal_type && nal_type<=5) || (nal_type==GF_AVC_NALU_SVC_PREFIX_NALU) || (nal_type==GF_AVC_NALU_SVC_SLICE) ? 0x20000000 : 0;  // VclNALUnitFlag
				res |= p[1] << 16;                     // use values of IdrFlag and PriorityId directly from SVC extension header
				res |= p[2] << 8;                      // use values of DependencyId and QualityId directly from SVC extension header
				res |= p[3] & 0xFC;                    // use values of TemporalId and UseRefBasePicFlag directly from SVC extension header
				res |= 0 ? 0x00000002 : 0;             // StoreBaseRepFlag TODO: SVC FF mentions a store_base_rep_flag which cannot be found in SVC spec

				// priority_id (6 bits) in SVC has inverse meaning -> lower value means higher priority - invert it and scale it to 8 bits
				prio = (63 - (p[1] & 0x3F)) << 2;

				if (set_subsamples) {
					gf_isom_add_subsample(import->dest, track, cur_samp+1, copy_size+size_length/8, prio, res, GF_TRUE);
				}

				if (nal_type==GF_AVC_NALU_SVC_PREFIX_NALU) {
					/* remember reserved and priority value */
					res_prev_nalu_prefix = res;
					priority_prev_nalu_prefix = prio;
				}
			} else 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, priority_prev_nalu_prefix, res_prev_nalu_prefix, GF_FALSE);
			}
			if (nal_type!=GF_AVC_NALU_SVC_PREFIX_NALU) {
				res_prev_nalu_prefix = 0;
				priority_prev_nalu_prefix = 0;
			}

			if (nal_type != GF_AVC_NALU_SVC_PREFIX_NALU) {
				prev_nalu_prefix_size = 0;
			} else {
				prev_nalu_prefix_size = nal_size;
			}

			switch (nal_type) {
			case GF_AVC_NALU_SVC_SLICE:
			case GF_AVC_NALU_NON_IDR_SLICE:
			case GF_AVC_NALU_DP_A_SLICE:
			case GF_AVC_NALU_DP_B_SLICE:
			case GF_AVC_NALU_DP_C_SLICE:
			case GF_AVC_NALU_IDR_SLICE:
			case GF_AVC_NALU_SLICE_AUX:
//			case GF_AVC_NALU_SVC_SLICE:
				if (!is_paff && avc.s_info.bottom_field_flag)
					is_paff = GF_TRUE;

				slice_is_ref = (avc.s_info.nal_unit_type==GF_AVC_NALU_IDR_SLICE) ? GF_TRUE : GF_FALSE;
				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 (avc.sei.recovery_point.valid || (import->flags & GF_IMPORT_FORCE_SYNC)) {
						Bool bIntraSlice = gf_media_avc_slice_is_intra(&avc);
						assert(avc.s_info.nal_unit_type!=GF_AVC_NALU_IDR_SLICE || bIntraSlice);

						sei_recovery_frame_count = avc.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 (!avc.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, ("[AVC Import] No valid SEI Recovery Point found although needed - forcing\n"));
							}
						}
						avc.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_avc_slice_is_IDR(&avc);
				}

				if (avc.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 -= avc.s_info.poc;
							gf_isom_modify_cts_offset(import->dest, track, j, samp->CTS_Offset);
							gf_isom_sample_del(&samp);
						}
					}
					poc_shift = avc.s_info.poc;
				}

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

		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, ("[avc-h264] 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, ("[avc-h264] 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 AVC-H264", (u32) cur_samp, cur_samp+1);
		cur_samp++;
	}


	/*recompute all CTS offsets*/
	if (has_cts_offset) {
		u32 i, 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 AVC-H264", (u32) cur_samp, cur_samp);

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


	if (import->flags & GF_IMPORT_FORCE_XPS_INBAND) {
		gf_isom_avc_config_update(import->dest, track, 1, avccfg);
		gf_isom_avc_set_inband_config(import->dest, track, 1);
	} else if (gf_list_count(avccfg->sequenceParameterSets) || !gf_list_count(svccfg->sequenceParameterSets) ) {
		gf_isom_avc_config_update(import->dest, track, 1, avccfg);
		if (gf_list_count(svccfg->sequenceParameterSets)) {
			gf_isom_svc_config_update(import->dest, track, 1, svccfg, GF_TRUE);
		}
	} else {
		gf_isom_svc_config_update(import->dest, track, 1, svccfg, 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, 0x7F);
	gf_isom_modify_alternate_brand(import->dest, GF_ISOM_BRAND_AVC1, 1);

	if (!gf_list_count(avccfg->sequenceParameterSets) && !gf_list_count(svccfg->sequenceParameterSets) && !(import->flags & GF_IMPORT_FORCE_XPS_INBAND)) {
		e = gf_import_message(import, GF_NON_COMPLIANT_BITSTREAM, "Import results: No SPS or PPS found in the bitstream ! Nothing imported\n");
	} else {
		u32 i;
		if (nb_sp || nb_si) {
			gf_import_message(import, GF_OK, "AVC 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, "AVC 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);
		}

		for (i=0; i<gf_list_count(svccfg->sequenceParameterSets); i++) {
			AVC_SPS *sps;
			GF_AVCConfigSlot *svcc = (GF_AVCConfigSlot*)gf_list_get(svccfg->sequenceParameterSets, i);
			sps = & avc.sps[svcc->id];
			if (sps && (sps->state & AVC_SUBSPS_PARSED)) {
				gf_import_message(import, GF_OK, "SVC (SSPS ID %d) Import results: Slices: %d I %d P %d B", svcc->id - GF_SVC_SSPS_ID_SHIFT, sps->nb_ei, sps->nb_ep, sps->nb_eb);
			}
		}

		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_avc_cfg_del(avccfg);
	gf_odf_avc_cfg_del(svccfg);
	gf_free(buffer);
	gf_bs_del(bs);
	gf_fclose(mdia);
	return e;
}