static RBinWasmTypeEntry *parse_type_entry(RBinWasmObj *bin, ut64 bound, ut32 index) {
	RBuffer *b = bin->buf;
	RBinWasmTypeEntry *type = R_NEW0 (RBinWasmTypeEntry);
	if (!type) {
		return NULL;
	}
	type->sec_i = index;
	type->file_offset = r_buf_tell (b);
	if (!consume_u7_r (b, bound, &type->form)) {
		goto beach;
	}
	if (type->form != R_BIN_WASM_VALUETYPE_FUNC) {
		R_LOG_WARN ("Halting types section parsing at invalid type 0x%02x at offset: 0x%" PFMTSZx "\n", type->form, type->file_offset);
		goto beach;
	}

	type->args = parse_type_vector (b, bound);
	if (!type->args) {
		goto beach;
	}

	type->rets = parse_type_vector (b, bound);
	if (!type->rets) {
		goto beach;
	}
	r_bin_wasm_type_entry_to_string (type);

	return type;

beach:
	free_type_entry (type);
	return NULL;
}

Fix #20336 - wasm bin parser ##crash


static RBinWasmTableEntry *parse_table_entry(RBinWasmObj *bin, ut64 bound, ut32 index) {
	RBuffer *b = bin->buf;
	RBinWasmTableEntry *table = R_NEW0 (RBinWasmTableEntry);
	if (table) {
		table->sec_i = index;
		table->file_offset = r_buf_tell (b);
		if (!consume_s7_r (b, bound, (st8 *)&table->element_type)) {
			goto beach;
		}
		if (!consume_limits_r (b, bound, &table->limits)) {
			goto beach;
		}
	}
	return table;

beach:
	free (table);
	return NULL;
}

Fix #20336 - wasm bin parser ##crash


RList *r_bin_wasm_get_custom_names(RBinWasmObj *bin) {
	RList *customs = NULL;

	r_return_val_if_fail (bin && bin->g_sections, NULL);

	if (bin->g_names) {
		return bin->g_names;
	}
	if (!(customs = r_bin_wasm_get_sections_by_id (bin->g_sections, R_BIN_WASM_SECTION_CUSTOM))) {
		return r_list_new ();
	}
	// support for multiple "name" sections against spec
	RBinWasmSection *cust = (RBinWasmSection *)r_list_first (customs);
	if (!cust || !cust->name) {
		r_list_free (customs);
		return r_list_new ();
	}
	if (strcmp (cust->name, "name")) {
		r_list_free (customs);
		return r_list_new ();
	}
	bin->g_names = r_bin_wasm_get_custom_name_entries (bin, cust);
	r_list_free (customs);
	return bin->g_names;
}

Fix #20336 - wasm bin parser ##crash


RList *r_bin_wasm_get_sections(RBinWasmObj *bin) {
	RList *ret = NULL;
	RBinWasmSection *ptr = NULL;

	if (!bin) {
		return NULL;
	}
	if (bin->g_sections) {
		return bin->g_sections;
	}
	if (!(ret = r_list_newf ((RListFree)wasm_sec_free))) {
		return NULL;
	}
	RBuffer *b = bin->buf;
	ut64 bound = r_buf_size (b) - 1;
	r_buf_seek (b, 8, R_BUF_SET);
	while (r_buf_tell (b) <= bound) {
		if (!(ptr = R_NEW0 (RBinWasmSection))) {
			return ret;
		}
		if (!consume_u7_r (b, bound, &ptr->id)) {
			goto beach;
		}
		if (!consume_u32_r (b, bound, &ptr->size)) {
			goto beach;
		}
		// against spec. TODO: choose criteria for parsing
		if (ptr->size < 1) {
			goto beach;
			// free (ptr);
			// continue;
		}
		ptr->offset = r_buf_tell (b);
		switch (ptr->id) {
		case R_BIN_WASM_SECTION_CUSTOM:
			// eprintf("custom section: 0x%x, ", (ut32)b->cur);
			if (!consume_encoded_name_new (b, bound, &ptr->name_len, &ptr->name)) {
				goto beach;
			}
			break;
		case R_BIN_WASM_SECTION_TYPE:
			// eprintf("section type: 0x%x, ", (ut32)b->cur);
			ptr->name = strdup ("type");
			ptr->name_len = 4;
			break;
		case R_BIN_WASM_SECTION_IMPORT:
			// eprintf("section import: 0x%x, ", (ut32)b->cur);
			ptr->name = strdup ("import");
			ptr->name_len = 6;
			break;
		case R_BIN_WASM_SECTION_FUNCTION:
			// eprintf("section function: 0x%x, ", (ut32)b->cur);
			ptr->name = strdup ("function");
			ptr->name_len = 8;
			break;
		case R_BIN_WASM_SECTION_TABLE:
			// eprintf("section table: 0x%x, ", (ut32)b->cur);
			ptr->name = strdup ("table");
			ptr->name_len = 5;
			break;
		case R_BIN_WASM_SECTION_MEMORY:
			// eprintf("section memory: 0x%x, ", (ut32)b->cur);
			ptr->name = strdup ("memory");
			ptr->name_len = 6;
			break;
		case R_BIN_WASM_SECTION_GLOBAL:
			// eprintf("section global: 0x%x, ", (ut32)b->cur);
			ptr->name = strdup ("global");
			ptr->name_len = 6;
			break;
		case R_BIN_WASM_SECTION_EXPORT:
			// eprintf("section export: 0x%x, ", (ut32)b->cur);
			ptr->name = strdup ("export");
			ptr->name_len = 6;
			break;
		case R_BIN_WASM_SECTION_START:
			// eprintf("section start: 0x%x\n", (ut32)b->cur);
			ptr->name = strdup ("start");
			ptr->name_len = 5;
			break;
		case R_BIN_WASM_SECTION_ELEMENT:
			// eprintf("section element: 0x%x, ", (ut32)b->cur);
			ptr->name = strdup ("element");
			ptr->name_len = 7;
			break;
		case R_BIN_WASM_SECTION_CODE:
			// eprintf("section code: 0x%x, ", (ut32)b->cur);
			ptr->name = strdup ("code");
			ptr->name_len = 4;
			break;
		case R_BIN_WASM_SECTION_DATA:
			// eprintf("section data: 0x%x, ", (ut32)b->cur);
			ptr->name = strdup ("data");
			ptr->name_len = 4;
			break;
		default:
			eprintf ("[wasm] error: unkown section id: %d\n", ptr->id);
			r_buf_seek (b, ptr->size - 1, R_BUF_CUR);
			continue;
		}
		if (ptr->offset + (ut64)ptr->size - 1 > bound) {
			// TODO: Better error handling here
			ut32 diff = ptr->size - (bound + 1 - ptr->offset);
			eprintf ("[wasm] Artificially reducing size of section %s by 0x%x bytes so it fits in the file\n", ptr->name, diff);
			ptr->size -= diff;
		}
		ptr->payload_data = r_buf_tell (b);
		ptr->payload_len = ptr->size - (ptr->payload_data - ptr->offset);
		if (ptr->payload_len > ptr->size) {
			goto beach;
		}
		r_buf_seek (b, ptr->payload_len, R_BUF_CUR);
		if (!r_list_append (ret, ptr)) {
			free (ptr);
			// should it jump to beach?
		}
		ptr = NULL;
	}
	bin->g_sections = ret;
	return ret;
beach:
	eprintf ("[wasm] error: beach sections\n");
	free (ptr);
	return ret;
}

Fix #20336 - wasm bin parser ##crash


static void free_code_entry(RBinWasmCodeEntry *ptr) {
	if (ptr) {
		free (ptr->locals);
		free (ptr);
	}
}

Fix #20336 - wasm bin parser ##crash


static bool append_rets(RStrBuf *sb, RBinWasmTypeVec *rets) {
	bool ret = true;
	if (!rets->count) {
		ret &= r_strbuf_append (sb, "nil");
	} else if (rets->count == 1) {
		ret &= r_strbuf_append (sb, r_bin_wasm_valuetype_to_string (rets->types[0]));
	} else {
		ret &= strbuf_append_type_vec (sb, rets);
	}
	return ret;
}

Fix #20336 - wasm bin parser ##crash


TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
  auto* params = reinterpret_cast<TfLiteDivParams*>(node->builtin_data);
  OpData* data = reinterpret_cast<OpData*>(node->user_data);

  const TfLiteTensor* input1;
  TF_LITE_ENSURE_OK(context,
                    GetInputSafe(context, node, kInputTensor1, &input1));
  const TfLiteTensor* input2;
  TF_LITE_ENSURE_OK(context,
                    GetInputSafe(context, node, kInputTensor2, &input2));
  TfLiteTensor* output;
  TF_LITE_ENSURE_OK(context,
                    GetOutputSafe(context, node, kOutputTensor, &output));

  // TODO(b/193904910): This can written with C++ templates
#define TF_LITE_CHECK_DIV_NON_ZERO(data_type)                       \
  const auto* input2_data = GetTensorData<data_type>(input2);       \
  const size_t input2_elements = input2->bytes / sizeof(data_type); \
  for (size_t i = 0; i < input2_elements; i++) {                    \
    TF_LITE_ENSURE(context, input2_data[i] != 0);                   \
  }

  if (output->type == kTfLiteFloat32) {
    // Div by zero seems ok in this case, just like in TF case infinities are
    // returned. So we don't do a check at this point.
    EvalDiv<kernel_type>(context, node, params, data, input1, input2, output);
  } else if (output->type == kTfLiteInt32) {
    TF_LITE_CHECK_DIV_NON_ZERO(int32_t);
    EvalDiv<kernel_type>(context, node, params, data, input1, input2, output);
  } else if (output->type == kTfLiteUInt8) {
    TF_LITE_CHECK_DIV_NON_ZERO(uint8_t);
    TF_LITE_ENSURE_OK(
        context, EvalQuantized<kernel_type>(context, node, params, data, input1,
                                            input2, output));
  } else {
    context->ReportError(
        context,
        "Div only supports FLOAT32, INT32 and quantized UINT8 now, got %d.",
        output->type);
    return kTfLiteError;
  }
#undef TF_LITE_CHECK_DIV_NON_ZERO

  return kTfLiteOk;
}

Prevent a division by 0 in division ops.

PiperOrigin-RevId: 385223169
Change-Id: Ia4228960b5d2aa44480385f74bdd70d21a3613c3


static void naludmx_set_dolby_vision(GF_NALUDmxCtx *ctx)
{
	u8 dv_cfg[24];
	if (!ctx->opid)
		return;

	switch (ctx->dv_mode) {
	case DVMODE_NONE:
	case DVMODE_CLEAN:
		return;
	//auto mode, wait until we have RPU or EL to signal profile
	case DVMODE_AUTO:
		if (!ctx->nb_dv_rpu && !ctx->nb_dv_el) return;
		break;
	}

	u32 dv_level = gf_dolby_vision_level(ctx->width, ctx->height, ctx->cur_fps.num, ctx->cur_fps.den, ctx->codecid);

	if (ctx->dv_profile==8) {
		if (!ctx->dv_compatid) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] DV profile 8 used but dv_compatid not set, defaulting to bt709 (=2)\n", ctx->log_name));
			ctx->dv_compatid = 2;
		}
	}

	memset(dv_cfg, 0, sizeof(u8)*24);
	GF_BitStream *bs = gf_bs_new(dv_cfg, 24, GF_BITSTREAM_WRITE);
	gf_bs_write_u8(bs, 1); //version major
	gf_bs_write_u8(bs, 0); //version minor
	gf_bs_write_int(bs, ctx->dv_profile, 7);
	gf_bs_write_int(bs, dv_level, 6);
	gf_bs_write_int(bs, ctx->nb_dv_rpu ? 1 : 0, 1); //rpu present
	gf_bs_write_int(bs, ctx->nb_dv_el ? 1 : 0, 1); //el present
	gf_bs_write_int(bs, 1, 1); //bl_present_flag always true, we don't split streams
	gf_bs_write_int(bs, ctx->dv_compatid, 4);
	//the rest is zero-reserved
	gf_bs_write_int(bs, 0, 28);
	gf_bs_write_u32(bs, 0);
	gf_bs_write_u32(bs, 0);
	gf_bs_write_u32(bs, 0);
	gf_bs_write_u32(bs, 0);
	gf_bs_del(bs);
	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DOLBY_VISION, &PROP_DATA(dv_cfg, 24));
}

fixed #2223


static u64 naludmx_next_start_code(GF_BitStream *bs, u64 offset, u64 fsize, u32 *sc_size)
{
	u32 pos=0, nb_zeros=0;
	while (offset+pos<fsize) {
		u8 b = gf_bs_read_u8(bs);
		pos++;
		switch (b) {
		case 1:
			//break at first 0xXX000001 or 0x00000001
			if (nb_zeros>=2) {
				*sc_size = (nb_zeros==2) ? 3 : 4;
				return offset+pos;
			}
			nb_zeros = 0;
			break;
		case 0:
			nb_zeros++;
			break;
		default:
			nb_zeros=0;
			break;
		}
	}
	//eof
	return 0;
}

fixed #2223


static s32 naludmx_parse_nal_vvc(GF_NALUDmxCtx *ctx, char *data, u32 size, Bool *skip_nal, Bool *is_slice, Bool *is_islice)
{
	s32 ps_idx = 0;
	s32 res;
	u8 nal_unit_type, temporal_id, layer_id;
	*skip_nal = GF_FALSE;

	if (size<2) return -1;
	gf_bs_reassign_buffer(ctx->bs_r, data, size);
	res = gf_vvc_parse_nalu_bs(ctx->bs_r, ctx->vvc_state, &nal_unit_type, &temporal_id, &layer_id);
	ctx->nb_nalus++;

	if (res < 0) {
		if (res == -1) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] Warning: Error parsing NAL unit\n", ctx->log_name));
		}
		*skip_nal = GF_TRUE;
	}

	if (layer_id && ctx->nosvc) {
		*skip_nal = GF_TRUE;
		return 0;
	}

	switch (nal_unit_type) {
	case GF_VVC_NALU_VID_PARAM:
		if (ctx->novpsext) {
			//this may modify nal_size, but we don't use it for bitstream reading
//			ps_idx = gf_hevc_read_vps_ex(data, &size, ctx->hevc_state, GF_TRUE);
			ps_idx = ctx->vvc_state->last_parsed_vps_id;
		} else {
			ps_idx = ctx->vvc_state->last_parsed_vps_id;
		}
		if (ps_idx<0) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] Error parsing Video Param Set\n", ctx->log_name));
		} else {
			naludmx_queue_param_set(ctx, data, size, GF_VVC_NALU_VID_PARAM, ps_idx);
		}
		*skip_nal = GF_TRUE;
		break;
	case GF_VVC_NALU_SEQ_PARAM:
		ps_idx = ctx->vvc_state->last_parsed_sps_id;
		if (ps_idx<0) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] Error parsing Sequence Param Set\n", ctx->log_name));
		} else {
			naludmx_queue_param_set(ctx, data, size, GF_VVC_NALU_SEQ_PARAM, ps_idx);
		}
		*skip_nal = GF_TRUE;
		break;
	case GF_VVC_NALU_PIC_PARAM:
		ps_idx = ctx->vvc_state->last_parsed_pps_id;
		if (ps_idx<0) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] Error parsing Picture Param Set\n", ctx->log_name));
		} else {
			naludmx_queue_param_set(ctx, data, size, GF_VVC_NALU_PIC_PARAM, ps_idx);
		}
		*skip_nal = GF_TRUE;
		break;
	case GF_VVC_NALU_DEC_PARAM:
		ps_idx = 0;
		naludmx_queue_param_set(ctx, data, size, GF_VVC_NALU_DEC_PARAM, ps_idx);
		*skip_nal = GF_TRUE;
		break;
	case GF_VVC_NALU_APS_PREFIX:
		//for now we keep APS in the stream
#if 0
		ps_idx = ctx->vvc_state->last_parsed_aps_id;
		if (ps_idx<0) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] Error parsing Decoder Param Set\n", ctx->log_name));
		} else {
			naludmx_queue_param_set(ctx, data, size, GF_VVC_NALU_APS_PREFIX, ps_idx);
		}
		*skip_nal = GF_TRUE;
#else
		//same logic as SEI
		naludmx_push_prefix(ctx, data, size, GF_FALSE);
		*skip_nal = GF_TRUE;
#endif
		break;
	case GF_VVC_NALU_SEI_PREFIX:
		gf_vvc_parse_sei(data, size, ctx->vvc_state);
		if (!ctx->nosei) {
			ctx->nb_sei++;

			naludmx_push_prefix(ctx, data, size, GF_FALSE);
		} else {
			ctx->nb_nalus--;
		}
		*skip_nal = GF_TRUE;
		break;
	case GF_VVC_NALU_SEI_SUFFIX:
		if (! ctx->is_playing) return 0;
		gf_vvc_parse_sei(data, size, ctx->vvc_state);
		if (ctx->nosei) {
			*skip_nal = GF_TRUE;
			ctx->nb_nalus--;
		} else {
			ctx->nb_sei++;
		}
		break;

	case GF_VVC_NALU_PIC_HEADER:
		if (! ctx->is_playing) return 0;
		break;

	/*slice_segment_layer_rbsp*/
	case GF_VVC_NALU_SLICE_TRAIL:
	case GF_VVC_NALU_SLICE_STSA:
	case GF_VVC_NALU_SLICE_RADL:
	case GF_VVC_NALU_SLICE_RASL:
	case GF_VVC_NALU_SLICE_IDR_W_RADL:
	case GF_VVC_NALU_SLICE_IDR_N_LP:
	case GF_VVC_NALU_SLICE_CRA:
	case GF_VVC_NALU_SLICE_GDR:
		if (! ctx->is_playing) return 0;
		*is_slice = GF_TRUE;
		ctx->last_layer_id = layer_id;
		ctx->last_temporal_id = temporal_id;
		if (! *skip_nal) {
			switch (ctx->vvc_state->s_info.slice_type) {
			case GF_VVC_SLICE_TYPE_P:
				if (layer_id) ctx->nb_e_p++;
				else ctx->nb_p++;
				break;
			case GF_VVC_SLICE_TYPE_I:
				if (layer_id) ctx->nb_e_i++;
				else ctx->nb_i++;
				*is_islice = GF_TRUE;
				break;
			case GF_VVC_SLICE_TYPE_B:
				if (layer_id) ctx->nb_e_b++;
				else ctx->nb_b++;
				break;
			case GF_VVC_SLICE_TYPE_UNKNOWN:
				ctx->vvc_no_stats = GF_TRUE;
				break;
			}
		}
		break;

	case GF_VVC_NALU_ACCESS_UNIT:
		ctx->nb_aud++;
		//no skip AUD in VVC

		if (!ctx->opid) {
			ctx->has_initial_aud = GF_TRUE;
			memcpy(ctx->init_aud, data, 3);
		}
		break;
	/*remove*/
	case GF_VVC_NALU_FILLER_DATA:
	case GF_VVC_NALU_END_OF_SEQ:
	case GF_VVC_NALU_END_OF_STREAM:
		*skip_nal = GF_TRUE;
		break;

	case GF_VVC_NALU_OPI:
		if (! ctx->is_playing) return 0;
		break;

	default:
		if (! ctx->is_playing) return 0;
		GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] NAL Unit type %d not handled - adding\n", ctx->log_name, nal_unit_type));
		break;
	}
	if (*skip_nal) return res;

	ctx->linf[layer_id].layer_id_plus_one = layer_id + 1;
	if (! ctx->linf[layer_id].max_temporal_id ) ctx->linf[layer_id].max_temporal_id = temporal_id;
	else if (ctx->linf[layer_id].max_temporal_id < temporal_id) ctx->linf[layer_id].max_temporal_id = temporal_id;

	if (! ctx->linf[layer_id].min_temporal_id ) ctx->linf[layer_id].min_temporal_id = temporal_id;
	else if (ctx->linf[layer_id].min_temporal_id > temporal_id) ctx->linf[layer_id].min_temporal_id = temporal_id;

	if (ctx->max_temporal_id[layer_id] < temporal_id)
		ctx->max_temporal_id[layer_id] = temporal_id;
	if (ctx->min_layer_id > layer_id) ctx->min_layer_id = layer_id;
	return res;
}

fixed #2223


static Bool naludmx_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
{
	u32 i;
	u64 file_pos = 0;
	GF_FilterEvent fevt;
	GF_NALUDmxCtx *ctx = gf_filter_get_udta(filter);

	switch (evt->base.type) {
	case GF_FEVT_PLAY:
		if (!ctx->is_playing) {
			ctx->is_playing = GF_TRUE;
			ctx->cts = ctx->dts = 0;
		}
		if (! ctx->is_file) {
			if (!ctx->initial_play_done) {
				ctx->initial_play_done = GF_TRUE;
				if (evt->play.start_range<0.1)
					return GF_FALSE;
			}
			ctx->resume_from = 0;
			ctx->nal_store_size = 0;
			return GF_FALSE;
		}
		if (ctx->start_range && (ctx->index<0)) {
			ctx->index = -ctx->index;
			ctx->file_loaded = GF_FALSE;
			ctx->duration.den = ctx->duration.num = 0;
			GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[%s] Play request from %d, building index\n", ctx->log_name, ctx->start_range));
			naludmx_check_dur(filter, ctx);
		}
		ctx->start_range = evt->play.start_range;
		ctx->in_seek = GF_TRUE;

		if (ctx->start_range) {
			ctx->nb_nalus = ctx->nb_i = ctx->nb_p = ctx->nb_b = ctx->nb_sp = ctx->nb_si = ctx->nb_sei = ctx->nb_idr = ctx->nb_cra = 0;
			for (i=1; i<ctx->index_size; i++) {
				if (ctx->indexes[i].duration>ctx->start_range) {
					ctx->cts = (u64) (ctx->indexes[i-1].duration * ctx->cur_fps.num);
					ctx->dts = ctx->dts_last_IDR = ctx->cts;
					file_pos = ctx->indexes[i-1].pos;
					ctx->seek_gdr_count = ctx->indexes[i-1].roll_count;
					if (ctx->seek_gdr_count) ctx->first_gdr = GF_TRUE;
					break;
				}
			}
		}
		if (!ctx->initial_play_done) {
			ctx->initial_play_done = GF_TRUE;
			//seek will not change the current source state, don't send a seek
			if (!file_pos) {
				//very short streams, input is done before we get notified for play and everything stored in memory: flush
				if (gf_filter_pid_is_eos(ctx->ipid) && (ctx->nal_store_size)) {
					gf_filter_post_process_task(filter);
				}
				return GF_TRUE;
			}
		}
		ctx->nb_frames = 0;
		ctx->nb_nalus = 0;
		ctx->resume_from = 0;
		ctx->nal_store_size = 0;

		//post a seek
		GF_FEVT_INIT(fevt, GF_FEVT_SOURCE_SEEK, ctx->ipid);
		fevt.seek.start_offset = file_pos;
		gf_filter_pid_send_event(ctx->ipid, &fevt);

		//cancel event
		return GF_TRUE;

	case GF_FEVT_STOP:
		//don't cancel event
		ctx->is_playing = GF_FALSE;
		ctx->nal_store_size = 0;
		ctx->resume_from = 0;
		ctx->cts = 0;
		return GF_FALSE;

	case GF_FEVT_SET_SPEED:
		//cancel event
		return GF_TRUE;
	default:
		break;
	}
	//by default don't cancel event - to rework once we have downloading in place
	return GF_FALSE;
}

fixed #2223


static void naludmx_del_param_list(GF_List *ps, Bool do_free)
{
	if (!ps) return;
	while (gf_list_count(ps)) {
		GF_NALUFFParam *sl = gf_list_pop_back(ps);
		if (sl->data) gf_free(sl->data);
		gf_free(sl);
	}

	if (do_free)
		gf_list_del(ps);
}

fixed #2223


GF_Err naludmx_process(GF_Filter *filter)
{
	GF_NALUDmxCtx *ctx = gf_filter_get_udta(filter);
	GF_FilterPacket *pck;
	u8 *start;
	u32 nalu_before = ctx->nb_nalus;
	u32 nalu_store_before = 0;
	s32 remain;
	Bool is_eos = GF_FALSE;
	Bool drop_packet = GF_FALSE;
	u64 byte_offset = GF_FILTER_NO_BO;

	//always reparse duration
	if (!ctx->file_loaded)
		naludmx_check_dur(filter, ctx);

	pck = gf_filter_pid_get_packet(ctx->ipid);
	if (!ctx->resume_from && !pck) {
		if (gf_filter_pid_is_eos(ctx->ipid)) {
			if (ctx->nal_store_size) {
				if (!ctx->is_playing)
					return GF_OK;

				start = ctx->nal_store;
				remain = ctx->nal_store_size;
				is_eos = GF_TRUE;
				goto naldmx_flush;
			}
			if (ctx->first_pck_in_au) {
				naludmx_finalize_au_flags(ctx);
			}
			//single-frame stream
			if (!ctx->poc_diff) ctx->poc_diff = 1;
			ctx->strict_poc = STRICT_POC_OFF;
			naludmx_enqueue_or_dispatch(ctx, NULL, GF_TRUE);
			if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
			ctx->src_pck = NULL;
			if (!ctx->opid) return GF_EOS;

			gf_filter_pid_set_info(ctx->opid, GF_PROP_PID_MAX_NALU_SIZE, &PROP_UINT(ctx->max_nalu_size) );
			if (ctx->codecid==GF_CODECID_HEVC) {
				naludmx_set_hevc_oinf(ctx, ctx->max_temporal_id);
				naludmx_set_hevc_linf(ctx);
				gf_filter_pid_set_info_str(ctx->opid, "hevc:min_lid", &PROP_UINT(ctx->min_layer_id) );
			}
			if (ctx->opid)
				gf_filter_pid_set_eos(ctx->opid);
			return GF_EOS;
		}
		return GF_OK;
	}

	if (!ctx->is_playing && ctx->opid)
		return GF_OK;

	//if we have bytes from previous packet in the header, we cannot switch timing until we know what these bytes are
	if (!ctx->nal_store_size)
		naldmx_switch_timestamps(ctx, pck);

	nalu_store_before = ctx->nal_store_size;
	if (!ctx->resume_from && pck) {
		u32 pck_size;
		const u8 *data = gf_filter_pck_get_data(pck, &pck_size);
		if (ctx->nal_store_alloc < ctx->nal_store_size + pck_size) {
			ctx->nal_store_alloc = ctx->nal_store_size + pck_size;
			ctx->nal_store = gf_realloc(ctx->nal_store, sizeof(char)*ctx->nal_store_alloc);
			if (!ctx->nal_store) {
				ctx->nal_store_alloc = 0;
				return GF_OUT_OF_MEM;
			}
		}
		byte_offset = gf_filter_pck_get_byte_offset(pck);
		if (byte_offset != GF_FILTER_NO_BO)
			byte_offset -= ctx->nal_store_size;
		memcpy(ctx->nal_store + ctx->nal_store_size, data, sizeof(char)*pck_size);
		ctx->nal_store_size += pck_size;
		drop_packet = GF_TRUE;
	}
	start = ctx->nal_store;
	remain = ctx->nal_store_size;

	if (ctx->resume_from) {
		if (ctx->opid && gf_filter_pid_would_block(ctx->opid))
			return GF_OK;

		assert(ctx->resume_from < ctx->nal_store_size);
		start += ctx->resume_from;
		remain -= ctx->resume_from;
		ctx->resume_from = 0;

		if (!pck && gf_filter_pid_is_eos(ctx->ipid))
			is_eos = GF_TRUE;
	}

naldmx_flush:
	if (!ctx->bs_r) {
		ctx->bs_r = gf_bs_new(start, remain, GF_BITSTREAM_READ);

#ifndef GPAC_DISABLE_LOG
		if (ctx->bsdbg && gf_log_tool_level_on(GF_LOG_MEDIA, GF_LOG_DEBUG))
			gf_bs_set_logger(ctx->bs_r, naldmx_bs_log, ctx);
#endif

	} else {
		gf_bs_reassign_buffer(ctx->bs_r, start, remain);
	}

    assert(remain>=0);

	while (remain) {
		u8 *pck_data;
		u8 *nal_data;
		u32 nal_size;
		s32 current;
		Bool skip_nal = GF_FALSE;
		u32 sc_size=0;
		u32 nal_type = 0;
		u32 nal_ref_idc = 0;
		s32 next=0;
		u32 next_sc_size=0;
		s32 nal_parse_result;
		Bool slice_is_idr, slice_force_ref;
		Bool is_slice = GF_FALSE;
		Bool is_islice = GF_FALSE;
		Bool bottom_field_flag = GF_FALSE;
		Bool au_start;
		u32 avc_svc_subs_reserved = 0;
		u8 avc_svc_subs_priority = 0;
		Bool recovery_point_valid = GF_FALSE;
		u32 recovery_point_frame_cnt = 0;
		Bool bIntraSlice = GF_FALSE;
		GF_FilterSAPType au_sap_type = GF_FILTER_SAP_NONE;
		Bool slice_is_b = GF_FALSE;
		Bool check_dep = GF_FALSE;
		Bool force_au_flush = GF_FALSE;
		s32 slice_poc = 0;

		//not enough bytes to parse start code + nal hdr
		if (!is_eos && (remain<6)) {
			break;
		}

		//locate next start code
		current = gf_media_nalu_next_start_code(start, remain, &sc_size);
		if (current == remain)
			current = -1;

		//no start code, gontinue gathering data
		if (current<0) {
			break;
		}

		assert(current>=0);

		//unknown data before start of nal, may happen when tuning in, discard
		if (current) {
			assert(remain>=current);
			start += current;
			remain -= current;
			current = 0;
			naldmx_check_timestamp_switch(ctx, &nalu_store_before, current, &drop_packet, pck);
		}

		if (!remain)
			break;

		//not enough bytes to parse start code + nal hdr
		if (!is_eos && (remain<6)) {
			break;
		}

		nal_data = start + sc_size;
		nal_size = remain - sc_size;

		//figure out which nal we need to completely load
		if (ctx->codecid==GF_CODECID_HEVC) {
			nal_type = nal_data[0];
			nal_type = (nal_type & 0x7E) >> 1;

			switch (nal_type) {
			case GF_HEVC_NALU_VID_PARAM:
			case GF_HEVC_NALU_SEQ_PARAM:
			case GF_HEVC_NALU_PIC_PARAM:
				force_au_flush = GF_TRUE;
			case GF_HEVC_NALU_SEI_PREFIX:
			case GF_HEVC_NALU_SEI_SUFFIX:
				break;
			case GF_HEVC_NALU_SLICE_TRAIL_N:
			case GF_HEVC_NALU_SLICE_TSA_N:
			case GF_HEVC_NALU_SLICE_STSA_N:
			case GF_HEVC_NALU_SLICE_RADL_N:
			case GF_HEVC_NALU_SLICE_RASL_N:
			case GF_HEVC_NALU_SLICE_RSV_VCL_N10:
			case GF_HEVC_NALU_SLICE_RSV_VCL_N12:
			case GF_HEVC_NALU_SLICE_RSV_VCL_N14:
				check_dep = GF_TRUE;
				break;
			default:
				if (nal_type<GF_HEVC_NALU_VID_PARAM)
					nal_ref_idc = GF_TRUE;
				break;
			}
			//check if VPS/SPS/PPS lid/tid are greater than last seen VCL. If not, force a picture flush
			//not doing so could lead in dispatching the config changed before the current AU is sent
			if (force_au_flush) {
				if (!ctx->first_pck_in_au) {
					force_au_flush = GF_FALSE;
				} else {
					u8 layer_id = nal_data[0] & 1;
					layer_id<<=5;
					layer_id |= (nal_data[1] & 0xF8) >> 3;
					u8 temporal_id = nal_data[2] & 0x7;
					if (ctx->last_layer_id < layer_id)
						force_au_flush = GF_FALSE;
					else if (ctx->last_layer_id == layer_id) {
						if (ctx->last_temporal_id < temporal_id)
							force_au_flush = GF_FALSE;
					}
				}
			}
		} else if (ctx->codecid==GF_CODECID_VVC) {
			nal_type = nal_data[1]>>3;
			switch (nal_type) {
			case GF_VVC_NALU_OPI:
			case GF_VVC_NALU_DEC_PARAM:
			case GF_VVC_NALU_VID_PARAM:
			case GF_VVC_NALU_SEQ_PARAM:
			case GF_VVC_NALU_PIC_PARAM:
				force_au_flush = GF_TRUE;
			case GF_VVC_NALU_SEI_PREFIX:
			case GF_VVC_NALU_SEI_SUFFIX:
			case GF_VVC_NALU_APS_PREFIX:
			case GF_VVC_NALU_APS_SUFFIX:
			case GF_VVC_NALU_PIC_HEADER:
				break;

			case GF_VVC_NALU_SLICE_TRAIL:
			case GF_VVC_NALU_SLICE_STSA:
			case GF_VVC_NALU_SLICE_RADL:
			case GF_VVC_NALU_SLICE_RASL:
			case GF_VVC_NALU_SLICE_IDR_W_RADL:
			case GF_VVC_NALU_SLICE_IDR_N_LP:
			case GF_VVC_NALU_SLICE_CRA:
			case GF_VVC_NALU_SLICE_GDR:
				if (ctx->deps) {
					check_dep = GF_TRUE;
				}
				break;
			default:
				if (nal_type<GF_HEVC_NALU_VID_PARAM)
					nal_ref_idc = GF_TRUE;
				break;
			}

			//check if VPS/SPS/PPS/OPI/DEC lid/tid are greater than last seen VCL. If not, force a picture flush
			//not doing so could lead in dispatching the config changed before the current AU is sent
			if (force_au_flush) {
				if (!ctx->first_pck_in_au) {
					force_au_flush = GF_FALSE;
				} else {
					u8 layer_id = nal_data[0] & 0x3f;
					u8 temporal_id = (nal_data[1] & 0x7);
					if (ctx->last_layer_id < layer_id)
						force_au_flush = GF_FALSE;
					else if (ctx->last_layer_id == layer_id) {
						if (ctx->last_temporal_id < temporal_id)
							force_au_flush = GF_FALSE;
					}
				}
			}
		} else {
			nal_type = nal_data[0] & 0x1F;
			nal_ref_idc = (nal_data[0] & 0x60) >> 5;
		}

		//locate next NAL start
		next = gf_media_nalu_next_start_code(nal_data, nal_size, &next_sc_size);
		if (!is_eos && (next == nal_size) && !ctx->full_au_source) {
			next = -1;
		}

		//next nal start not found, wait
		if (next<0) {
			break;
		}

		//this is our exact NAL size, without start code
		nal_size = next;

		if (ctx->codecid==GF_CODECID_HEVC) {
			nal_parse_result = naludmx_parse_nal_hevc(ctx, nal_data, nal_size, &skip_nal, &is_slice, &is_islice);
		} else if (ctx->codecid==GF_CODECID_VVC) {
			nal_parse_result = naludmx_parse_nal_vvc(ctx, nal_data, nal_size, &skip_nal, &is_slice, &is_islice);
		} else {
			nal_parse_result = naludmx_parse_nal_avc(ctx, nal_data, nal_size, nal_type, &skip_nal, &is_slice, &is_islice);
		}

		//dispatch right away if analyze
		if (ctx->analyze) {
			skip_nal = GF_FALSE;
			ctx->sei_buffer_size = 0;
		}

		//new frame - if no slices, we detected the new frame on AU delimiter, don't flush new frame !
		if ((nal_parse_result>0) && !ctx->first_slice_in_au) {
			naludmx_end_access_unit(ctx);
		}

		naludmx_check_pid(filter, ctx, force_au_flush);
		if (!ctx->opid) skip_nal = GF_TRUE;

		if (skip_nal) {
			nal_size += sc_size;
			assert((u32) remain >= nal_size);
			start += nal_size;
			remain -= nal_size;
			naldmx_check_timestamp_switch(ctx, &nalu_store_before, nal_size, &drop_packet, pck);
			continue;
		}

		if (!ctx->is_playing) {
			ctx->resume_from = (u32) (start - ctx->nal_store);
            assert(ctx->resume_from<=ctx->nal_store_size);
			GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[%s] not yet playing\n", ctx->log_name));

			if (drop_packet)
				gf_filter_pid_drop_packet(ctx->ipid);
			return GF_OK;
		}
		if (ctx->in_seek) {
			u64 nb_frames_at_seek = (u64) (ctx->start_range * ctx->cur_fps.num);
			if (ctx->cts + ctx->cur_fps.den >= nb_frames_at_seek) {
				//u32 samples_to_discard = (ctx->cts + ctx->dts_inc) - nb_samples_at_seek;
				if (ctx->seek_gdr_count)
					ctx->seek_gdr_count--;
				else
					ctx->in_seek = GF_FALSE;
			}
		}

		if (nal_parse_result<0) {
			if (byte_offset != GF_FILTER_NO_BO) {
				u64 bo = byte_offset;
				bo += (start - ctx->nal_store);

				GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] Error parsing NAL Unit %d (byte offset "LLU" size %d type %d frame %d last POC %d) - skipping\n", ctx->log_name, ctx->nb_nalus, bo, nal_size, nal_type, ctx->nb_frames, ctx->last_poc));
			} else {
				GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] Error parsing NAL Unit %d (size %d type %d frame %d last POC %d) - skipping\n", ctx->log_name, ctx->nb_nalus, nal_size, nal_type, ctx->nb_frames, ctx->last_poc));
			}
			nal_size += sc_size;
			assert((u32) remain >= nal_size);
			start += nal_size;
			remain -= nal_size;
			naldmx_check_timestamp_switch(ctx, &nalu_store_before, nal_size, &drop_packet, pck);
			continue;
		}

		if (check_dep) {
			if ((ctx->codecid==GF_CODECID_HEVC) && ctx->hevc_state->s_info.sps) {
				HEVC_VPS *vps;
				u32 temporal_id = nal_data[1] & 0x7;
				vps = & ctx->hevc_state->vps[ctx->hevc_state->s_info.sps->vps_id];
				if (temporal_id + 1 < vps->max_sub_layers) {
					nal_ref_idc = GF_TRUE;
				}
			} else if (ctx->codecid==GF_CODECID_VVC) {
				if (ctx->vvc_state->s_info.non_ref_pic) {
					nal_ref_idc = GF_FALSE;
				} else {
					//todo
					nal_ref_idc = GF_TRUE;
				}
			}
		}

		if (is_islice) ctx->has_islice = GF_TRUE;

		//store all variables needed to compute POC/CTS and sample SAP and recovery info
		if (ctx->codecid==GF_CODECID_HEVC) {
#ifndef GPAC_DISABLE_HEVC
			slice_is_idr = gf_hevc_slice_is_IDR(ctx->hevc_state);

			recovery_point_valid = ctx->hevc_state->sei.recovery_point.valid;
			recovery_point_frame_cnt = ctx->hevc_state->sei.recovery_point.frame_cnt;
			bIntraSlice = gf_hevc_slice_is_intra(ctx->hevc_state);

			au_sap_type = GF_FILTER_SAP_NONE;
			if (gf_hevc_slice_is_IDR(ctx->hevc_state)) {
				au_sap_type = GF_FILTER_SAP_1;
			}
			else {
				switch (ctx->hevc_state->s_info.nal_unit_type) {
				case GF_HEVC_NALU_SLICE_BLA_W_LP:
				case GF_HEVC_NALU_SLICE_BLA_W_DLP:
					au_sap_type = GF_FILTER_SAP_3;
					break;
				case GF_HEVC_NALU_SLICE_BLA_N_LP:
					au_sap_type = GF_FILTER_SAP_1;
					break;
				case GF_HEVC_NALU_SLICE_CRA:
					au_sap_type = GF_FILTER_SAP_3;
					break;
				}
			}

			slice_poc = ctx->hevc_state->s_info.poc;

			/*need to store TS offsets*/
			switch (ctx->hevc_state->s_info.slice_type) {
			case GF_AVC_TYPE_B:
			case GF_AVC_TYPE2_B:
				slice_is_b = GF_TRUE;
				break;
			}
#endif // GPAC_DISABLE_HEVC
		} else if (ctx->codecid==GF_CODECID_VVC) {
			slice_is_idr = gf_vvc_slice_is_ref(ctx->vvc_state);
			recovery_point_valid = ctx->vvc_state->s_info.recovery_point_valid;
			recovery_point_frame_cnt = ctx->vvc_state->s_info.gdr_recovery_count;

//			commented, set below
//			if (ctx->vvc_state->s_info.irap_or_gdr_pic && !ctx->vvc_state->s_info.gdr_pic)
//				bIntraSlice = GF_TRUE; //gf_hevc_slice_is_intra(ctx->hevc_state);

			au_sap_type = GF_FILTER_SAP_NONE;
			if (ctx->vvc_state->s_info.irap_or_gdr_pic && !ctx->vvc_state->s_info.gdr_pic) {
				bIntraSlice = GF_TRUE;

				switch (ctx->vvc_state->s_info.nal_unit_type) {
				case GF_VVC_NALU_SLICE_CRA:
					au_sap_type = GF_FILTER_SAP_3;
					slice_is_idr = GF_FALSE;
					break;
				case GF_VVC_NALU_SLICE_IDR_N_LP:
					au_sap_type = GF_FILTER_SAP_1;
					break;
				case GF_VVC_NALU_SLICE_IDR_W_RADL:
					au_sap_type = GF_FILTER_SAP_2;
					slice_is_idr = GF_TRUE;
					break;
				}
			} else {
				switch (ctx->vvc_state->s_info.nal_unit_type) {
				case GF_VVC_NALU_SLICE_IDR_N_LP:
					au_sap_type = GF_FILTER_SAP_1;
					slice_is_idr = GF_TRUE;
					bIntraSlice = GF_TRUE;
					break;
				case GF_VVC_NALU_SLICE_CRA:
					au_sap_type = GF_FILTER_SAP_3;
					bIntraSlice = GF_TRUE;
					break;
				case GF_VVC_NALU_SLICE_IDR_W_RADL:
					bIntraSlice = GF_TRUE;
					if (ctx->vvc_state->s_info.gdr_pic) {
						au_sap_type = GF_FILTER_SAP_3;
					} else {
						au_sap_type = GF_FILTER_SAP_1;
						slice_is_idr = GF_TRUE;
					}
					break;
				}
			}

			slice_poc = ctx->vvc_state->s_info.poc;

			/*need to store TS offsets*/
			switch (ctx->vvc_state->s_info.slice_type) {
			case GF_AVC_TYPE_B:
			case GF_AVC_TYPE2_B:
				slice_is_b = GF_TRUE;
				break;
			}
		} else {

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

					// priority_id (6 bits) in SVC has inverse meaning -> lower value means higher priority - invert it and scale it to 8 bits
					avc_svc_subs_priority = (63 - (p[1] & 0x3F)) << 2;
				}
				if (nal_type==GF_AVC_NALU_SVC_PREFIX_NALU) {
                    if (ctx->svc_prefix_buffer_size) {
                        GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] broken bitstream, two consecutive SVC prefix NALU without SVC slice in-between\n", ctx->log_name));
                        ctx->svc_prefix_buffer_size = 0;
                    }

					/* remember reserved and priority value */
					ctx->svc_nalu_prefix_reserved = avc_svc_subs_reserved;
					ctx->svc_nalu_prefix_priority = avc_svc_subs_priority;

					ctx->svc_prefix_buffer_size = nal_size;
					if (ctx->svc_prefix_buffer_size > ctx->svc_prefix_buffer_alloc) {
						ctx->svc_prefix_buffer_alloc = ctx->svc_prefix_buffer_size;
						ctx->svc_prefix_buffer = gf_realloc(ctx->svc_prefix_buffer, ctx->svc_prefix_buffer_size);
					}
					memcpy(ctx->svc_prefix_buffer, start+sc_size, ctx->svc_prefix_buffer_size);

					assert( (u32) remain >= sc_size + nal_size);
					start += sc_size + nal_size;
					remain -= sc_size + nal_size;
					continue;
				}
			} else if (is_slice) {
				// RefPicFlag
				avc_svc_subs_reserved |= (start[0] & 0x60) ? 0x80000000 : 0;
				// VclNALUnitFlag
				avc_svc_subs_reserved |= (1<=nal_type && nal_type<=5) || (nal_type==GF_AVC_NALU_SVC_PREFIX_NALU) || (nal_type==GF_AVC_NALU_SVC_SLICE) ? 0x20000000 : 0;
				avc_svc_subs_priority = 0;
			}

			if (is_slice && ctx->avc_state->s_info.field_pic_flag) {
				ctx->is_paff = GF_TRUE;
				bottom_field_flag = ctx->avc_state->s_info.bottom_field_flag;
			}

			slice_is_idr = (ctx->avc_state->s_info.nal_unit_type==GF_AVC_NALU_IDR_SLICE) ? GF_TRUE : GF_FALSE;

			recovery_point_valid = ctx->avc_state->sei.recovery_point.valid;
			recovery_point_frame_cnt = ctx->avc_state->sei.recovery_point.frame_cnt;
			bIntraSlice = gf_avc_slice_is_intra(ctx->avc_state);

			au_sap_type = GF_FILTER_SAP_NONE;
			if (ctx->avc_state->s_info.nal_unit_type == GF_AVC_NALU_IDR_SLICE)
				au_sap_type = GF_FILTER_SAP_1;

			slice_poc = ctx->avc_state->s_info.poc;
			/*need to store TS offsets*/
			switch (ctx->avc_state->s_info.slice_type) {
			case GF_AVC_TYPE_B:
			case GF_AVC_TYPE2_B:
				slice_is_b = GF_TRUE;
				break;
			}
		}

		if (is_slice) {
			Bool first_in_au = ctx->first_slice_in_au;

			if (slice_is_idr)
				ctx->nb_idr++;

			if (au_sap_type==GF_FILTER_SAP_3)
				ctx->nb_cra++;

			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 (ctx->first_slice_in_au) {
				ctx->first_slice_in_au = GF_FALSE;
				if (recovery_point_valid) {
					ctx->sei_recovery_frame_count = 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 (!recovery_point_frame_cnt && bIntraSlice) {
						ctx->has_islice = 1;
						if (ctx->use_opengop_gdr == 1) {
							ctx->use_opengop_gdr = 2; /*avoid message flooding*/
							GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] No valid SEI Recovery Point found although needed - forcing\n", ctx->log_name));
						}
					}
					if (ctx->codecid==GF_CODECID_HEVC) {
						ctx->hevc_state->sei.recovery_point.valid = 0;
					} else if (ctx->codecid==GF_CODECID_VVC) {
						ctx->vvc_state->s_info.recovery_point_valid = 0;
					} else {
						ctx->avc_state->sei.recovery_point.valid = 0;
					}
					if (bIntraSlice && ctx->force_sync && (ctx->sei_recovery_frame_count==0))
						slice_force_ref = GF_TRUE;
				}
				ctx->au_sap = au_sap_type;
				ctx->bottom_field_flag = bottom_field_flag;
			}

			if (slice_poc < ctx->poc_shift) {

				u32 i, count = gf_list_count(ctx->pck_queue);
				for (i=0; i<count; i++) {
					u64 dts, cts;
					GF_FilterPacket *q_pck = gf_list_get(ctx->pck_queue, i);
					assert(q_pck);
					dts = gf_filter_pck_get_dts(q_pck);
					if (dts == GF_FILTER_NO_TS) continue;
					cts = gf_filter_pck_get_cts(q_pck);
					cts += ctx->poc_shift;
					cts -= slice_poc;
					gf_filter_pck_set_cts(q_pck, cts);
				}

				ctx->poc_shift = slice_poc;
			}

			/*if #pics, compute smallest POC increase*/
			if (slice_poc != ctx->last_poc) {
				s32 pdiff = ABS(ctx->last_poc - slice_poc);

				if ((slice_poc < 0) && !ctx->last_poc)
					ctx->poc_diff = 0;
				else if ((slice_poc < 0) && (-slice_poc < ctx->poc_diff)) {
					pdiff = -slice_poc;
					ctx->poc_diff = 0;
				}

				if (!ctx->poc_diff || (ctx->poc_diff > (s32) pdiff ) ) {
					ctx->poc_diff = pdiff;
					ctx->poc_probe_done = GF_FALSE;
				} else if (first_in_au) {
					//second frame with the same poc diff, we should be able to properly recompute CTSs
					ctx->poc_probe_done = GF_TRUE;
				}
				ctx->last_poc = slice_poc;
			}
			GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[%s] POC is %d - min poc diff %d - slice is IDR %d (SAP %d)\n", ctx->log_name, slice_poc, ctx->poc_diff, slice_is_idr, au_sap_type));

			/*ref slice, reset poc*/
			if (slice_is_idr) {
				if (first_in_au) {
					Bool temp_poc_diff = GF_FALSE;
					//two consecutive IDRs, force poc_diff to 1 if 0 (when we have intra-only) to force frame dispatch
					if (ctx->last_frame_is_idr && !ctx->poc_diff) {
						temp_poc_diff = GF_TRUE;
						ctx->poc_diff = 1;
					}
					//new ref frame, dispatch all pending packets
					naludmx_enqueue_or_dispatch(ctx, NULL, GF_TRUE);

					//if IDR with DLP (sap2), only reset poc probing if the poc is below current max poc
					//otherwise assume no diff in poc
					if ((au_sap_type == GF_FILTER_SAP_2) && (ctx->max_last_poc >= ctx->last_poc) ){
						ctx->au_sap2_poc_reset = GF_TRUE;
					}

					if ((au_sap_type == GF_FILTER_SAP_1) || ctx->au_sap2_poc_reset) {
						if (!ctx->au_sap2_poc_reset)
							ctx->last_poc = 0;

						ctx->max_last_poc = ctx->last_poc;
						ctx->max_last_b_poc = ctx->last_poc;
						ctx->poc_shift = 0;
						//force probing of POC diff, this will prevent dispatching frames with wrong CTS until we have a clue of min poc_diff used
						ctx->poc_probe_done = 0;
					}
					ctx->last_frame_is_idr = GF_TRUE;
					if (temp_poc_diff)
						ctx->poc_diff = 0;
				}
			}
			/*forced ref slice*/
			else if (slice_force_ref) {
				ctx->last_frame_is_idr = GF_FALSE;
				if (first_in_au) {
					//new ref frame, dispatch all pending packets
					naludmx_enqueue_or_dispatch(ctx, NULL, GF_TRUE);

					/*adjust POC shift as sample will now be marked as sync, so we must store poc as if IDR (eg POC=0) for our CTS offset computing to be correct*/
					ctx->poc_shift = slice_poc;

					//force probing of POC diff, this will prevent dispatching frames with wrong CTS until we have a clue of min poc_diff used
					ctx->poc_probe_done = 0;
				}
			}
			/*strictly less - this is a new P slice*/
			else if (ctx->max_last_poc < ctx->last_poc) {
				ctx->max_last_b_poc = 0;
				ctx->max_last_poc = ctx->last_poc;
				ctx->last_frame_is_idr = GF_FALSE;
			}
			/*stricly greater*/
			else if (slice_is_b && (ctx->max_last_poc > ctx->last_poc)) {
				ctx->last_frame_is_idr = GF_FALSE;
				if (!ctx->max_last_b_poc) {
					ctx->max_last_b_poc = ctx->last_poc;
				}
				/*if same poc than last max, this is a B-slice*/
				else if (ctx->last_poc > ctx->max_last_b_poc) {
					ctx->max_last_b_poc = ctx->last_poc;
				}
				/*otherwise we had a B-slice reference: do nothing*/
			} else {
				ctx->last_frame_is_idr = GF_FALSE;
			}

			if (ctx->deps) {
				if (nal_ref_idc) {
					ctx->has_ref_slices = GF_TRUE;
				}
				if ((ctx->codecid==GF_CODECID_AVC) && (ctx->avc_state->s_info.redundant_pic_cnt) ) {
					ctx->has_redundant = GF_TRUE;
				}
			}
		}

		au_start = ctx->first_pck_in_au ? GF_FALSE : GF_TRUE;

		if (ctx->has_initial_aud) {
			u32 audelim_size = (ctx->codecid!=GF_CODECID_AVC) ? 3 : 2;
			/*dst_pck = */naludmx_start_nalu(ctx, audelim_size, GF_FALSE, &au_start, &pck_data);
			memcpy(pck_data + ctx->nal_length , ctx->init_aud, audelim_size);
			ctx->has_initial_aud = GF_FALSE;
			if (ctx->subsamples) {
				naludmx_add_subsample(ctx, audelim_size, avc_svc_subs_priority, avc_svc_subs_reserved);
			}
		}
		if (ctx->sei_buffer_size) {
			//sei buffer is already nal size prefixed
			/*dst_pck = */naludmx_start_nalu(ctx, ctx->sei_buffer_size, GF_TRUE, &au_start, &pck_data);
			memcpy(pck_data, ctx->sei_buffer, ctx->sei_buffer_size);
			if (ctx->subsamples) {
				naludmx_add_subsample(ctx, ctx->sei_buffer_size - ctx->nal_length, avc_svc_subs_priority, avc_svc_subs_reserved);
			}
			ctx->sei_buffer_size = 0;
		}

		if (ctx->svc_prefix_buffer_size) {
			/*dst_pck = */naludmx_start_nalu(ctx, ctx->svc_prefix_buffer_size, GF_FALSE, &au_start, &pck_data);
			memcpy(pck_data + ctx->nal_length, ctx->svc_prefix_buffer, ctx->svc_prefix_buffer_size);
			if (ctx->subsamples) {
				naludmx_add_subsample(ctx, ctx->svc_prefix_buffer_size, ctx->svc_nalu_prefix_priority, ctx->svc_nalu_prefix_reserved);
			}
			ctx->svc_prefix_buffer_size = 0;
		}

		//nalu size field
		/*dst_pck = */naludmx_start_nalu(ctx, (u32) nal_size, GF_FALSE, &au_start, &pck_data);
		pck_data += ctx->nal_length;

		//add subsample info before touching the size
		if (ctx->subsamples) {
			naludmx_add_subsample(ctx, (u32) nal_size, avc_svc_subs_priority, avc_svc_subs_reserved);
		}

		//bytes only come from the data packet
		memcpy(pck_data, nal_data, (size_t) nal_size);

		nal_size += sc_size;
		start += nal_size;
		remain -= nal_size;
		naldmx_check_timestamp_switch(ctx, &nalu_store_before, nal_size, &drop_packet, pck);

		//don't demux too much of input, abort when we would block. This avoid dispatching
		//a huge number of frames in a single call
		if (remain && gf_filter_pid_would_block(ctx->opid)) {
			ctx->resume_from = (u32) (start - ctx->nal_store);
			assert(ctx->resume_from <= ctx->nal_store_size);
			assert(ctx->resume_from == ctx->nal_store_size - remain);
			if (drop_packet)
				gf_filter_pid_drop_packet(ctx->ipid);
			return GF_OK;
		}
	}

	if (remain) {
		if (is_eos && (remain == ctx->nal_store_size)) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] Incomplete last NAL and eos, discarding\n", ctx->log_name));
			remain = 0;
		} else {
			assert((u32) remain<=ctx->nal_store_size);
			memmove(ctx->nal_store, start, remain);
		}
	}
	ctx->nal_store_size = remain;

	if (drop_packet)
		gf_filter_pid_drop_packet(ctx->ipid);

	if (is_eos)
		return naludmx_process(filter);

	if ((ctx->nb_nalus>nalu_before) && gf_filter_reporting_enabled(filter)) {
		char szStatus[1024];

		sprintf(szStatus, "%s %dx%d % 10d NALU % 8d I % 8d P % 8d B % 8d SEI", ctx->log_name, ctx->width, ctx->height, ctx->nb_nalus, ctx->nb_i, ctx->nb_p, ctx->nb_b, ctx->nb_sei);
		gf_filter_update_status(filter, -1, szStatus);
	}
	if (ctx->full_au_source && ctx->poc_probe_done) {
		if (ctx->first_pck_in_au)
			naludmx_finalize_au_flags(ctx);

		naludmx_enqueue_or_dispatch(ctx, NULL, GF_TRUE);
	}
	return GF_OK;
}

fixed #2223


static void naludmx_check_dur(GF_Filter *filter, GF_NALUDmxCtx *ctx)
{
	FILE *stream;
	GF_BitStream *bs;
	u64 duration, cur_dur, nal_start, filesize;
	u32 probe_size=0, start_code_size;
	AVCState *avc_state = NULL;
	HEVCState *hevc_state = NULL;
	VVCState *vvc_state = NULL;
	Bool first_slice_in_pic = GF_TRUE;
	const GF_PropertyValue *p;
	const char *filepath = NULL;
	if (!ctx->opid || ctx->timescale || ctx->file_loaded) return;

	p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILEPATH);
	if (!p || !p->value.string || !strncmp(p->value.string, "gmem://", 7)) {
		ctx->is_file = GF_FALSE;
		ctx->file_loaded = GF_TRUE;
		return;
	}
	filepath = p->value.string;
	ctx->is_file = GF_TRUE;

	if (ctx->index<0) {
		if (gf_opts_get_bool("temp", "force_indexing")) {
			ctx->index = 1.0;
		} else {
			p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_DOWN_SIZE);
			if (!p || (p->value.longuint > 20000000)) {
				GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[%s] Source file larger than 20M, skipping indexing\n", ctx->log_name));
				if (!gf_sys_is_test_mode())
					probe_size = 20000000;
			} else {
				ctx->index = -ctx->index;
			}
		}
	}
	if ((ctx->index<=0) && !probe_size) {
		ctx->duration.num = 1;
		ctx->file_loaded = GF_TRUE;
		return;
	}

	if (ctx->codecid==GF_CODECID_HEVC) {
		GF_SAFEALLOC(hevc_state, HEVCState);
		if (!hevc_state) return;
	} else if (ctx->codecid==GF_CODECID_VVC) {
		GF_SAFEALLOC(vvc_state, VVCState);
		if (!vvc_state) return;
	} else {
		GF_SAFEALLOC(avc_state, AVCState);
		if (!avc_state) return;
	}

	stream = gf_fopen_ex(filepath, NULL, "rb", GF_TRUE);
	if (!stream) {
		if (hevc_state) gf_free(hevc_state);
		if (vvc_state) gf_free(vvc_state);
		if (avc_state) gf_free(avc_state);
		if (gf_fileio_is_main_thread(filepath)) {
			ctx->duration.num = 1;
			ctx->file_loaded = GF_TRUE;
		}
		return;
	}
	ctx->index_size = 0;
	duration = 0;
	cur_dur = 0;

	bs = gf_bs_from_file(stream, GF_BITSTREAM_READ);
	gf_bs_enable_emulation_byte_removal(bs, GF_TRUE);
	filesize = gf_bs_available(bs);

	nal_start = naludmx_next_start_code(bs, 0, filesize, &start_code_size);
	if (!nal_start) {
		if (hevc_state) gf_free(hevc_state);
		if (avc_state) gf_free(avc_state);
		gf_bs_del(bs);
		gf_fclose(stream);
		ctx->duration.num = 1;
		ctx->file_loaded = GF_TRUE;
		return;
	}

	while (1) {
		s32 res;
		u32 gdr_frame_count = 0;
		Bool is_rap = GF_FALSE;
		Bool is_slice = GF_FALSE;

		//parse directly from current pos (next byte is first byte of nal hdr)
		if (hevc_state) {
#ifndef GPAC_DISABLE_HEVC
			u8 temporal_id, layer_id, nal_type;

			res = gf_hevc_parse_nalu_bs(bs, hevc_state, &nal_type, &temporal_id, &layer_id);
			if (res>0) first_slice_in_pic = GF_TRUE;
			switch (nal_type) {
			case GF_HEVC_NALU_SLICE_IDR_N_LP:
			case GF_HEVC_NALU_SLICE_IDR_W_DLP:
			case GF_HEVC_NALU_SLICE_CRA:
			case GF_HEVC_NALU_SLICE_BLA_N_LP:
			case GF_HEVC_NALU_SLICE_BLA_W_LP:
			case GF_HEVC_NALU_SLICE_BLA_W_DLP:
				is_rap = GF_TRUE;
				is_slice = GF_TRUE;
				break;
			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:
				is_slice = GF_TRUE;
				break;
			}
			//also mark first slice in gdr as valid seek point
			if (is_slice && hevc_state->sei.recovery_point.valid) {
				is_rap = GF_TRUE;
				hevc_state->sei.recovery_point.valid = GF_FALSE;
				gdr_frame_count = hevc_state->sei.recovery_point.frame_cnt;
			}
#endif // GPAC_DISABLE_HEVC
		} else if (vvc_state) {

			u8 temporal_id, layer_id, nal_type;

			res = gf_vvc_parse_nalu_bs(bs, vvc_state, &nal_type, &temporal_id, &layer_id);
			if (res>0) first_slice_in_pic = GF_TRUE;
			switch (nal_type) {
			case GF_VVC_NALU_SLICE_TRAIL:
			case GF_VVC_NALU_SLICE_STSA:
			case GF_VVC_NALU_SLICE_RADL:
			case GF_VVC_NALU_SLICE_RASL:
				is_slice = GF_TRUE;
				break;
			case GF_VVC_NALU_SLICE_IDR_W_RADL:
			case GF_VVC_NALU_SLICE_IDR_N_LP:
			case GF_VVC_NALU_SLICE_CRA:
			case GF_VVC_NALU_SLICE_GDR:
				is_rap = GF_TRUE;
				is_slice = GF_TRUE;
				if (vvc_state->s_info.gdr_pic)
					gdr_frame_count = vvc_state->s_info.gdr_recovery_count;
				break;
			}
		} else {
			u32 nal_type;
			res = gf_avc_parse_nalu(bs, avc_state);
			if (res>0) first_slice_in_pic = GF_TRUE;

			nal_type = avc_state->last_nal_type_parsed;
			switch (nal_type) {
			case GF_AVC_NALU_IDR_SLICE:
				is_rap = GF_TRUE;
				is_slice = GF_TRUE;
				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:
				is_slice = GF_TRUE;
				break;
			case GF_AVC_NALU_SEI:
				naludmx_probe_recovery_sei(bs, avc_state);
				break;

			}
			//also mark open GOP or first slice in gdr as valid seek point
			if (is_slice && avc_state->sei.recovery_point.valid) {
				is_rap = GF_TRUE;
				avc_state->sei.recovery_point.valid = GF_FALSE;
				gdr_frame_count = avc_state->sei.recovery_point.frame_cnt;
			}
		}

		if (probe_size && (nal_start>probe_size) && is_rap) {
			break;
		}

		if (!probe_size && is_rap && first_slice_in_pic && (cur_dur >= ctx->index * ctx->cur_fps.num) ) {
			if (!ctx->index_alloc_size) ctx->index_alloc_size = 10;
			else if (ctx->index_alloc_size == ctx->index_size) ctx->index_alloc_size *= 2;
			ctx->indexes = gf_realloc(ctx->indexes, sizeof(NALUIdx)*ctx->index_alloc_size);
			ctx->indexes[ctx->index_size].pos = nal_start - start_code_size;
			ctx->indexes[ctx->index_size].duration = (Double) duration;
			ctx->indexes[ctx->index_size].duration /= ctx->cur_fps.num;
			ctx->indexes[ctx->index_size].roll_count = gdr_frame_count;
			ctx->index_size ++;
			cur_dur = 0;
		}

		if (is_slice && first_slice_in_pic) {
			duration += ctx->cur_fps.den;
			cur_dur += ctx->cur_fps.den;
			first_slice_in_pic = GF_FALSE;
		}

		//align since some NAL parsing may stop anywhere
		gf_bs_align(bs);
		nal_start = naludmx_next_start_code(bs, gf_bs_get_position(bs), filesize, &start_code_size);
		if (!nal_start)
			break;
	}
	if (probe_size)
		probe_size = (u32) gf_bs_get_position(bs);

	gf_bs_del(bs);
	gf_fclose(stream);
	if (hevc_state) gf_free(hevc_state);
	if (vvc_state) gf_free(vvc_state);
	if (avc_state) gf_free(avc_state);

	if (!ctx->duration.num || (ctx->duration.num  * ctx->cur_fps.num != duration * ctx->duration.den)) {
		if (probe_size) {
			duration *= filesize/probe_size;
		}
		ctx->duration.num = (s32) duration;
		if (probe_size) ctx->duration.num = -ctx->duration.num;
		ctx->duration.den = ctx->cur_fps.num;

		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));

		if (duration && (!gf_sys_is_test_mode() || gf_opts_get_bool("temp", "force_indexing"))) {
			filesize *= 8 * ctx->duration.den;
			filesize /= ctx->duration.num;
			ctx->bitrate = (u32) filesize;
		}
	}

	p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILE_CACHED);
	if (p && p->value.boolean) ctx->file_loaded = GF_TRUE;
}

fixed #2223


static GF_Err naludmx_initialize(GF_Filter *filter)
{
	GF_NALUDmxCtx *ctx = gf_filter_get_udta(filter);
	ctx->sps = gf_list_new();
	ctx->pps = gf_list_new();
	switch (ctx->nal_length) {
	case 1:
		ctx->max_nalu_size_allowed = 0xFF;
		break;
	case 2:
		ctx->max_nalu_size_allowed = 0xFFFF;
		break;
	case 4:
		ctx->max_nalu_size_allowed = 0xFFFFFFFF;
		break;
	case 0:
		ctx->max_nalu_size_allowed = 0xFFFFFFFF;
		ctx->nal_length = 4;
		ctx->nal_adjusted = GF_TRUE;
		break;
	default:
		GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] NAL size length %d is not allowed, defaulting to 4 bytes\n", ctx->log_name));
		ctx->max_nalu_size_allowed = 0xFFFFFFFF;
		ctx->nal_length = 4;
		break;
	}
	return GF_OK;
}

fixed #2223


static void naludmx_push_prefix(GF_NALUDmxCtx *ctx, u8 *data, u32 size, Bool avc_sei_rewrite)
{
	if (ctx->sei_buffer_alloc < ctx->sei_buffer_size + size + ctx->nal_length) {
		ctx->sei_buffer_alloc = ctx->sei_buffer_size + size + ctx->nal_length;
		ctx->sei_buffer = gf_realloc(ctx->sei_buffer, ctx->sei_buffer_alloc);
	}

	if (!ctx->bs_w) ctx->bs_w = gf_bs_new(ctx->sei_buffer + ctx->sei_buffer_size, ctx->nal_length + size, GF_BITSTREAM_WRITE);
	else gf_bs_reassign_buffer(ctx->bs_w, ctx->sei_buffer + ctx->sei_buffer_size, ctx->nal_length + size);
	gf_bs_write_int(ctx->bs_w, size, 8*ctx->nal_length);
	memcpy(ctx->sei_buffer + ctx->sei_buffer_size + ctx->nal_length, data, size);

	if (avc_sei_rewrite) {
		u32 rw_sei_size = gf_avc_reformat_sei(ctx->sei_buffer + ctx->sei_buffer_size + ctx->nal_length, size, ctx->seirw, ctx->avc_state);
		if (rw_sei_size < size) {
			gf_bs_seek(ctx->bs_w, 0);
			gf_bs_write_int(ctx->bs_w, rw_sei_size, 8*ctx->nal_length);
			size = rw_sei_size;
		}
	}
	ctx->sei_buffer_size += size + ctx->nal_length;
}

fixed #2223


static void naludmx_reset_param_sets(GF_NALUDmxCtx *ctx, Bool do_free)
{
	naludmx_del_param_list(ctx->sps, do_free);
	naludmx_del_param_list(ctx->pps, do_free);
	naludmx_del_param_list(ctx->vps, do_free);
	naludmx_del_param_list(ctx->sps_ext, do_free);
	naludmx_del_param_list(ctx->pps_svc, do_free);
	naludmx_del_param_list(ctx->vvc_aps_pre, do_free);
	naludmx_del_param_list(ctx->vvc_dci, do_free);

}

fixed #2223


static void naludmx_finalize_au_flags(GF_NALUDmxCtx *ctx)
{
	u64 ts;
	Bool is_rap = GF_FALSE;

	if (!ctx->first_pck_in_au)
		return;
	if (ctx->au_sap) {
		gf_filter_pck_set_sap(ctx->first_pck_in_au, ctx->au_sap);
		if ((ctx->au_sap == GF_FILTER_SAP_1) || ctx->au_sap2_poc_reset) {
			ctx->dts_last_IDR = gf_filter_pck_get_dts(ctx->first_pck_in_au);
			if (ctx->is_paff)
				ctx->dts_last_IDR *= 2;
		}
		if (ctx->au_sap <= GF_FILTER_SAP_3) {
			is_rap = GF_TRUE;
		}
	}
	else if (ctx->has_islice && ctx->force_sync && (ctx->sei_recovery_frame_count==0)) {
		gf_filter_pck_set_sap(ctx->first_pck_in_au, GF_FILTER_SAP_1);
		if (!ctx->use_opengop_gdr) {
			ctx->use_opengop_gdr = 1;
			GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] Forcing non-IDR samples with I slices to be marked as sync points - resulting file will not be ISOBMFF compliant\n", ctx->log_name));
		}
		is_rap = GF_TRUE;
	}
	/*set roll info sampleGroups info*/
	else if (!ctx->au_sap && ( (ctx->sei_recovery_frame_count >= 0) || ctx->has_islice) ) {
		/*generic GDR*/
		if (ctx->sei_recovery_frame_count > 0) {
			if (!ctx->use_opengop_gdr) ctx->use_opengop_gdr = 1;
			gf_filter_pck_set_sap(ctx->first_pck_in_au, GF_FILTER_SAP_4);
			gf_filter_pck_set_roll_info(ctx->first_pck_in_au, ctx->sei_recovery_frame_count);
		}
		/*open-GOP*/
		else if ((ctx->sei_recovery_frame_count == 0) && ctx->has_islice) {
			if (!ctx->use_opengop_gdr) ctx->use_opengop_gdr = 2;
			gf_filter_pck_set_sap(ctx->first_pck_in_au, GF_FILTER_SAP_3);
			is_rap = GF_TRUE;
		}
	}
	if (ctx->is_paff) {
		gf_filter_pck_set_interlaced(ctx->first_pck_in_au, ctx->bottom_field_flag ? 2 : 1);
	}

	//if TS is set, the packet was the first in AU in the input timed packet (eg PES), we reuse the input timing
	ts = gf_filter_pck_get_cts(ctx->first_pck_in_au);
	if (ts == GF_FILTER_NO_TS) {
		/*we store the POC (last POC minus the poc shift) as the CTS offset and re-update the CTS when dispatching*/
		assert(ctx->last_poc >= ctx->poc_shift);
		gf_filter_pck_set_cts(ctx->first_pck_in_au, CTS_POC_OFFSET_SAFETY + ctx->last_poc - ctx->poc_shift);
		//we use the carousel flag temporarly to indicate the cts must be recomputed
		gf_filter_pck_set_carousel_version(ctx->first_pck_in_au, 1);
	}

	if (ctx->subsamp_buffer_size) {
		gf_filter_pck_set_property(ctx->first_pck_in_au, GF_PROP_PCK_SUBS, &PROP_DATA(ctx->subsamp_buffer, ctx->subsamp_buffer_size) );
		ctx->subsamp_buffer_size = 0;
		ctx->subs_mapped_bytes = 0;
	}
	if (ctx->deps) {
		u8 flags = 0;
		//dependsOn
		flags = (is_rap) ? 2 : 1;
		flags <<= 2;
		//dependedOn
	 	flags |= ctx->has_ref_slices ? 1 : 2;
		flags <<= 2;
		//hasRedundant
	 	flags |= ctx->has_redundant ? 1 : 2;
	 	gf_filter_pck_set_dependency_flags(ctx->first_pck_in_au, flags);
	}
	ctx->has_ref_slices = GF_FALSE;
	ctx->has_redundant = GF_FALSE;

	if ((ctx->hevc_state && ctx->hevc_state->clli_valid)
		|| (ctx->vvc_state && ctx->vvc_state->clli_valid)
	) {
		u8 *clli = ctx->hevc_state ? ctx->hevc_state->clli_data : ctx->vvc_state->clli_data;
		u32 crc = gf_crc_32(clli, 4);
		if (!ctx->clli_crc) {
			naludmx_update_clli_mdcv(ctx, GF_FALSE);
		}

		if (crc != ctx->clli_crc) {
			gf_filter_pck_set_property(ctx->first_pck_in_au, GF_PROP_PID_CONTENT_LIGHT_LEVEL, &PROP_DATA(clli, 4));
		}
	}
	if ((ctx->hevc_state && ctx->hevc_state->mdcv_valid)
		|| (ctx->vvc_state && ctx->vvc_state->mdcv_valid)
	) {
		u8 *mdcv = ctx->hevc_state ? ctx->hevc_state->mdcv_data : ctx->vvc_state->mdcv_data;
		u32 crc = gf_crc_32(mdcv, 24);
		if (!ctx->mdcv_crc) {
			naludmx_update_clli_mdcv(ctx, GF_FALSE);
		}
		if (crc != ctx->mdcv_crc) {
			gf_filter_pck_set_property(ctx->first_pck_in_au, GF_PROP_PID_MASTER_DISPLAY_COLOUR, &PROP_DATA(mdcv, 24));
		}
	}
	if (ctx->hevc_state)
		ctx->hevc_state->clli_valid = ctx->hevc_state->mdcv_valid = 0;
	if (ctx->vvc_state)
		ctx->vvc_state->clli_valid = ctx->vvc_state->mdcv_valid = 0;

	//if we reuse input packets timing, we can dispatch asap.
	//otherwise if poc probe is done (we know the min_poc_diff between images) and we are not in strict mode, dispatch asap
	//otherwise we will need to wait for the next ref frame to make sure we know all pocs ...
	if (!ctx->notime || (!ctx->strict_poc && ctx->poc_probe_done) )
		naludmx_enqueue_or_dispatch(ctx, NULL, GF_TRUE);

	ctx->first_pck_in_au = NULL;
}

fixed #2223


static void naludmx_enqueue_or_dispatch(GF_NALUDmxCtx *ctx, GF_FilterPacket *n_pck, Bool flush_ref)
{
	//TODO: we are dispatching frames in "negctts mode", ie we may have DTS>CTS
	//need to signal this for consumers using DTS (eg MPEG-2 TS)
	if (flush_ref && ctx->pck_queue && ctx->poc_diff) {
		u32 dts_inc=0;
		s32 last_poc = 0;
		Bool patch_missing_frame = GF_FALSE;
		//send all reference packet queued
		if (ctx->strict_poc==STRICT_POC_ERROR) {
			u32 i;
			u32 nb_bframes = 0;
			for (i=0; i<gf_list_count(ctx->pck_queue); i++) {
				s32 poc;
				u64 poc_ts, dts;
				GF_FilterPacket *q_pck = gf_list_get(ctx->pck_queue, i);

				if (q_pck == ctx->first_pck_in_au) break;

				dts = gf_filter_pck_get_dts(q_pck);
				if (dts == GF_FILTER_NO_TS) continue;
				poc_ts = gf_filter_pck_get_cts(q_pck);
				assert(poc_ts != GF_FILTER_NO_TS);
				poc = (s32) ((s64) poc_ts - CTS_POC_OFFSET_SAFETY);

				if (i) {
					if (last_poc>poc) nb_bframes ++;
					else if (last_poc + ctx->poc_diff<poc)
						patch_missing_frame = GF_TRUE;
				}
				last_poc = poc;
			}
			if (nb_bframes>1)
				patch_missing_frame = GF_FALSE;
			else if (nb_bframes)
				patch_missing_frame = GF_TRUE;
		}
		last_poc = GF_INT_MIN;

		while (gf_list_count(ctx->pck_queue) ) {
			u64 dts;
			GF_FilterPacket *q_pck = gf_list_get(ctx->pck_queue, 0);

			if (q_pck == ctx->first_pck_in_au) break;

			dts = gf_filter_pck_get_dts(q_pck);
			if (dts != GF_FILTER_NO_TS) {
				s32 poc;
				u64 poc_ts, cts;
				u8 carousel_info = gf_filter_pck_get_carousel_version(q_pck);

				//we reused timing from source packets
				if (!carousel_info) {
					assert(ctx->timescale);
					gf_list_rem(ctx->pck_queue, 0);
					gf_filter_pck_send(q_pck);
					continue;
				}
				gf_filter_pck_set_carousel_version(q_pck, 0);

				poc_ts = gf_filter_pck_get_cts(q_pck);
				assert(poc_ts != GF_FILTER_NO_TS);
				poc = (s32) ((s64) poc_ts - CTS_POC_OFFSET_SAFETY);

				if (patch_missing_frame) {
					if (last_poc!=GF_INT_MIN) {
						//check if we missed an IDR (poc reset)
						if (poc && (last_poc > poc) ) {
							last_poc = 0;
							dts_inc += ctx->cur_fps.den;
							ctx->dts_last_IDR = dts;
							ctx->dts += ctx->cur_fps.den;
						}
						//check if we miss a frame
						while (last_poc + ctx->poc_diff < poc) {
							last_poc += ctx->poc_diff;
							dts_inc += ctx->cur_fps.den;
							ctx->dts += ctx->cur_fps.den;
						}
					}
					last_poc = poc;
					dts += dts_inc;
				}
				//poc is stored as diff since last IDR which has min_poc
				cts = ( (ctx->min_poc + (s32) poc) * ctx->cur_fps.den ) / ctx->poc_diff + ctx->dts_last_IDR;

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

				gf_filter_pck_set_cts(q_pck, cts);
				GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[%s] Frame timestamps computed dts "LLU" cts "LLU" (poc %d min poc %d poc_diff %d last IDR DTS "LLU")\n", ctx->log_name, dts, cts, poc, ctx->min_poc, ctx->poc_diff, ctx->dts_last_IDR));

				if (ctx->importer && ctx->cur_fps.den) {
					poc = (s32) ( (s64) cts - (s64) dts);
					if (poc<0) poc = -poc;
					poc /= ctx->cur_fps.den;
					if (poc > ctx->max_total_delay)
						ctx->max_total_delay = poc;
				}
			}
			gf_list_rem(ctx->pck_queue, 0);
			gf_filter_pck_send(q_pck);
		}
	}
	if (!n_pck) return;

	if (!ctx->pck_queue) ctx->pck_queue = gf_list_new();
	gf_list_add(ctx->pck_queue, n_pck);
}

fixed #2223


static void naludmx_check_pid(GF_Filter *filter, GF_NALUDmxCtx *ctx, Bool force_au_flush)
{
	u32 w, h, ew, eh;
	u8 *dsi, *dsi_enh;
	u32 dsi_size, dsi_enh_size;
	u32 crc_cfg, crc_cfg_enh;
	GF_Fraction sar;
	Bool has_hevc_base = GF_TRUE;
	Bool has_colr_info = GF_FALSE;

	if (ctx->analyze) {
		if (ctx->opid && !ctx->ps_modified) return;
	} else {
		if (!ctx->ps_modified) return;
		if (ctx->opid && (!gf_list_count(ctx->sps) || !gf_list_count(ctx->pps)))
			return;
	}
	ctx->ps_modified = GF_FALSE;

	dsi = dsi_enh = NULL;

	if (ctx->notime) {
		ctx->cur_fps = ctx->fps;
		if (!ctx->cur_fps.num || !ctx->cur_fps.den) {
			ctx->cur_fps.num = 25000;
			ctx->cur_fps.den = 1000;
		}
	}

	if (ctx->codecid==GF_CODECID_HEVC) {
		naludmx_create_hevc_decoder_config(ctx, &dsi, &dsi_size, &dsi_enh, &dsi_enh_size, &w, &h, &ew, &eh, &sar, &has_hevc_base);
	} else if (ctx->codecid==GF_CODECID_VVC) {
		naludmx_create_vvc_decoder_config(ctx, &dsi, &dsi_size, &dsi_enh, &dsi_enh_size, &w, &h, &ew, &eh, &sar, &has_hevc_base);
	} else {
		naludmx_create_avc_decoder_config(ctx, &dsi, &dsi_size, &dsi_enh, &dsi_enh_size, &w, &h, &ew, &eh, &sar);
	}
	crc_cfg = crc_cfg_enh = 0;
	if (dsi) crc_cfg = gf_crc_32(dsi, dsi_size);
	if (dsi_enh) crc_cfg_enh = gf_crc_32(dsi_enh, dsi_enh_size);

	if (!ctx->analyze && (!w || !h)) {
		if (dsi) gf_free(dsi);
		if (dsi_enh) gf_free(dsi_enh);
		return;
	}

	if (!ctx->opid) {
		ctx->opid = gf_filter_pid_new(filter);

		naludmx_check_dur(filter, ctx);
		ctx->first_slice_in_au = GF_TRUE;
	}

	if ((ctx->crc_cfg == crc_cfg) && (ctx->crc_cfg_enh == crc_cfg_enh)
		&& (ctx->width==w) && (ctx->height==h)
		&& (ctx->sar.num * sar.den == ctx->sar.den * sar.num)
	) {
		if (dsi) gf_free(dsi);
		if (dsi_enh) gf_free(dsi_enh);
		return;
	}

	if (force_au_flush) {
		naludmx_end_access_unit(ctx);
	}

	naludmx_enqueue_or_dispatch(ctx, NULL, GF_TRUE);
	if (!ctx->analyze && (gf_list_count(ctx->pck_queue)>1))  {
		GF_LOG(dsi_enh ? GF_LOG_DEBUG : GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] xPS changed but could not flush frames before signaling state change %s\n", ctx->log_name, dsi_enh ? "- likely scalable xPS update" : "!"));
	}

	//copy properties at init or reconfig
	gf_filter_pid_copy_properties(ctx->opid, ctx->ipid);

	//don't change codec type if reframing an ES (for HLS SAES)
	if (!ctx->timescale)
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, & PROP_UINT(GF_STREAM_VISUAL));
	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL);
	if (!gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_ID))
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_ID, &PROP_UINT(1));

	ctx->width = w;
	ctx->height = h;
	ctx->sar = sar;
	ctx->crc_cfg = crc_cfg;
	ctx->crc_cfg_enh = crc_cfg_enh;

	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, & PROP_UINT( ctx->width));
	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, & PROP_UINT( ctx->height));
	if (ew && eh) {
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH_MAX, & PROP_UINT( ew ));
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT_MAX, & PROP_UINT( eh ));
	}
	if (ctx->sar.den)
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAR, & PROP_FRAC(ctx->sar));
	else
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAR, NULL);

	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FPS, & PROP_FRAC(ctx->cur_fps));
	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, & PROP_UINT(ctx->timescale ? ctx->timescale : ctx->cur_fps.num));

	if (ctx->explicit || !has_hevc_base) {
		u32 enh_cid = GF_CODECID_SVC;
		if (ctx->codecid==GF_CODECID_HEVC) enh_cid = GF_CODECID_LHVC;
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT(enh_cid));
		if (dsi) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, &PROP_DATA_NO_COPY(dsi, dsi_size) );
	} else {
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, & PROP_UINT(ctx->codecid));
		if (dsi) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, &PROP_DATA_NO_COPY(dsi, dsi_size) );
		if (dsi_enh) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT, &PROP_DATA_NO_COPY(dsi_enh, dsi_enh_size) );
	}

	if (ctx->bitrate) {
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BITRATE, & PROP_UINT(ctx->bitrate));
	}

	if ((ctx->codecid==GF_CODECID_HEVC) && gf_list_count(ctx->vps) ) {
		GF_Err e = naludmx_set_hevc_oinf(ctx, NULL);
		if (e) {
			GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] Failed to create OINF chunk\n", ctx->log_name));
		}
		naludmx_set_hevc_linf(ctx);
	}
	if (ctx->duration.num)
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration));

	if (ctx->is_file /* && ctx->index*/) {
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_PLAYBACK_MODE, & PROP_UINT(GF_PLAYBACK_MODE_FASTFORWARD) );
	}
	//set interlaced or remove interlaced property
	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_INTERLACED, ctx->interlaced ? & PROP_UINT(GF_TRUE) : NULL);

	if (ctx->codecid==GF_CODECID_HEVC) {
		HEVC_SPS *sps = &ctx->hevc_state->sps[ctx->hevc_state->sps_active_idx];
		if (sps->colour_description_present_flag) {
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_COLR_PRIMARIES, & PROP_UINT(sps->colour_primaries) );
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_COLR_TRANSFER, & PROP_UINT(sps->transfer_characteristic) );
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_COLR_MX, & PROP_UINT(sps->matrix_coeffs) );
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_COLR_RANGE, & PROP_BOOL(sps->video_full_range_flag) );
			has_colr_info = GF_TRUE;
		}
	} else if (ctx->codecid==GF_CODECID_VVC) {
	} else {
		/*use the last active SPS*/
		if (ctx->avc_state->sps[ctx->avc_state->sps_active_idx].vui_parameters_present_flag
		&& ctx->avc_state->sps[ctx->avc_state->sps_active_idx].vui.colour_description_present_flag) {
			AVC_VUI *vui = &ctx->avc_state->sps[ctx->avc_state->sps_active_idx].vui;

			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_COLR_PRIMARIES, & PROP_UINT(vui->colour_primaries) );
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_COLR_TRANSFER, & PROP_UINT(vui->transfer_characteristics) );
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_COLR_MX, & PROP_UINT(vui->matrix_coefficients) );
			gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_COLR_RANGE, & PROP_BOOL(vui->video_full_range_flag) );
			has_colr_info = GF_TRUE;
		}
	}

	if (!has_colr_info) {
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_COLR_PRIMARIES, NULL);
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_COLR_TRANSFER, NULL);
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_COLR_MX, NULL);
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_COLR_RANGE, NULL);
	}

	naludmx_update_clli_mdcv(ctx, GF_TRUE);

	naludmx_set_dolby_vision(ctx);

}

fixed #2223


static s32 naludmx_parse_nal_hevc(GF_NALUDmxCtx *ctx, char *data, u32 size, Bool *skip_nal, Bool *is_slice, Bool *is_islice)
{
#ifdef GPAC_DISABLE_HEVC
	return -1;
#else
	s32 ps_idx = 0;
	s32 res;
	u8 nal_unit_type, temporal_id, layer_id;
	*skip_nal = GF_FALSE;

	if (size<2) return -1;

	gf_bs_reassign_buffer(ctx->bs_r, data, size);
	res = gf_hevc_parse_nalu_bs(ctx->bs_r, ctx->hevc_state, &nal_unit_type, &temporal_id, &layer_id);
	ctx->nb_nalus++;

	if (res < 0) {
		if (res == -1) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] Warning: Error parsing NAL unit\n", ctx->log_name));
		}
		*skip_nal = GF_TRUE;
	}

	if (layer_id && ctx->nosvc) {
		*skip_nal = GF_TRUE;
		return 0;
	}

	switch (nal_unit_type) {
	case GF_HEVC_NALU_VID_PARAM:
		if (ctx->novpsext) {
			//this may modify nal_size, but we don't use it for bitstream reading
			ps_idx = gf_hevc_read_vps_ex(data, &size, ctx->hevc_state, GF_TRUE);
		} else {
			ps_idx = ctx->hevc_state->last_parsed_vps_id;
		}
		if (ps_idx<0) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] Error parsing Video Param Set\n", ctx->log_name));
		} else {
			naludmx_queue_param_set(ctx, data, size, GF_HEVC_NALU_VID_PARAM, ps_idx);
		}
		*skip_nal = GF_TRUE;
		break;
	case GF_HEVC_NALU_SEQ_PARAM:
		ps_idx = ctx->hevc_state->last_parsed_sps_id;
		if (ps_idx<0) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] Error parsing Sequence Param Set\n", ctx->log_name));
		} else {
			naludmx_queue_param_set(ctx, data, size, GF_HEVC_NALU_SEQ_PARAM, ps_idx);
		}
		*skip_nal = GF_TRUE;
		break;
	case GF_HEVC_NALU_PIC_PARAM:
		ps_idx = ctx->hevc_state->last_parsed_pps_id;
		if (ps_idx<0) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] Error parsing Picture Param Set\n", ctx->log_name));
		} else {
			naludmx_queue_param_set(ctx, data, size, GF_HEVC_NALU_PIC_PARAM, ps_idx);
		}
		*skip_nal = GF_TRUE;
		break;
	case GF_HEVC_NALU_SEI_PREFIX:
		gf_hevc_parse_sei(data, size, ctx->hevc_state);
		if (!ctx->nosei) {
			ctx->nb_sei++;
			naludmx_push_prefix(ctx, data, size, GF_FALSE);
		} else {
			ctx->nb_nalus--;
		}
		*skip_nal = GF_TRUE;
		break;
	case GF_HEVC_NALU_SEI_SUFFIX:
		if (! ctx->is_playing) return 0;
		if (ctx->nosei) {
			*skip_nal = GF_TRUE;
			ctx->nb_nalus--;
		} else {
			ctx->nb_sei++;
		}
		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:
		if (! ctx->is_playing) return 0;
		*is_slice = GF_TRUE;
		ctx->last_layer_id = layer_id;
		ctx->last_temporal_id = temporal_id;
		if (! *skip_nal) {
			switch (ctx->hevc_state->s_info.slice_type) {
			case GF_HEVC_SLICE_TYPE_P:
				if (layer_id) ctx->nb_e_p++;
				else ctx->nb_p++;
				break;
			case GF_HEVC_SLICE_TYPE_I:
				if (layer_id) ctx->nb_e_i++;
				else ctx->nb_i++;
				*is_islice = GF_TRUE;
				break;
			case GF_HEVC_SLICE_TYPE_B:
				if (layer_id) ctx->nb_e_b++;
				else ctx->nb_b++;
				break;
			}
		}
		break;

	case GF_HEVC_NALU_ACCESS_UNIT:
		ctx->nb_aud++;
		if (!ctx->audelim) {
			*skip_nal = GF_TRUE;
		} else if (!ctx->opid) {
			ctx->has_initial_aud = GF_TRUE;
			memcpy(ctx->init_aud, data, 3);
		}
		break;
	/*remove*/
	case GF_HEVC_NALU_FILLER_DATA:
	case GF_HEVC_NALU_END_OF_SEQ:
	case GF_HEVC_NALU_END_OF_STREAM:
		*skip_nal = GF_TRUE;
		break;

	//parsing is partial, see https://github.com/DolbyLaboratories/dlb_mp4base/blob/70a2e1d4d99a8439b7b8087bf50dd503eeea2291/src/esparser/parser_hevc.c#L1233
	case GF_HEVC_NALU_DV_RPU:
		if (ctx->dv_mode==DVMODE_CLEAN) {
			*skip_nal = GF_TRUE;
		} else {
			ctx->nb_dv_rpu ++;
			if (ctx->nb_dv_rpu==1)
				naludmx_set_dolby_vision(ctx);
		}
		break;
	case GF_HEVC_NALU_DV_EL:
		if ((ctx->dv_mode==DVMODE_CLEAN) || (ctx->dv_mode==DVMODE_SINGLE)) {
			*skip_nal = GF_TRUE;
		} else {
			ctx->nb_dv_el ++;
			if (ctx->nb_dv_el==1)
				naludmx_set_dolby_vision(ctx);
		}
		break;

	default:
		if (! ctx->is_playing) return 0;
		GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[%s] NAL Unit type %d not handled - adding\n", ctx->log_name, nal_unit_type));
		break;
	}
	if (*skip_nal) return res;

	ctx->linf[layer_id].layer_id_plus_one = layer_id + 1;
	if (! ctx->linf[layer_id].max_temporal_id ) ctx->linf[layer_id].max_temporal_id = temporal_id;
	else if (ctx->linf[layer_id].max_temporal_id < temporal_id) ctx->linf[layer_id].max_temporal_id = temporal_id;

	if (! ctx->linf[layer_id].min_temporal_id ) ctx->linf[layer_id].min_temporal_id = temporal_id;
	else if (ctx->linf[layer_id].min_temporal_id > temporal_id) ctx->linf[layer_id].min_temporal_id = temporal_id;

	if (ctx->max_temporal_id[layer_id] < temporal_id)
		ctx->max_temporal_id[layer_id] = temporal_id;
	if (ctx->min_layer_id > layer_id) ctx->min_layer_id = layer_id;
	return res;
#endif // GPAC_DISABLE_HEVC
}

fixed #2223


static void naludmx_update_clli_mdcv(GF_NALUDmxCtx *ctx, Bool reset_crc)
{
	if (!ctx->opid) return;

	if (reset_crc)
		ctx->clli_crc = 0;
	if ((ctx->hevc_state && ctx->hevc_state->clli_valid)
		|| (ctx->vvc_state && ctx->vvc_state->clli_valid)
	) {
		u8 *clli = ctx->hevc_state ? ctx->hevc_state->clli_data : ctx->vvc_state->clli_data;
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CONTENT_LIGHT_LEVEL, &PROP_DATA(clli, 4));
		ctx->clli_crc = gf_crc_32(clli, 4);
	}
	if (reset_crc)
		ctx->mdcv_crc = 0;

	if ((ctx->hevc_state && ctx->hevc_state->mdcv_valid)
		|| (ctx->vvc_state && ctx->vvc_state->mdcv_valid)
	) {
		u8 *mdcv = ctx->hevc_state ? ctx->hevc_state->mdcv_data : ctx->vvc_state->mdcv_data;
		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_MASTER_DISPLAY_COLOUR, &PROP_DATA(mdcv, 24));
		ctx->mdcv_crc = gf_crc_32(mdcv, 24);
	}
}

fixed #2223


static GFINLINE void naludmx_update_time(GF_NALUDmxCtx *ctx)
{
	assert(ctx->cur_fps.num);

	if (!ctx->notime) {
		//very first frame, no dts diff, assume 3000/90k. It should only hurt if we have several frames packet in the first packet sent
		u64 dts_inc = ctx->cur_fps.den ? ctx->cur_fps.den : 3000;
		ctx->cts += dts_inc;
		ctx->dts += dts_inc;
	} else {
		assert(ctx->cur_fps.den);
		ctx->cts += ctx->cur_fps.den;
		ctx->dts += ctx->cur_fps.den;
	}
}

fixed #2223


static void naludmx_create_vvc_decoder_config(GF_NALUDmxCtx *ctx, u8 **dsi, u32 *dsi_size, u8 **dsi_enh, u32 *dsi_enh_size, u32 *max_width, u32 *max_height, u32 *max_enh_width, u32 *max_enh_height, GF_Fraction *sar, Bool *has_vvc_base)
{
	u32 i, count;
	u8 layer_id;
	Bool first = GF_TRUE;
	Bool first_lvvc = GF_TRUE;
	GF_VVCConfig *cfg;
	u32 max_w, max_h, max_ew, max_eh;

	*has_vvc_base = GF_FALSE;

	max_w = max_h = 0;
	max_ew = max_eh = 0;
	sar->num = sar->den = 0;

	cfg = gf_odf_vvc_cfg_new();
	cfg->nal_unit_size = ctx->nal_length;

	//check we have one pps or sps in base layer
	count = gf_list_count(ctx->sps);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->sps, i);
		layer_id = (sl->data[0] & 0x3f);
		//todo, base is not always 0 !
		if (!layer_id) {
			*has_vvc_base = GF_TRUE;
			break;
		}
	}
	count = gf_list_count(ctx->pps);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->pps, i);
		layer_id = (sl->data[0] & 0x3f);
		//todo, base is not always 0 !
		if (!layer_id) {
			*has_vvc_base = GF_TRUE;
			break;
		}
	}
	//assign vps first so that they are serialized first
	count = gf_list_count(ctx->vps);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->vps, i);
		VVC_VPS *vps = &ctx->vvc_state->vps[sl->id];

		if (!i) {
			cfg->avgFrameRate = vps->rates[0].avg_pic_rate;
			cfg->constantFrameRate = vps->rates[0].constant_pic_rate_idc;
			cfg->numTemporalLayers = vps->max_sub_layers;
		}
		if (!ctx->analyze)
			naludmx_add_param_nalu(cfg->param_array, sl, GF_VVC_NALU_VID_PARAM);
	}

	count = gf_list_count(ctx->sps);
	for (i=0; i<count; i++) {
		Bool is_lvvc = GF_FALSE;
		GF_NALUFFParam *sl = gf_list_get(ctx->sps, i);
		VVC_SPS *sps = &ctx->vvc_state->sps[sl->id];
		layer_id = sl->data[0] & 0x3f;
		if (!layer_id) *has_vvc_base = GF_TRUE;

		if (ctx->explicit || layer_id) {
			is_lvvc = GF_TRUE;
		}

		if (first || (is_lvvc && first_lvvc) ) {
			VVC_VPS *vps = &ctx->vvc_state->vps[sps->vps_id];
			cfg->avgFrameRate = 0;
			cfg->constantFrameRate = 1;
			cfg->numTemporalLayers = sps->max_sublayers;
			cfg->nal_unit_size = ctx->nal_length;
			cfg->ptl_present = vps->num_ptl ? 1 : 0;

			if (vps->num_ptl) {
				cfg->num_constraint_info = vps->ptl[0].gci_present ? 1 : 12;
				cfg->general_profile_idc = vps->ptl[0].general_profile_idc;
				cfg->general_tier_flag = vps->ptl[0].general_tier_flag;
				cfg->general_level_idc = vps->ptl[0].general_level_idc;
				cfg->ptl_frame_only_constraint = vps->ptl[0].frame_only_constraint;
				cfg->ptl_multilayer_enabled = vps->ptl[0].multilayer_enabled;

				cfg->general_constraint_info = gf_malloc(sizeof(u8) * cfg-> num_constraint_info);
				if (cfg->general_constraint_info)
					memcpy(cfg->general_constraint_info, vps->ptl[0].gci, cfg->num_constraint_info);

				//todo set temporal sublayers
				cfg->ptl_sublayer_present_mask = 0;
				cfg->num_sub_profiles = 0;
				cfg->ols_idx = 0;
			}
			cfg->chroma_format = sps->chroma_format_idc;
			cfg->bit_depth = sps->bitdepth;
			cfg->maxPictureWidth = sps->width;
			cfg->maxPictureHeight = sps->height;

			if (sps->aspect_ratio_info_present_flag && sps->sar_width && sps->sar_height) {
				sar->num = sps->sar_width;
				sar->den = sps->sar_height;
			}

			/*disable frame rate scan, most bitstreams have wrong values there*/
			if (ctx->notime && first && (!ctx->fps.num || !ctx->fps.den) && sps->has_timing_info
				/*if detected FPS is greater than 1000, assume wrong timing info*/
				&& (sps->time_scale <= 1000*sps->num_units_in_tick)
			) {
				ctx->cur_fps.num = sps->time_scale;
				ctx->cur_fps.den = sps->num_units_in_tick;
				gf_media_get_reduced_frame_rate(&ctx->cur_fps.num, &ctx->cur_fps.den);

				if (!ctx->fps.num && ctx->dts==ctx->fps.den)
					ctx->dts = ctx->cur_fps.den;
			}
			ctx->fps = ctx->cur_fps;
		}
		first = GF_FALSE;
		if (is_lvvc) {
			first_lvvc = GF_FALSE;
			if (sps->width > max_ew) max_ew = sps->width;
			if (sps->height > max_eh) max_eh = sps->height;
		} else {
			if (sps->width > max_w) max_w = sps->width;
			if (sps->height > max_h) max_h = sps->height;
		}
		if (!ctx->analyze)
			naludmx_add_param_nalu(cfg->param_array, sl, GF_VVC_NALU_SEQ_PARAM);
	}

	count = gf_list_count(ctx->pps);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->pps, i);
		layer_id = sl->data[0] & 0x3F;
		if (!layer_id) *has_vvc_base = GF_TRUE;
		if (!ctx->analyze)
			naludmx_add_param_nalu(cfg->param_array, sl, GF_VVC_NALU_PIC_PARAM);
	}

	count = gf_list_count(ctx->vvc_dci);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->vvc_dci, i);
		layer_id = sl->data[0] & 0x3F;
		if (!layer_id) *has_vvc_base = GF_TRUE;
		if (!ctx->analyze)
			naludmx_add_param_nalu(cfg->param_array, sl, GF_VVC_NALU_DEC_PARAM);
	}

	count = gf_list_count(ctx->vvc_aps_pre);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->vvc_aps_pre, i);
		layer_id = sl->data[0] & 0x3F;
		if (!layer_id) *has_vvc_base = GF_TRUE;
		if (!ctx->analyze)
			naludmx_add_param_nalu(cfg->param_array, sl, GF_VVC_NALU_APS_PREFIX);
	}

	*dsi = *dsi_enh = NULL;
	*dsi_size = *dsi_enh_size = 0;

	gf_odf_vvc_cfg_write(cfg, dsi, dsi_size);
	*max_width = max_w;
	*max_height = max_h;
	*max_enh_width = max_ew;
	*max_enh_height = max_eh;

	count = gf_list_count(cfg->param_array);
	for (i=0; i<count; i++) {
		GF_NALUFFParamArray *pa = gf_list_get(cfg->param_array, i);
		gf_list_reset(pa->nalus);
	}
	gf_odf_vvc_cfg_del(cfg);
}

fixed #2223


static void naludmx_end_access_unit(GF_NALUDmxCtx *ctx)
{
	//finalize current fram flags - we will flush(send) later on
	naludmx_finalize_au_flags(ctx);

	ctx->has_islice = GF_FALSE;
	ctx->first_slice_in_au = GF_TRUE;
	ctx->sei_recovery_frame_count = -1;
	ctx->au_sap = GF_FILTER_SAP_NONE;
	ctx->au_sap2_poc_reset = GF_FALSE;
	ctx->bottom_field_flag = GF_FALSE;
}

fixed #2223


static void naldmx_bs_log(void *udta, const char *field_name, u32 nb_bits, u64 field_val, s32 idx1, s32 idx2, s32 idx3)
{
	GF_NALUDmxCtx *ctx = (GF_NALUDmxCtx *) udta;
	GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, (" %s", field_name));
	if (idx1>=0) {
		GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("_%d", idx1));
		if (idx2>=0) {
			GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("_%d", idx2));
			if (idx3>=0) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("_%d", idx3));
			}
		}
	}
	GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("=\""LLD, field_val));
	if ((ctx->bsdbg==2) && ((s32) nb_bits > 1) )
		GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("(%u)", nb_bits));

	GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("\" "));
}

fixed #2223


static void naludmx_update_nalu_maxsize(GF_NALUDmxCtx *ctx, u32 size)
{
	if (ctx->max_nalu_size < size) {
		ctx->max_nalu_size = size;
		if (size > ctx->max_nalu_size_allowed) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[%s] nal size %d larger than max allowed size %d - change import settings\n", ctx->log_name, size, ctx->max_nalu_size_allowed ));
		}
	}
}

fixed #2223


static const char *naludmx_probe_data(const u8 *data, u32 size, GF_FilterProbeScore *score)
{
	u32 sc, sc_size;
	u32 not_hevc=0;
	u32 not_avc=0;
	u32 not_vvc=0;
	u32 nb_hevc=0;
	u32 nb_avc=0;
	u32 nb_vvc=0;
	u32 nb_nalus=0;
	u32 nb_hevc_zero=0;
	u32 nb_avc_zero=0;
	u32 nb_vvc_zero=0;
	u32 nb_sps_hevc=0,nb_pps_hevc=0,nb_vps_hevc=0;
	u32 nb_sps_avc=0,nb_pps_avc=0;
	u32 nb_sps_vvc=0,nb_pps_vvc=0,nb_vps_vvc=0;

	while (size>3) {
		u32 nal_type=0;
		sc_size = 0;
		sc = gf_media_nalu_next_start_code(data, size, &sc_size);
		if (!sc_size) break;

		data += sc + sc_size;
		if (size <= sc + sc_size) break;
		size -= sc + sc_size;

		if (data[0] & 0x80) {
			not_avc++;
			not_hevc++;
			not_vvc++;
			continue;
		}
		nb_nalus++;

		nal_type = (data[0] & 0x7E) >> 1;
		if (nal_type<=40) {
			nb_hevc++;
			switch (nal_type) {
			case GF_HEVC_NALU_PIC_PARAM:
				if (nb_sps_hevc)
					nb_pps_hevc++;
				break;
			case GF_HEVC_NALU_SEQ_PARAM:
				nb_sps_hevc++;
				break;
			case GF_HEVC_NALU_VID_PARAM:
				nb_vps_hevc++;
				break;
			case 0:
				nb_hevc_zero++;
				break;
			}
		} else {
			if ((nal_type!=GF_HEVC_NALU_DV_RPU) && (nal_type!=GF_HEVC_NALU_DV_EL))
				not_hevc++;
		}

		nal_type = data[0] & 0x1F;
		if (nal_type && nal_type<=24) {
			nb_avc++;
			switch (nal_type) {
			case GF_AVC_NALU_PIC_PARAM:
				if (nb_sps_avc)
					nb_pps_avc++;
				break;
			case GF_AVC_NALU_SEQ_PARAM:
				nb_sps_avc++;
				break;
			case 0:
				nb_avc_zero++;
				break;
			}
		} else {
			if ((nal_type!=GF_AVC_NALU_DV_RPU) && (nal_type!=GF_AVC_NALU_DV_EL))
				not_avc++;
		}

		//check vvc - 2nd bit reserved to 0
		if (data[0] & 0x40) {
			not_vvc++;
			continue;
		}
		nal_type = data[1] >> 3;
		if (nal_type>31) {
			not_vvc++;
			continue;
		}
		nb_vvc++;
		switch (nal_type) {
		case GF_VVC_NALU_PIC_PARAM:
			if (nb_sps_vvc)
				nb_pps_vvc++;
			break;
		case GF_VVC_NALU_SEQ_PARAM:
			nb_sps_vvc++;
			break;
		case GF_VVC_NALU_VID_PARAM:
			nb_vps_vvc++;
			break;
		case GF_VVC_NALU_ACCESS_UNIT:
			//to detect files without VPS correctly
			nb_vps_vvc++;
			break;
		case 0:
			nb_vvc_zero++;
			break;
		}
	}

	if (!nb_sps_avc || !nb_pps_avc) nb_avc=0;
	if (!nb_sps_hevc || !nb_pps_hevc || !nb_vps_hevc) nb_hevc=0;
	//VPS is optional in VVC, don't check for its presence
	if (!nb_sps_vvc || !nb_pps_vvc) nb_vvc=0;
	if (not_avc) nb_avc=0;
	if (not_hevc) nb_hevc=0;
	if (not_vvc) nb_vvc=0;

	if (not_avc && not_hevc && not_vvc) return NULL;
	if (nb_avc==nb_avc_zero) nb_avc=0;
	if (nb_hevc==nb_hevc_zero) nb_hevc=0;
	if (nb_vvc==nb_vvc_zero) nb_vvc=0;

	if (!nb_hevc && !nb_avc && !nb_vvc) return NULL;
	*score = GF_FPROBE_SUPPORTED;
	if (!nb_hevc) return (nb_vvc>nb_avc) ? "video/vvc" : "video/avc";
	if (!nb_avc) return (nb_vvc>nb_hevc) ? "video/vvc" : "video/hevc";
	if (!nb_vvc) return (nb_avc>nb_hevc) ? "video/avc" : "video/hevc";

	if ((nb_hevc>nb_avc) && (nb_hevc>nb_vvc)) return "video/hevc";
	if ((nb_vvc>nb_avc) && (nb_vvc>nb_hevc)) return "video/vvc";
	return "video/avc";
}

fixed #2223


static void naludmx_create_hevc_decoder_config(GF_NALUDmxCtx *ctx, u8 **dsi, u32 *dsi_size, u8 **dsi_enh, u32 *dsi_enh_size, u32 *max_width, u32 *max_height, u32 *max_enh_width, u32 *max_enh_height, GF_Fraction *sar, Bool *has_hevc_base)
{
#ifndef GPAC_DISABLE_HEVC
	u32 i, count;
	u8 layer_id;
	Bool first = GF_TRUE;
	Bool first_lhvc = GF_TRUE;
	GF_HEVCConfig *cfg;
	GF_HEVCConfig *hvcc;
	GF_HEVCConfig *lvcc;
	u32 max_w, max_h, max_ew, max_eh;

	*has_hevc_base = GF_FALSE;

	max_w = max_h = 0;
	max_ew = max_eh = 0;
	sar->num = sar->den = 0;

	hvcc = gf_odf_hevc_cfg_new();
	lvcc = gf_odf_hevc_cfg_new();
	hvcc->nal_unit_size = ctx->nal_length;
	lvcc->nal_unit_size = ctx->nal_length;
	lvcc->is_lhvc = GF_TRUE;
	//check we have one pps or sps in base layer
	count = gf_list_count(ctx->sps);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->sps, i);
		layer_id = ((sl->data[0] & 0x1) << 5) | (sl->data[1] >> 3);
		if (!layer_id) {
			*has_hevc_base = GF_TRUE;
			break;
		}
	}
	count = gf_list_count(ctx->pps);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->pps, i);
		layer_id = ((sl->data[0] & 0x1) << 5) | (sl->data[1] >> 3);
		if (!layer_id) {
			*has_hevc_base = GF_TRUE;
			break;
		}
	}
	//assign vps first so that they are serialized first
	count = gf_list_count(ctx->vps);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->vps, i);
		HEVC_VPS *vps = &ctx->hevc_state->vps[sl->id];

		if (!i) {
			hvcc->avgFrameRate = lvcc->avgFrameRate = vps->rates[0].avg_pic_rate;
			hvcc->constantFrameRate = lvcc->constantFrameRate = vps->rates[0].constant_pic_rate_idc;
			hvcc->numTemporalLayers = lvcc->numTemporalLayers = vps->max_sub_layers;
			hvcc->temporalIdNested = lvcc->temporalIdNested = vps->temporal_id_nesting;
		}
		//TODO set scalability mask
		if (!ctx->analyze)
			naludmx_add_param_nalu((ctx->explicit || ! (*has_hevc_base) ) ? lvcc->param_array : hvcc->param_array, sl, GF_HEVC_NALU_VID_PARAM);
	}

	count = gf_list_count(ctx->sps);
	for (i=0; i<count; i++) {
		Bool is_lhvc = GF_FALSE;
		GF_NALUFFParam *sl = gf_list_get(ctx->sps, i);
		HEVC_SPS *sps = &ctx->hevc_state->sps[sl->id];
		layer_id = ((sl->data[0] & 0x1) << 5) | (sl->data[1] >> 3);
		if (!layer_id) *has_hevc_base = GF_TRUE;

		if (ctx->explicit || layer_id) {
			cfg = lvcc;
			is_lhvc = GF_TRUE;
		} else {
			cfg = hvcc;
		}

		if (first || (is_lhvc && first_lhvc) ) {
			cfg->configurationVersion = 1;
			cfg->profile_space = sps->ptl.profile_space;
			cfg->tier_flag = sps->ptl.tier_flag;
			cfg->profile_idc = sps->ptl.profile_idc;
			cfg->general_profile_compatibility_flags = sps->ptl.profile_compatibility_flag;
			cfg->progressive_source_flag = sps->ptl.general_progressive_source_flag;
			cfg->interlaced_source_flag = sps->ptl.general_interlaced_source_flag;
			cfg->non_packed_constraint_flag = sps->ptl.general_non_packed_constraint_flag;
			cfg->frame_only_constraint_flag = sps->ptl.general_frame_only_constraint_flag;
			cfg->constraint_indicator_flags = sps->ptl.general_reserved_44bits;
			cfg->level_idc = sps->ptl.level_idc;
			cfg->chromaFormat = sps->chroma_format_idc;
			cfg->luma_bit_depth = sps->bit_depth_luma;
			cfg->chroma_bit_depth = sps->bit_depth_chroma;
			ctx->interlaced = cfg->interlaced_source_flag ? GF_TRUE : GF_FALSE;

			if (sps->aspect_ratio_info_present_flag && sps->sar_width && sps->sar_height) {
				sar->num = sps->sar_width;
				sar->den = sps->sar_height;
			}

			/*disable frame rate scan, most bitstreams have wrong values there*/
			if (ctx->notime && first && (!ctx->fps.num || !ctx->fps.den) && sps->has_timing_info
				/*if detected FPS is greater than 1000, assume wrong timing info*/
				&& (sps->time_scale <= 1000*sps->num_units_in_tick)
			) {
				ctx->cur_fps.num = sps->time_scale;
				ctx->cur_fps.den = sps->num_units_in_tick;

				if (!ctx->fps.num && ctx->dts==ctx->fps.den)
					ctx->dts = ctx->cur_fps.den;
			}
			ctx->fps = ctx->cur_fps;
		}
		first = GF_FALSE;
		if (is_lhvc) {
			first_lhvc = GF_FALSE;
			if (sps->width > max_ew) max_ew = sps->width;
			if (sps->height > max_eh) max_eh = sps->height;
		} else {
			if (sps->width > max_w) max_w = sps->width;
			if (sps->height > max_h) max_h = sps->height;
		}
		if (!ctx->analyze)
			naludmx_add_param_nalu(cfg->param_array, sl, GF_HEVC_NALU_SEQ_PARAM);
	}

	cfg = ctx->explicit ? lvcc : hvcc;
	count = gf_list_count(ctx->pps);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->pps, i);
		layer_id = ((sl->data[0] & 0x1) << 5) | (sl->data[1] >> 3);
		if (!layer_id) *has_hevc_base = GF_TRUE;
		if (!ctx->analyze)
			naludmx_add_param_nalu(layer_id ? lvcc->param_array : cfg->param_array, sl, GF_HEVC_NALU_PIC_PARAM);
	}

	*dsi = *dsi_enh = NULL;
	*dsi_size = *dsi_enh_size = 0;

	if (ctx->explicit || ! (*has_hevc_base) ) {
		naludmx_hevc_set_parall_type(ctx, lvcc);
		gf_odf_hevc_cfg_write(lvcc, dsi, dsi_size);
		*max_width = *max_enh_width = max_ew;
		*max_height = *max_enh_height = max_eh;
	} else {
		naludmx_hevc_set_parall_type(ctx, hvcc);
		gf_odf_hevc_cfg_write(hvcc, dsi, dsi_size);
		if (gf_list_count(lvcc->param_array) ) {
			naludmx_hevc_set_parall_type(ctx, lvcc);
			gf_odf_hevc_cfg_write(lvcc, dsi_enh, dsi_enh_size);
		}
		*max_width = max_w;
		*max_height = max_h;
		*max_enh_width = max_ew;
		*max_enh_height = max_eh;
	}
	count = gf_list_count(hvcc->param_array);
	for (i=0; i<count; i++) {
		GF_NALUFFParamArray *pa = gf_list_get(hvcc->param_array, i);
		gf_list_reset(pa->nalus);
	}
	count = gf_list_count(lvcc->param_array);
	for (i=0; i<count; i++) {
		GF_NALUFFParamArray *pa = gf_list_get(lvcc->param_array, i);
		gf_list_reset(pa->nalus);
	}
	gf_odf_hevc_cfg_del(hvcc);
	gf_odf_hevc_cfg_del(lvcc);
#endif // GPAC_DISABLE_HEVC
}

fixed #2223


void naludmx_create_avc_decoder_config(GF_NALUDmxCtx *ctx, u8 **dsi, u32 *dsi_size, u8 **dsi_enh, u32 *dsi_enh_size, u32 *max_width, u32 *max_height, u32 *max_enh_width, u32 *max_enh_height, GF_Fraction *sar)
{
	u32 i, count;
	Bool first = GF_TRUE;
	Bool first_svc = GF_TRUE;
	GF_AVCConfig *cfg;
	GF_AVCConfig *avcc;
	GF_AVCConfig *svcc;
	u32 max_w, max_h, max_ew, max_eh;

	max_w = max_h = max_ew = max_eh = 0;
	sar->num = sar->den = 0;

	avcc = gf_odf_avc_cfg_new();
	svcc = gf_odf_avc_cfg_new();
	avcc->nal_unit_size = ctx->nal_length;
	svcc->nal_unit_size = ctx->nal_length;

	ctx->is_mvc = GF_FALSE;
	count = gf_list_count(ctx->sps);
	for (i=0; i<count; i++) {
		Bool is_svc = GF_FALSE;
		GF_NALUFFParam *sl = gf_list_get(ctx->sps, i);
		AVC_SPS *sps = &ctx->avc_state->sps[sl->id];
		u32 nal_type = sl->data[0] & 0x1F;

		if ((sps->profile_idc == 118) || (sps->profile_idc == 128)) {
			ctx->is_mvc = GF_TRUE;
		}

		if (ctx->explicit) {
			cfg = svcc;
		} else if (nal_type == GF_AVC_NALU_SVC_SUBSEQ_PARAM) {
			cfg = svcc;
			is_svc = GF_TRUE;
		} else {
			cfg = avcc;
		}

		if (first || (is_svc && first_svc) ) {
			cfg->configurationVersion = 1;
			cfg->profile_compatibility = sps->prof_compat;
			cfg->AVCProfileIndication = sps->profile_idc;
			cfg->AVCLevelIndication = sps->level_idc;
			cfg->chroma_format = sps->chroma_format;
			cfg->luma_bit_depth = 8 + sps->luma_bit_depth_m8;
			cfg->chroma_bit_depth = 8 + sps->chroma_bit_depth_m8;
			/*try to patch ?*/
			if (!gf_avcc_use_extensions(cfg->AVCProfileIndication)
				&& ((cfg->chroma_format>1) || (cfg->luma_bit_depth>8) || (cfg->chroma_bit_depth>8))
			) {
				if ((cfg->luma_bit_depth>8) || (cfg->chroma_bit_depth>8)) {
					cfg->AVCProfileIndication = 110;
				} else {
					cfg->AVCProfileIndication = (cfg->chroma_format==3) ? 244 : 122;
				}
			}
			if (sps->vui_parameters_present_flag && sps->vui.par_num && sps->vui.par_den) {
				sar->num = sps->vui.par_num;
				sar->den = sps->vui.par_den;
			}
			ctx->interlaced = sps->frame_mbs_only_flag ? GF_FALSE : GF_TRUE;

			/*disable frame rate scan, most bitstreams have wrong values there*/
			if (first && (!ctx->fps.num || !ctx->fps.den) && sps->vui.timing_info_present_flag
				/*if detected FPS is greater than 1000, assume wrong timing info*/
				&& (sps->vui.time_scale <= 1000*sps->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 (!sps->vui.pic_struct_present_flag) {
					DeltaTfiDivisorIdx = 1 + (1 - ctx->avc_state->s_info.field_pic_flag);
				} else {
					if (!ctx->avc_state->sei.pic_timing.pic_struct)
						DeltaTfiDivisorIdx = 2;
					else if (ctx->avc_state->sei.pic_timing.pic_struct == 8)
						DeltaTfiDivisorIdx = 6;
					else
						DeltaTfiDivisorIdx = (ctx->avc_state->sei.pic_timing.pic_struct+1) / 2;
				}
				if (ctx->notime && sps->vui.time_scale && sps->vui.num_units_in_tick) {
					ctx->cur_fps.num = 2 * sps->vui.time_scale;
					ctx->cur_fps.den = 2 * sps->vui.num_units_in_tick * DeltaTfiDivisorIdx;

					if (!ctx->fps.num && ctx->dts==ctx->fps.den)
						ctx->dts = ctx->cur_fps.den;
				}
				if (! sps->vui.fixed_frame_rate_flag)
					GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[%s] Possible Variable Frame Rate: VUI \"fixed_frame_rate_flag\" absent\n", ctx->log_name));
			}
			ctx->fps = ctx->cur_fps;
		}
		first = GF_FALSE;
		if (is_svc) {
			first_svc = GF_FALSE;
			if (sps->width > max_ew) max_ew = sps->width;
			if (sps->height > max_eh) max_eh = sps->height;
		} else {
			if (sps->width > max_w) max_w = sps->width;
			if (sps->height > max_h) max_h = sps->height;
		}
		if (!ctx->analyze)
			gf_list_add(cfg->sequenceParameterSets, sl);
	}

	cfg = ctx->explicit ? svcc : avcc;
	count = gf_list_count(ctx->sps_ext);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->sps_ext, i);
		if (!cfg->sequenceParameterSetExtensions) cfg->sequenceParameterSetExtensions = gf_list_new();
		if (!ctx->analyze)
			gf_list_add(cfg->sequenceParameterSetExtensions, sl);
	}

	cfg = ctx->explicit ? svcc : avcc;
	count = gf_list_count(ctx->pps);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->pps, i);
		if (!ctx->analyze)
			gf_list_add(cfg->pictureParameterSets, sl);
	}

	cfg = svcc;
	count = gf_list_count(ctx->pps_svc);
	for (i=0; i<count; i++) {
		GF_NALUFFParam *sl = gf_list_get(ctx->pps_svc, i);
		if (!ctx->analyze)
			gf_list_add(cfg->pictureParameterSets, sl);
	}

	*dsi = *dsi_enh = NULL;
	*dsi_size = *dsi_enh_size = 0;

	if (ctx->explicit) {
		gf_odf_avc_cfg_write(svcc, dsi, dsi_size);
	} else {
		gf_odf_avc_cfg_write(avcc, dsi, dsi_size);
		if (gf_list_count(svcc->sequenceParameterSets) || svcc->sequenceParameterSetExtensions) {
			gf_odf_avc_cfg_write(svcc, dsi_enh, dsi_enh_size);
		}
	}
	gf_list_reset(avcc->sequenceParameterSets);
	gf_list_reset(avcc->sequenceParameterSetExtensions);
	gf_list_reset(avcc->pictureParameterSets);
	gf_list_reset(svcc->sequenceParameterSets);
	gf_list_reset(svcc->sequenceParameterSetExtensions);
	gf_list_reset(svcc->pictureParameterSets);
	gf_odf_avc_cfg_del(avcc);
	gf_odf_avc_cfg_del(svcc);
	*max_width = max_w;
	*max_height = max_h;
	*max_enh_width = max_ew;
	*max_enh_height = max_eh;
}

fixed #2223


static void naldmx_switch_timestamps(GF_NALUDmxCtx *ctx, GF_FilterPacket *pck)
{
	//input pid sets some timescale - we flushed pending data , update cts
	if (!ctx->notime) {
		u64 ts = gf_filter_pck_get_cts(pck);
		if (ts != GF_FILTER_NO_TS) {
			ctx->prev_cts = ctx->cts;
			ctx->cts = ts;
		}
		ts = gf_filter_pck_get_dts(pck);
		if (ts != GF_FILTER_NO_TS) {
			if (ctx->full_au_source) {
				ctx->prev_dts = ctx->dts;
				ctx->dts = ts;
			} else {
				GF_FilterClockType ck_type = gf_filter_pid_get_clock_info(ctx->ipid, NULL, NULL);
				if (ck_type==GF_FILTER_CLOCK_PCR_DISC)
					ctx->dts = ts;
				else if (ctx->dts<ts)
					ctx->dts=ts;

				if (!ctx->prev_dts) ctx->prev_dts = ts;
				else if (ctx->prev_dts != ts) {
					u64 diff = ts;
					diff -= ctx->prev_dts;
					if (!ctx->cur_fps.den)
						ctx->cur_fps.den = (u32) diff;
					else if (ctx->cur_fps.den > diff)
						ctx->cur_fps.den = (u32) diff;

					ctx->prev_dts = ts;
				}
			}
		}
		ctx->pck_duration = gf_filter_pck_get_duration(pck);
		if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
		ctx->src_pck = pck;
		gf_filter_pck_ref_props(&ctx->src_pck);
		//store framing flags. If input_is_au_start, the first NAL of the first frame beginning in this packet will
		//use the DTS/CTS of the input packet, otherwise we will use our internal POC recompute
		gf_filter_pck_get_framing(pck, &ctx->input_is_au_start, NULL);
	}
}

fixed #2223


void naludmx_probe_recovery_sei(GF_BitStream *bs, AVCState *avc)
{
	/*parse SEI*/
	while (gf_bs_available(bs)) {
		u32 ptype, psize;

		ptype = 0;
		while (1) {
			u8 v = gf_bs_read_int(bs, 8);
			ptype += v;
			if (v != 0xFF) break;
		}

		psize = 0;
		while (1) {
			u8 v = gf_bs_read_int(bs, 8);
			psize += v;
			if (v != 0xFF) break;
		}

		if (ptype==6) {
			avc->sei.recovery_point.frame_cnt = gf_bs_read_ue(bs);
			avc->sei.recovery_point.valid = 1;
			return;
		}

		gf_bs_skip_bytes(bs, psize);

		ptype = gf_bs_peek_bits(bs, 8, 0);
		if (ptype == 0x80) {
			break;
		}
	}
}

fixed #2223