GF_Err gf_isom_set_dolby_vision_profile(GF_ISOFile* movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_DOVIDecoderConfigurationRecord *dvcc)
{
	GF_Err e;
	GF_TrackBox* trak;
	GF_Box *dv_cfge = NULL;
	GF_MPEGVisualSampleEntryBox* entry;
	Bool switch_type = GF_FALSE;
	Bool is_avc = GF_FALSE;
	GF_SampleDescriptionBox* stsd;
	GF_DOVIConfigurationBox* dovi = NULL;
	e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
	if (e) return e;

	trak = gf_isom_get_track_from_file(movie, trackNumber);
	if (!trak) return GF_BAD_PARAM;

	stsd = trak->Media->information->sampleTable->SampleDescription;
	if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
	if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
		return movie->LastError = GF_BAD_PARAM;
	}
	entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
	//no support for generic sample entries (eg, no MPEG4 descriptor)
	if (entry == NULL) return GF_BAD_PARAM;
	if (!movie->keep_utc)
		trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();

	if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_OK;

	if (dvcc) {
		switch_type = dvcc->force_dv;
		switch (dvcc->dv_profile) {
		case 1:
		case 3:
		case 5:
			switch_type = 1;
			break;
		}
	}

	switch (entry->type) {
	case GF_ISOM_BOX_TYPE_HEV1:
	case GF_ISOM_BOX_TYPE_HEV2:
	case GF_ISOM_BOX_TYPE_DVHE:
		entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVHE : GF_ISOM_BOX_TYPE_HEV1;
		break;
	case GF_ISOM_BOX_TYPE_HVC1:
	case GF_ISOM_BOX_TYPE_HVC2:
	case GF_ISOM_BOX_TYPE_DVH1:
		entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVH1 : GF_ISOM_BOX_TYPE_HVC1;
		break;
	case GF_ISOM_BOX_TYPE_AVC1:
	case GF_ISOM_BOX_TYPE_DVA1:
		is_avc = GF_TRUE;
		entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVA1 : GF_ISOM_BOX_TYPE_AVC1;
		break;
	case GF_ISOM_BOX_TYPE_AVC3:
	case GF_ISOM_BOX_TYPE_DVAV:
		is_avc = GF_TRUE;
		entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVAV : GF_ISOM_BOX_TYPE_AVC3;
		break;
	case GF_ISOM_BOX_TYPE_AV01:
	case GF_ISOM_BOX_TYPE_DAV1:
		entry->type = switch_type ? GF_ISOM_BOX_TYPE_DAV1 : GF_ISOM_BOX_TYPE_AV01;
		break;
	default:
		return GF_NOT_SUPPORTED;
	}

	u32 dve_type = is_avc ? GF_ISOM_BOX_TYPE_AVCE : GF_ISOM_BOX_TYPE_HVCE;
	dv_cfge = gf_isom_box_find_child(entry->child_boxes, dve_type);

	dovi = entry->dovi_config;
	if (!dvcc) {
		if (dovi) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)dovi);
		entry->dovi_config = NULL;

		if (dv_cfge) gf_isom_box_del_parent(&entry->child_boxes, dv_cfge);
		//reverse entry type
		switch (entry->type) {
		case GF_ISOM_BOX_TYPE_DVHE:
			entry->type = GF_ISOM_BOX_TYPE_HEV1;
			break;
		case GF_ISOM_BOX_TYPE_DVH1:
			entry->type = GF_ISOM_BOX_TYPE_HVC1;
			break;
		case GF_ISOM_BOX_TYPE_DVA1:
			entry->type = GF_ISOM_BOX_TYPE_AVC1;
			break;
		case GF_ISOM_BOX_TYPE_DVAV:
			entry->type = GF_ISOM_BOX_TYPE_AVC3;
			break;
		case GF_ISOM_BOX_TYPE_DAV1:
			entry->type = GF_ISOM_BOX_TYPE_AV01;
			break;
		default:
			break;
		}
		return GF_OK;
	}
	if (!dovi) {
		dovi = (GF_DOVIConfigurationBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_DVCC);
		if (!dovi) return GF_OUT_OF_MEM;
		entry->dovi_config = dovi;
	}
	if (dvcc->dv_profile < 8) {
		dovi->type = GF_ISOM_BOX_TYPE_DVCC;
	} else {
		dovi->type = GF_ISOM_BOX_TYPE_DVVC;
	}
	dovi->DOVIConfig = *dvcc;

	if (dv_cfge) gf_isom_box_del_parent(&entry->child_boxes, dv_cfge);
	dv_cfge = NULL;

	//inject avcE / hvcE if not split layer - not clear from the spec what is supposed to be in these, we just clone avcC/hvcC
	if (dvcc->bl_present_flag) {
		GF_Box *src = is_avc ? (GF_Box *)entry->avc_config : (GF_Box *)entry->hevc_config;
		if (!src) return GF_BAD_PARAM;
		e = gf_isom_clone_box(src, &dv_cfge);
		if (e) return e;
		dv_cfge->type = dve_type;
		gf_list_add(entry->child_boxes, dv_cfge);
	}
	return GF_OK;
}
GF_Err gf_isom_set_dolby_vision_profile(GF_ISOFile* movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_DOVIDecoderConfigurationRecord *dvcc)
{
	GF_Err e;
	GF_TrackBox* trak;
	GF_Box *dv_cfge = NULL;
	GF_MPEGVisualSampleEntryBox* entry;
	Bool switch_type = GF_FALSE;
	Bool is_avc = GF_FALSE;
	GF_SampleDescriptionBox* stsd;
	GF_DOVIConfigurationBox* dovi = NULL;
	e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
	if (e) return e;

	trak = gf_isom_get_track_from_file(movie, trackNumber);
	if (!trak) return GF_BAD_PARAM;

	stsd = trak->Media->information->sampleTable->SampleDescription;
	if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE;
	if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->child_boxes)) {
		return movie->LastError = GF_BAD_PARAM;
	}
	entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(stsd->child_boxes, StreamDescriptionIndex - 1);
	 
	if (entry == NULL) return GF_BAD_PARAM;
	if (!movie->keep_utc)
		trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time();

	if (entry->internal_type != GF_ISOM_SAMPLE_ENTRY_VIDEO) return GF_OK;

	if (dvcc) {
		switch_type = dvcc->force_dv;
		switch (dvcc->dv_profile) {
		case 1:
		case 3:
		case 5:
			switch_type = 1;
			break;
		}
	}

	switch (entry->type) {
	case GF_ISOM_BOX_TYPE_HEV1:
	case GF_ISOM_BOX_TYPE_HEV2:
	case GF_ISOM_BOX_TYPE_DVHE:
		entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVHE : GF_ISOM_BOX_TYPE_HEV1;
		break;
	case GF_ISOM_BOX_TYPE_HVC1:
	case GF_ISOM_BOX_TYPE_HVC2:
	case GF_ISOM_BOX_TYPE_DVH1:
		entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVH1 : GF_ISOM_BOX_TYPE_HVC1;
		break;
	case GF_ISOM_BOX_TYPE_AVC1:
	case GF_ISOM_BOX_TYPE_DVA1:
		is_avc = GF_TRUE;
		entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVA1 : GF_ISOM_BOX_TYPE_AVC1;
		break;
	case GF_ISOM_BOX_TYPE_AVC3:
	case GF_ISOM_BOX_TYPE_DVAV:
		is_avc = GF_TRUE;
		entry->type = switch_type ? GF_ISOM_BOX_TYPE_DVAV : GF_ISOM_BOX_TYPE_AVC3;
		break;
	case GF_ISOM_BOX_TYPE_AV01:
	case GF_ISOM_BOX_TYPE_DAV1:
		entry->type = switch_type ? GF_ISOM_BOX_TYPE_DAV1 : GF_ISOM_BOX_TYPE_AV01;
		break;
	default:
		return GF_NOT_SUPPORTED;
	}

	u32 dve_type = is_avc ? GF_ISOM_BOX_TYPE_AVCE : GF_ISOM_BOX_TYPE_HVCE;
	dv_cfge = gf_isom_box_find_child(entry->child_boxes, dve_type);

	dovi = entry->dovi_config;
	if (!dvcc) {
		if (dovi) gf_isom_box_del_parent(&entry->child_boxes, (GF_Box*)dovi);
		entry->dovi_config = NULL;

		if (dv_cfge) gf_isom_box_del_parent(&entry->child_boxes, dv_cfge);
		 
		switch (entry->type) {
		case GF_ISOM_BOX_TYPE_DVHE:
			entry->type = GF_ISOM_BOX_TYPE_HEV1;
			break;
		case GF_ISOM_BOX_TYPE_DVH1:
			entry->type = GF_ISOM_BOX_TYPE_HVC1;
			break;
		case GF_ISOM_BOX_TYPE_DVA1:
			entry->type = GF_ISOM_BOX_TYPE_AVC1;
			break;
		case GF_ISOM_BOX_TYPE_DVAV:
			entry->type = GF_ISOM_BOX_TYPE_AVC3;
			break;
		case GF_ISOM_BOX_TYPE_DAV1:
			entry->type = GF_ISOM_BOX_TYPE_AV01;
			break;
		default:
			break;
		}
		return GF_OK;
	}
	if (!dovi) {
		dovi = (GF_DOVIConfigurationBox*)gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_DVCC);
		if (!dovi) return GF_OUT_OF_MEM;
		entry->dovi_config = dovi;
	}
	if (dvcc->dv_profile < 8) {
		dovi->type = GF_ISOM_BOX_TYPE_DVCC;
	} else {
		dovi->type = GF_ISOM_BOX_TYPE_DVVC;
	}
	dovi->DOVIConfig = *dvcc;

	if (dv_cfge) gf_isom_box_del_parent(&entry->child_boxes, dv_cfge);
	dv_cfge = NULL;

	 
	if (dvcc->bl_present_flag) {
		GF_Box *src = is_avc ? (GF_Box *)entry->avc_config : (GF_Box *)entry->hevc_config;
		if (!src) return GF_BAD_PARAM;
		e = gf_isom_clone_box(src, &dv_cfge);
		if (e) return e;
		dv_cfge->type = dve_type;
		gf_list_add(entry->child_boxes, dv_cfge);
	}
	return GF_OK;
}