}

void dump_hevc_track_info(GF_ISOFile *file, u32 trackNum, GF_HEVCConfig *hevccfg
#if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_HEVC)
	, HEVCState *hevc_state
#endif /*GPAC_DISABLE_AV_PARSERS  && defined(GPAC_DISABLE_HEVC)*/
	)
{
#if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_HEVC)
	u32 idx;
#endif
	u32 k;
	Bool non_hevc_base_layer=GF_FALSE;
	fprintf(stderr, "\t%s Info:", hevccfg->is_lhvc ? "LHVC" : "HEVC");
	if (!hevccfg->is_lhvc)
		fprintf(stderr, " Profile %s @ Level %g - Chroma Format %s\n", gf_hevc_get_profile_name(hevccfg->profile_idc), ((Double)hevccfg->level_idc) / 30.0, gf_avc_hevc_get_chroma_format_name(hevccfg->chromaFormat));
	fprintf(stderr, "\n");
	fprintf(stderr, "\tNAL Unit length bits: %d", 8*hevccfg->nal_unit_size);
	if (!hevccfg->is_lhvc)
		fprintf(stderr, " - general profile compatibility 0x%08X\n", hevccfg->general_profile_compatibility_flags);
	fprintf(stderr, "\n");
	fprintf(stderr, "\tParameter Sets: ");
	for (k=0; k<gf_list_count(hevccfg->param_array); k++) {
		GF_NALUFFParamArray *ar=gf_list_get(hevccfg->param_array, k);
		if (ar->type==GF_HEVC_NALU_SEQ_PARAM) {
			fprintf(stderr, "%d SPS ", gf_list_count(ar->nalus));
		}
		if (ar->type==GF_HEVC_NALU_PIC_PARAM) {
			fprintf(stderr, "%d PPS ", gf_list_count(ar->nalus));
		}
		if (ar->type==GF_HEVC_NALU_VID_PARAM) {
			fprintf(stderr, "%d VPS ", gf_list_count(ar->nalus));

#if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_HEVC)
			for (idx=0; idx<gf_list_count(ar->nalus); idx++) {
				GF_NALUFFParam *vps = gf_list_get(ar->nalus, idx);
				s32 ps_idx=gf_hevc_read_vps(vps->data, vps->size, hevc_state);
				if (hevccfg->is_lhvc && (ps_idx>=0)) {
					non_hevc_base_layer = ! hevc_state->vps[ps_idx].base_layer_internal_flag;
				}
			}
#endif

		}
	}

	fprintf(stderr, "\n");
#if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_HEVC)
	for (k=0; k<gf_list_count(hevccfg->param_array); k++) {
		GF_NALUFFParamArray *ar=gf_list_get(hevccfg->param_array, k);
		u32 width, height;
		s32 par_n, par_d;

		if (ar->type !=GF_HEVC_NALU_SEQ_PARAM) continue;
		for (idx=0; idx<gf_list_count(ar->nalus); idx++) {
			GF_Err e;
			GF_NALUFFParam *sps = gf_list_get(ar->nalus, idx);
			par_n = par_d = -1;
			e = gf_hevc_get_sps_info_with_state(hevc_state, sps->data, sps->size, NULL, &width, &height, &par_n, &par_d);
			if (e==GF_OK) {
				fprintf(stderr, "\tSPS resolution %dx%d", width, height);
				if ((par_n>0) && (par_d>0)) {
					u32 tw, th;
					gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL);
					fprintf(stderr, " - Pixel Aspect Ratio %d:%d - Indicated track size %d x %d", par_n, par_d, tw, th);
				}
				fprintf(stderr, "\n");
			} else {
				M4_LOG(GF_LOG_ERROR, ("Failed to read SPS: %s\n\n", gf_error_to_string(e) ));
			}
		}
	}
#endif
	if (!hevccfg->is_lhvc)
		fprintf(stderr, "\tBit Depth luma %d - Chroma %d - %d temporal layers\n", hevccfg->luma_bit_depth, hevccfg->chroma_bit_depth, hevccfg->numTemporalLayers);
	else
		fprintf(stderr, "\t%d temporal layers\n", hevccfg->numTemporalLayers);
	if (hevccfg->is_lhvc) {
		fprintf(stderr, "\t%sHEVC base layer - Complete representation %d\n", non_hevc_base_layer ? "Non-" : "", hevccfg->complete_representation);
	}

	for (k=0; k<gf_list_count(hevccfg->param_array); k++) {
		GF_NALUFFParamArray *ar=gf_list_get(hevccfg->param_array, k);
		if (ar->type==GF_HEVC_NALU_SEQ_PARAM) print_config_hash(ar->nalus, "SPS");
		else if (ar->type==GF_HEVC_NALU_PIC_PARAM) print_config_hash(ar->nalus, "PPS");
		else if (ar->type==GF_HEVC_NALU_VID_PARAM) print_config_hash(ar->nalus, "VPS");


}

void dump_vvc_track_info(GF_ISOFile *file, u32 trackNum, GF_VVCConfig *vvccfg
#if !defined(GPAC_DISABLE_AV_PARSERS)
	, VVCState *vvc_state
#endif /*GPAC_DISABLE_AV_PARSERS  && defined(GPAC_DISABLE_HEVC)*/
	)
{
#if !defined(GPAC_DISABLE_AV_PARSERS)
	u32 idx;
#endif
	u32 k;
	fprintf(stderr, "\tVVC Info:");

	fprintf(stderr, " Profile %d @ Level %d - Chroma Format %s\n", vvccfg->general_profile_idc, vvccfg->general_level_idc, vvccfg->chromaformat_plus_one ? gf_avc_hevc_get_chroma_format_name(vvccfg->chromaformat_plus_one-1) : "n/a");
	fprintf(stderr, "\n");
	fprintf(stderr, "\tNAL Unit length bits: %d", 8*vvccfg->nal_unit_size);
	if (vvccfg->general_constraint_info && vvccfg->num_constraint_info && vvccfg->general_constraint_info[0]) {
		fprintf(stderr, " - general constraint info 0x");
		for (idx=0; idx<vvccfg->num_constraint_info; idx++) {
			fprintf(stderr, "%02X", vvccfg->general_constraint_info[idx]);
		}
	}
	fprintf(stderr, "\n");
	fprintf(stderr, "\tParameter Sets: ");
	for (k=0; k<gf_list_count(vvccfg->param_array); k++) {
		GF_NALUFFParamArray *ar=gf_list_get(vvccfg->param_array, k);
		if (ar->type==GF_VVC_NALU_SEQ_PARAM) {
			fprintf(stderr, "%d SPS ", gf_list_count(ar->nalus));
		}
		if (ar->type==GF_VVC_NALU_PIC_PARAM) {
			fprintf(stderr, "%d PPS ", gf_list_count(ar->nalus));
		}
		if (ar->type==GF_VVC_NALU_VID_PARAM) {
			fprintf(stderr, "%d VPS ", gf_list_count(ar->nalus));

#if !defined(GPAC_DISABLE_AV_PARSERS) && 0 //TODO
			for (idx=0; idx<gf_list_count(ar->nalus); idx++) {
				GF_NALUFFParam *vps = gf_list_get(ar->nalus, idx);
				s32 ps_idx=gf_hevc_read_vps(vps->data, vps->size, hevc_state);
				if (hevccfg->is_lhvc && (ps_idx>=0)) {
					non_hevc_base_layer = ! hevc_state->vps[ps_idx].base_layer_internal_flag;
				}
			}
#endif

		}
	}

	fprintf(stderr, "\n");
#if !defined(GPAC_DISABLE_AV_PARSERS) && 0 //TODO
	for (k=0; k<gf_list_count(vvccfg->param_array); k++) {
		GF_NALUFFParamArray *ar=gf_list_get(vvccfg->param_array, k);
		u32 width, height;
		s32 par_n, par_d;

		if (ar->type !=GF_VVC_NALU_SEQ_PARAM) continue;
		for (idx=0; idx<gf_list_count(ar->nalus); idx++) {
			GF_Err e;
			GF_NALUFFParam *sps = gf_list_get(ar->nalus, idx);
			par_n = par_d = -1;
			e = gf_vvc_get_sps_info_with_state(vvc_state, sps->data, sps->size, NULL, &width, &height, &par_n, &par_d);
			if (e==GF_OK) {
				fprintf(stderr, "\tSPS resolution %dx%d", width, height);
				if ((par_n>0) && (par_d>0)) {
					u32 tw, th;
					gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL);
					fprintf(stderr, " - Pixel Aspect Ratio %d:%d - Indicated track size %d x %d", par_n, par_d, tw, th);
				}
				fprintf(stderr, "\n");
			} else {
				M4_LOG(GF_LOG_ERROR, ("\nFailed to read SPS: %s\n\n", gf_error_to_string(e) ));
			}
		}
	}
#endif
	fprintf(stderr, "\tBit Depth %d - %d temporal layers\n", vvccfg->bit_depth_plus_one-1, vvccfg->numTemporalLayers);

	for (k=0; k<gf_list_count(vvccfg->param_array); k++) {
		GF_NALUFFParamArray *ar=gf_list_get(vvccfg->param_array, k);
		if (ar->type==GF_VVC_NALU_SEQ_PARAM) print_config_hash(ar->nalus, "SPS");
		else if (ar->type==GF_VVC_NALU_PIC_PARAM) print_config_hash(ar->nalus, "PPS");
		else if (ar->type==GF_VVC_NALU_VID_PARAM) print_config_hash(ar->nalus, "VPS");

The DumpTrackInfo function in GPAC 1.0.1 allows attackers to cause a denial of service (NULL pointer dereference) via a crafted file in the MP4Box command.


GF_Err dump_isom_cover_art(GF_ISOFile *file, char *inName, Bool is_final_name)
{
	const u8 *tag;
	FILE *t;
	u32 tag_len;
	GF_Err e = gf_isom_apple_get_tag(file, GF_ISOM_ITUNE_COVER_ART, &tag, &tag_len);
	if (e!=GF_OK) {
		if (e==GF_URL_ERROR) {
			M4_LOG(GF_LOG_WARNING, ("No cover art found\n"));
			return GF_OK;
		}
		return e;
	}

	if (inName) {
		char szName[1024];
		if (is_final_name) {
			strcpy(szName, inName);
		} else {
			sprintf(szName, "%s.%s", inName, (tag_len>>31) ? "png" : "jpg");
		}
		t = gf_fopen(szName, "wb");
		if (!t) {
			M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szName));
			return GF_IO_ERR;
		}
	} else {
		t = stdout;
	}
	gf_fwrite(tag, tag_len & 0x7FFFFFFF, t);

	if (inName) gf_fclose(t);
	return GF_OK;
}


}

void dump_isom_saps(GF_ISOFile *file, GF_ISOTrackID trackID, u32 dump_saps_mode, char *inName, Bool is_final_name)
{
	FILE *dump;
	u32 i, count;
	s64 media_offset=0;
	u32 track = gf_isom_get_track_by_id(file, trackID);
	if (inName) {
		char szBuf[GF_MAX_PATH];
		strcpy(szBuf, inName);

		if (!is_final_name) sprintf(szBuf, "%s_%d_cues.xml", inName, trackID);
		dump = gf_fopen(szBuf, "wt");
		if (!dump) {
			M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szBuf));
			return;
		}
	} else {
		dump = stdout;
	}

	fprintf(dump, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
	fprintf(dump, "<DASHCues xmlns=\"urn:gpac:dash:schema:cues:2018\">\n");
	fprintf(dump, "<Stream id=\"%d\" timescale=\"%d\"", trackID, gf_isom_get_media_timescale(file, track) );
	if (dump_saps_mode==4) {
		fprintf(dump, " mode=\"edit\"");
		gf_isom_get_edit_list_type(file, track, &media_offset);
	}
	fprintf(dump, ">\n");

	count = gf_isom_get_sample_count(file, track);
	for (i=0; i<count; i++) {
		s64 cts, dts;
		u32 di;
		Bool traf_start = 0;
		u32 sap_type = 0;
		u64 doffset;

		GF_ISOSample *samp = gf_isom_get_sample_info(file, track, i+1, &di, &doffset);

		traf_start = gf_isom_sample_is_fragment_start(file, track, i+1, NULL);

		sap_type = samp->IsRAP;
		if (!sap_type) {
			Bool is_rap;
			GF_ISOSampleRollType roll_type;
			s32 roll_dist;
			gf_isom_get_sample_rap_roll_info(file, track, i+1, &is_rap, &roll_type, &roll_dist);
			if (roll_type) sap_type = SAP_TYPE_4;
			else if (is_rap)  sap_type = SAP_TYPE_3;
		}

		if (!sap_type) {
			gf_isom_sample_del(&samp);
			continue;
		}

		dts = cts = samp->DTS;
		cts += samp->CTS_Offset;
		fprintf(dump, "<Cue sap=\"%d\"", sap_type);
		if (dump_saps_mode==4) {
			cts += media_offset;
			fprintf(dump, " cts=\""LLD"\"", cts);
		} else {
			if (!dump_saps_mode || (dump_saps_mode==1)) fprintf(dump, " sample=\"%d\"", i+1);
			if (!dump_saps_mode || (dump_saps_mode==2)) fprintf(dump, " cts=\""LLD"\"", cts);
			if (!dump_saps_mode || (dump_saps_mode==3)) fprintf(dump, " dts=\""LLD"\"", dts);
		}

		if (traf_start)
			fprintf(dump, " wasFragStart=\"yes\"");

		fprintf(dump, "/>\n");

		gf_isom_sample_del(&samp);
	}
	fprintf(dump, "</Stream>\n");
	fprintf(dump, "</DASHCues>\n");


#include <gpac/mpd.h>

void get_file_callback(void *usr_cbk, GF_NETIO_Parameter *parameter)
{
	if (parameter->msg_type==GF_NETIO_DATA_EXCHANGE) {
		u64 tot_size, done, max;
		u32 bps;
		gf_dm_sess_get_stats(parameter->sess, NULL, NULL, &tot_size, &done, &bps, NULL);
		if (tot_size) {
			max = done;
			max *= 100;
			max /= tot_size;
			fprintf(stderr, "download %02d %% at %05d kpbs\r", (u32) max, bps*8/1000);
		}


#ifndef GPAC_DISABLE_ISOM_HINTING

void dump_isom_sdp(GF_ISOFile *file, char *inName, Bool is_final_name)
{
	const char *sdp;
	u32 size, i;
	FILE *dump;

	if (inName) {
		char szBuf[1024];
		strcpy(szBuf, inName);
		if (!is_final_name) {
			char *ext = strchr(szBuf, '.');
			if (ext) ext[0] = 0;
			strcat(szBuf, "_sdp.txt");
		}
		dump = gf_fopen(szBuf, "wt");
		if (!dump) {
			M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szBuf));
			return;
		}
	} else {
		dump = stdout;
		fprintf(dump, "# File SDP content \n\n");
	}
	//get the movie SDP
	gf_isom_sdp_get(file, &sdp, &size);
	if (sdp && size)
		fprintf(dump, "%s", sdp);
	fprintf(dump, "\r\n");

	//then tracks
	for (i=0; i<gf_isom_get_track_count(file); i++) {
		if (gf_isom_get_media_type(file, i+1) != GF_ISOM_MEDIA_HINT) continue;
		gf_isom_sdp_track_get(file, i+1, &sdp, &size);
		fprintf(dump, "%s", sdp);
	}
	fprintf(dump, "\n\n");


static void PrintFixed(Fixed val, Bool add_space)
{
	if (add_space) fprintf(stderr, " ");
	if (val==FIX_MIN) fprintf(stderr, "-I");
	else if (val==FIX_MAX) fprintf(stderr, "+I");
	else fprintf(stderr, "%g", FIX2FLT(val));
}


}

GF_Err dump_isom_udta(GF_ISOFile *file, char *inName, Bool is_final_name, u32 dump_udta_type, u32 dump_udta_track)
{
	u8 *data;
	FILE *t;
	bin128 uuid;
	u32 count, res;
	GF_Err e;

	memset(uuid, 0, 16);
	count = gf_isom_get_user_data_count(file, dump_udta_track, dump_udta_type, uuid);
	if (!count) {
		M4_LOG(GF_LOG_ERROR, ("No UDTA for type %s found\n", gf_4cc_to_str(dump_udta_type) ));
		return GF_NOT_FOUND;
	}

	data = NULL;
	count = 0;
	e = gf_isom_get_user_data(file, dump_udta_track, dump_udta_type, uuid, 0, &data, &count);
	if (e) {
		M4_LOG(GF_LOG_ERROR, ("Error dumping UDTA %s: %s\n", gf_4cc_to_str(dump_udta_type), gf_error_to_string(e) ));
		return e;
	}
	if (inName) {
		char szName[1024];
		if (is_final_name)
			strcpy(szName, inName);
		else
			sprintf(szName, "%s_%s.udta", inName, gf_4cc_to_str(dump_udta_type) );

		t = gf_fopen(szName, "wb");
		if (!t) {
			gf_free(data);
			M4_LOG(GF_LOG_ERROR, ("Cannot open file %s\n", szName ));
			return GF_IO_ERR;
		}
	} else {
		t = stdout;
	}
	res = (u32) gf_fwrite(data+8, count-8, t);
	if (inName) gf_fclose(t);
	gf_free(data);
	if (count-8 != res) {
		M4_LOG(GF_LOG_ERROR, ("Error writing udta to file\n"));
		return GF_IO_ERR;
	}


}

GF_Err rip_mpd(const char *mpd_src, const char *output_dir)
{
	GF_DownloadSession *sess;
	u32 i, connect_time, reply_time, download_time, req_hdr_size, rsp_hdr_size;
	GF_Err e;
	GF_DOMParser *mpd_parser=NULL;
	GF_MPD *mpd=NULL;
	GF_MPD_Period *period;
	GF_MPD_AdaptationSet *as;
	GF_MPD_Representation *rep;
	char szName[GF_MAX_PATH];
	GF_DownloadManager *dm;

	if (output_dir) {
		char *sep;
		strcpy(szName, output_dir);
		sep = gf_file_basename(szName);
		if (sep) sep[0] = 0;
		gf_opts_set_key("temp", "cache", szName);
	} else {
		gf_opts_set_key("temp", "cache", ".");
	}
	gf_opts_set_key("temp", "clean-cache", "true");
	dm = gf_dm_new(NULL);

/*
	char *name = strrchr(mpd_src, '/');
	if (!name) name = strrchr(mpd_src, '\\');
	if (!name) name = "manifest.mpd";
	else name ++;

	if (strchr(name, '?') || strchr(name, '&')) name = "manifest.mpd";
*/

	fprintf(stderr, "Downloading %s\n", mpd_src);
	sess = get_file(mpd_src, dm, &e);
	if (!sess) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error downloading MPD file %s: %s\n", mpd_src, gf_error_to_string(e) ));
		goto err_exit;
	}
	strcpy(szName, gf_dm_sess_get_cache_name(sess) );
	gf_dm_sess_get_header_sizes_and_times(sess, &req_hdr_size, &rsp_hdr_size, &connect_time, &reply_time, &download_time);
	gf_dm_sess_del(sess);

	if (e) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error fetching MPD file %s: %s\n", mpd_src, gf_error_to_string(e)));
		goto err_exit;
	}
	else {
		GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("Fetched file %s\n", mpd_src));
	}
	GF_LOG(GF_LOG_DEBUG, GF_LOG_APP, ("GET Header size %d - Reply header size %d\n", req_hdr_size, rsp_hdr_size));
	GF_LOG(GF_LOG_DEBUG, GF_LOG_APP, ("GET time: Connect Time %d - Reply Time %d - Download Time %d\n", connect_time, reply_time, download_time));

	mpd_parser = gf_xml_dom_new();
	e = gf_xml_dom_parse(mpd_parser, szName, NULL, NULL);

	if (e != GF_OK) {
		gf_xml_dom_del(mpd_parser);
		GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error parsing MPD %s : %s\n", mpd_src, gf_error_to_string(e)));
		return e;
	}
	mpd = gf_mpd_new();
	e = gf_mpd_init_from_dom(gf_xml_dom_get_root(mpd_parser), mpd, mpd_src);
	gf_xml_dom_del(mpd_parser);
	mpd_parser=NULL;
	if (e) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error initializing MPD %s : %s\n", mpd_src, gf_error_to_string(e)));
		goto err_exit;
	}
	else {
		GF_LOG(GF_LOG_DEBUG, GF_LOG_APP, ("MPD %s initialized: %s\n", szName, gf_error_to_string(e)));
	}

	revert_cache_file(szName);
	if (mpd->type==GF_MPD_TYPE_DYNAMIC) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("MPD rip is not supported on live sources\n"));
		e = GF_NOT_SUPPORTED;
		goto err_exit;
	}

	i=0;
	while ((period = (GF_MPD_Period *) gf_list_enum(mpd->periods, &i))) {
		char *initTemplate = NULL;
		Bool segment_base = GF_FALSE;
		u32 j=0;

		if (period->segment_base) segment_base=GF_TRUE;

		if (period->segment_template && period->segment_template->initialization) {
			initTemplate = period->segment_template->initialization;
		}

		while ((as = gf_list_enum(period->adaptation_sets, &j))) {
			u32 k=0;
			if (!initTemplate && as->segment_template && as->segment_template->initialization) {
				initTemplate = as->segment_template->initialization;
			}
			if (as->segment_base) segment_base=GF_TRUE;

			while ((rep = gf_list_enum(as->representations, &k))) {
				u64 out_range_start, out_range_end, segment_duration;
				Bool is_in_base_url;
				char *seg_url;
				u32 seg_idx=0;
				if (rep->segment_template && rep->segment_template->initialization) {
					initTemplate = rep->segment_template->initialization;
				} else if (k>1) {
					initTemplate = NULL;
				}
				if (rep->segment_base) segment_base=GF_TRUE;

				e = gf_mpd_resolve_url(mpd, rep, as, period, mpd_src, 0, GF_MPD_RESOLVE_URL_INIT, 0, 0, &seg_url, &out_range_start, &out_range_end, &segment_duration, &is_in_base_url, NULL, NULL, NULL);
				if (e) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error resolving init segment name : %s\n", gf_error_to_string(e)));
					continue;
				}
				//not a byte range, replace URL
				if (segment_base) {

				} else if (out_range_start || out_range_end || !seg_url) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("byte range rip not yet implemented\n"));
					if (seg_url) gf_free(seg_url);
					e = GF_NOT_SUPPORTED;
					goto err_exit;
				}

				fprintf(stderr, "Downloading %s\n", seg_url);
				sess = get_file(seg_url, dm, &e);
				if (e) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error downloading init segment %s from MPD %s : %s\n", seg_url, mpd_src, gf_error_to_string(e)));
					goto err_exit;
				}
				revert_cache_file((char *) gf_dm_sess_get_cache_name(sess) );
				gf_free(seg_url);
				gf_dm_sess_del(sess);

				if (segment_base) continue;

				while (1) {
					e = gf_mpd_resolve_url(mpd, rep, as, period, mpd_src, 0, GF_MPD_RESOLVE_URL_MEDIA, seg_idx, 0, &seg_url, &out_range_start, &out_range_end, &segment_duration, NULL, NULL, NULL, NULL);
					if (e) {
						if (e<0) {
							GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error resolving segment name : %s\n", gf_error_to_string(e)));
						}
						break;
					}

					seg_idx++;

					if (out_range_start || out_range_end || !seg_url) {
						GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("byte range rip not yet implemented\n"));
						if (seg_url) gf_free(seg_url);
						break;
					}
					fprintf(stderr, "Downloading %s\n", seg_url);
					sess = get_file(seg_url, dm, &e);
					if (e) {
						gf_free(seg_url);
						if (e != GF_URL_ERROR) {
							GF_LOG(GF_LOG_ERROR, GF_LOG_APP, ("Error downloading segment %s: %s\n", seg_url, gf_error_to_string(e)));
						} else {
							//todo, properly detect end of dash representation
							e = GF_OK;
						}
						break;
					}
					revert_cache_file((char *) gf_dm_sess_get_cache_name(sess) );
					gf_free(seg_url);
					gf_dm_sess_del(sess);
				}
			}
		}
	}

err_exit:
	if (mpd) gf_mpd_del(mpd);
	gf_dm_del(dm);


}

static void DumpMetaItem(GF_ISOFile *file, Bool root_meta, u32 tk_num, char *name)
{
	char szInd[2];
	u32 i, count, primary_id;
	u32 meta_type = gf_isom_get_meta_type(file, root_meta, tk_num);
	if (name[0]=='\t') {
		szInd[0] = '\t';
		szInd[1] = 0;
	} else {
		szInd[0] = 0;
	}

	count = gf_isom_get_meta_item_count(file, root_meta, tk_num);
	primary_id = gf_isom_get_meta_primary_item_id(file, root_meta, tk_num);
	fprintf(stderr, "%s type: \"%s\" - %d resource item(s)\n", name, meta_type ? gf_4cc_to_str(meta_type) : "undefined", (count+(primary_id>0)));
	switch (gf_isom_has_meta_xml(file, root_meta, tk_num)) {
	case 1:
		fprintf(stderr, "%sMeta has XML resource\n", szInd);
		break;
	case 2:
		fprintf(stderr, "%sMeta has BinaryXML resource\n", szInd);
		break;
	}
	if (primary_id) {
		fprintf(stderr, "%sPrimary Item - ID %d\n", szInd, primary_id);
	}
	for (i=0; i<count; i++) {
		const char *it_name, *mime, *enc, *url, *urn;
		Bool self_ref;
		u32 ID;
		u32 it_type, cenc_scheme, cenc_version;
		GF_Err e = gf_isom_get_meta_item_info(file, root_meta, tk_num, i+1, &ID, &it_type, &cenc_scheme, &cenc_version, &self_ref, &it_name, &mime, &enc, &url, &urn);
		if (e) {
			fprintf(stderr, "%sItem #%d fetch info error: %s\n", szInd, i+1, gf_error_to_string(e) );
			continue;
		}
		fprintf(stderr, "%sItem #%d: ID %d type %s", szInd, i+1, ID, gf_4cc_to_str(it_type));
		if (self_ref) fprintf(stderr, " Self-Reference");
		else if (it_name && it_name[0]) fprintf(stderr, " Name \"%s\"", it_name);
		if (mime) fprintf(stderr, " MIME: \"%s\"", mime);
		if (enc) fprintf(stderr, " ContentEncoding: \"%s\"", enc);

		if (meta_type == GF_META_ITEM_TYPE_PICT) {
			GF_ImageItemProperties img_props;
			e = gf_isom_get_meta_image_props(file, root_meta, tk_num, ID, &img_props);
			if (e) {
				fprintf(stderr, " invalid image properties !");
			} else {
				u32 j;
				Bool chan_diff = 0;
				if (img_props.width && img_props.height) {
					fprintf(stderr, " size %ux%u", img_props.width, img_props.height);
				}
				if (img_props.hSpacing && img_props.vSpacing) {
					fprintf(stderr, " SAR %u/%u", img_props.hSpacing, img_props.vSpacing);
				}
				if (img_props.num_channels) {
					fprintf(stderr, " %d channel%s (", img_props.num_channels, (img_props.num_channels>1) ? "s" : "");
					for (j=1; j<img_props.num_channels; j++) {
						if (img_props.bits_per_channel[0] != img_props.bits_per_channel[j])
							chan_diff = 1;
					}
					if (chan_diff) {
						for (j=0; j<img_props.num_channels; j++) {
							if (j) fprintf(stderr, ",");
							fprintf(stderr, "%d", img_props.bits_per_channel[j]);
						}
					} else {
						fprintf(stderr, "%d", img_props.bits_per_channel[0]);
					}
					fprintf(stderr, " bpc)");
				}
				if (img_props.hOffset || img_props.vOffset)
					fprintf(stderr, " Offset %ux%u", img_props.hOffset, img_props.vOffset);
				if (img_props.alpha) fprintf(stderr, " Alpha");
				if (img_props.hidden) fprintf(stderr, " Hidden");
				if (img_props.angle) fprintf(stderr, " Rotate %d", img_props.angle);
				if (img_props.mirror) fprintf(stderr, " Mirror %d", img_props.mirror);
				if (img_props.clap_hden || img_props.clap_wden)
					fprintf(stderr, " Clap %d/%d,%d/%d,%d/%d,%d/%d", img_props.clap_wnum, img_props.clap_wden, img_props.clap_hnum, img_props.clap_hden, img_props.clap_honum, img_props.clap_hoden, img_props.clap_vonum, img_props.clap_voden);
			}
		}

		if (cenc_scheme) {
			Bool is_protected;
			u8 skip_byte_block, crypt_byte_block;
			const u8 *key_info;
			u32 key_info_size;
			fprintf(stderr, " - Protection scheme: %s v0x%08X", gf_4cc_to_str(cenc_scheme), cenc_version);

			gf_isom_extract_meta_item_get_cenc_info(file, root_meta, tk_num, ID, &is_protected, &skip_byte_block, &crypt_byte_block, &key_info, &key_info_size, NULL, NULL, NULL, NULL);
			if (skip_byte_block && crypt_byte_block)
				fprintf(stderr, " - Pattern %d:%d", skip_byte_block, crypt_byte_block);
			fprintf(stderr, "\n");
			dump_key_info(key_info, key_info_size, is_protected);
		}
		fprintf(stderr, "\n");
		if (url) fprintf(stderr, "%sURL: %s\n", szInd, url);
		if (urn) fprintf(stderr, "%sURN: %s\n", szInd, urn);


void dump_isom_scene_stats(char *file, char *inName, Bool is_final_name, u32 stat_level)
{
	GF_Err e;
	FILE *dump;
	Bool close;
	u32 i, j, count;
	char szBuf[1024];
	GF_SceneManager *ctx;
	GF_SceneLoader load;
	GF_StatManager *sm;
	GF_List *sample_list;
	GF_SceneGraph *scene_graph;

	dump = NULL;
	sm = NULL;
	sample_list = NULL;

	close = 0;

	scene_graph = gf_sg_new();
	ctx = gf_sm_new(scene_graph);
	memset(&load, 0, sizeof(GF_SceneLoader));
	load.fileName = file;
	load.ctx = ctx;

	if (get_file_type_by_ext(file) == 1) {
		load.isom = gf_isom_open(file, GF_ISOM_OPEN_READ, NULL);
		if (!load.isom) {
			M4_LOG(GF_LOG_ERROR, ("Cannot open file: %s\n", gf_error_to_string(gf_isom_last_error(NULL))));
			gf_sm_del(ctx);
			gf_sg_del(scene_graph);
			return;
		}
	}

	e = gf_sm_load_init(&load);
	if (!e) e = gf_sm_load_run(&load);
	gf_sm_load_done(&load);
	if (e<0) goto exit;

	if (inName) {
		strcpy(szBuf, inName);
		if (!is_final_name) strcat(szBuf, "_stat.xml");
		dump = gf_fopen(szBuf, "wt");
		if (!dump) {
			M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szBuf));
			return;
		}
		close = 1;
	} else {
		dump = stdout;
		close = 0;
	}

	fprintf(stderr, "Analysing Scene\n");

	fprintf(dump, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
	fprintf(dump, "<!-- Scene Graph Statistics Generated by MP4Box - GPAC ");
	if (! gf_sys_is_test_mode())
		fprintf(dump, "%s ",  gf_gpac_version());
	fprintf(dump, "-->\n");

	fprintf(dump, "<SceneStatistics file=\"%s\" DumpType=\"%s\">\n", gf_file_basename(file), (stat_level==1) ? "full scene" : ((stat_level==2) ? "AccessUnit based" : "SceneGraph after each AU"));

	sm = gf_sm_stats_new();

	/*stat level 1: complete scene stat*/
	if (stat_level == 1) {
		e = gf_sm_stats_for_scene(sm, ctx);
		if (!e) dump_stats(dump, gf_sm_stats_get(sm) );
		goto exit;
	}
	/*re_order all BIFS-AUs*/
	sample_list = gf_list_new();
	/*configure all systems streams we're dumping*/
	for (i=0; i<gf_list_count(ctx->streams); i++) {
		GF_StreamContext *sc = gf_list_get(ctx->streams, i);
		if (sc->streamType != GF_STREAM_SCENE) continue;
		for (j=0; j<gf_list_count(sc->AUs); j++) {
			GF_AUContext *au = gf_list_get(sc->AUs, j);
			ReorderAU(sample_list, au);
		}
	}

	count = gf_list_count(sample_list);
	for (i=0; i<count; i++) {
		GF_AUContext *au = gf_list_get(sample_list, i);

		for (j=0; j<gf_list_count(au->commands); j++) {
			GF_Command *com = gf_list_get(au->commands, j);
			/*stat level 2 - get command stats*/
			if (stat_level==2) {
				e = gf_sm_stats_for_command(sm, com);
				if (e) goto exit;
			}
			/*stat level 3 - apply command*/
			if (stat_level==3) gf_sg_command_apply(scene_graph, com, 0);
		}
		/*stat level 3: get graph stat*/
		if (stat_level==3) {
			e = gf_sm_stats_for_graph(sm, scene_graph);
			if (e) goto exit;
		}
		if (stat_level==2) {
			fprintf(dump, "<AUStatistics StreamID=\"%d\" AUTime=\""LLD"\">\n", au->owner->ESID, au->timing);
		} else {
			fprintf(dump, "<GraphStatistics StreamID=\"%d\" AUTime=\""LLD"\">\n", au->owner->ESID, au->timing);
		}
		/*dump stats*/
		dump_stats(dump, gf_sm_stats_get(sm) );
		/*reset stats*/
		gf_sm_stats_reset(sm);
		if (stat_level==2) {
			fprintf(dump, "</AUStatistics>\n");
		} else {
			fprintf(dump, "</GraphStatistics>\n");
		}

		gf_set_progress("Analysing AU", i+1, count);
	}

exit:
	if (sample_list) gf_list_del(sample_list);
	if (sm) gf_sm_stats_del(sm);
	gf_sm_del(ctx);
	gf_sg_del(scene_graph);
	if (load.isom) gf_isom_delete(load.isom);
	if (e) {
		M4_LOG(GF_LOG_ERROR, ("Stats error: %s\n", gf_error_to_string(e)));
	} else {
		fprintf(dump, "</SceneStatistics>\n");
	}
	if (dump && close) gf_fclose(dump);
	fprintf(stderr, "done\n");
}


static void dump_key_info(const u8 *key_info, u32 key_info_size, Bool is_protected)
{
	if (!key_info) return;
	u32 j, k, kpos=3;
	u32 nb_keys = 1;
	if (key_info[0]) {
		nb_keys = key_info[1];
		nb_keys <<= 8;
		nb_keys |= key_info[2];
	}
	for (k=0; k<nb_keys; k++) {
		u8 constant_iv_size=0;
		u8 iv_size=key_info[kpos+1];
		fprintf(stderr, "\t\tKID");
		if (nb_keys>1)
			fprintf(stderr, "%d", k+1);
		fprintf(stderr, " ");
		for (j=0; j<16; j++) fprintf(stderr, "%02X", key_info[kpos+1+j]);
		kpos+=17;
		if (!iv_size && is_protected) {
			constant_iv_size = key_info[1];
			kpos += 1 + constant_iv_size;
		}
		fprintf(stderr, " - %sIV size %d \n", constant_iv_size ? "const " : "", constant_iv_size ? constant_iv_size : iv_size);


#ifndef GPAC_DISABLE_ISOM_DUMP

GF_Err dump_isom_xml(GF_ISOFile *file, char *inName, Bool is_final_name, Bool do_track_dump, Bool merge_vtt_cues, Bool skip_init, Bool skip_samples)
{
	GF_Err e;
	FILE *dump = stdout;
	Bool do_close=GF_FALSE;
	if (!file) return GF_ISOM_INVALID_FILE;

	if (inName) {
		char szBuf[1024];
		strcpy(szBuf, inName);
		if (!is_final_name) {
			strcat(szBuf, do_track_dump ? "_dump.xml" : "_info.xml");
		}
		dump = gf_fopen(szBuf, "wt");
		if (!dump) {
			M4_LOG(GF_LOG_ERROR, ("Failed to open %s\n", szBuf));
			return GF_IO_ERR;
		}
		do_close=GF_TRUE;
	}

	fprintf(dump, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
	if (do_track_dump) {
		fprintf(dump, "<ISOBaseMediaFileTrace>\n");
	}
	e = gf_isom_dump(file, dump, skip_init, skip_samples);
	if (e) {
		M4_LOG(GF_LOG_ERROR, ("Error dumping ISO structure\n"));
	}

	if (do_track_dump) {
#ifndef GPAC_DISABLE_MEDIA_EXPORT
		u32 i;
		//because of dump mode we need to reopen in regular read mode to avoid mem leaks
		GF_ISOFile *the_file = gf_isom_open(gf_isom_get_filename(file), GF_ISOM_OPEN_READ, NULL);
		u32 tcount = gf_isom_get_track_count(the_file);
		fprintf(dump, "<Tracks>\n");

		for (i=0; i<tcount; i++) {
			GF_MediaExporter dumper;
			GF_ISOTrackID trackID = gf_isom_get_track_id(the_file, i+1);
			u32 mtype = gf_isom_get_media_type(the_file, i+1);
			u32 msubtype = gf_isom_get_media_subtype(the_file, i+1, 1);
			Bool fmt_handled = GF_FALSE;
			memset(&dumper, 0, sizeof(GF_MediaExporter));
			dumper.file = the_file;
			dumper.trackID = trackID;
			dumper.dump_file = dump;

			if (mtype == GF_ISOM_MEDIA_HINT) {
#ifndef GPAC_DISABLE_ISOM_HINTING
				char *name=NULL;
				if (msubtype==GF_ISOM_SUBTYPE_RTP) name = "RTPHintTrack";
				else if (msubtype==GF_ISOM_SUBTYPE_SRTP) name = "SRTPHintTrack";
				else if (msubtype==GF_ISOM_SUBTYPE_RRTP) name = "RTPReceptionHintTrack";
				else if (msubtype==GF_ISOM_SUBTYPE_RTCP) name = "RTCPReceptionHintTrack";
				else if (msubtype==GF_ISOM_SUBTYPE_FLUTE) name = "FLUTEReceptionHintTrack";
				else name = "UnknownHintTrack";

				fprintf(dump, "<%s trackID=\"%d\">\n", name, trackID);

#ifndef GPAC_DISABLE_ISOM_HINTING
				u32 j, scount=gf_isom_get_sample_count(the_file, i+1);
				for (j=0; j<scount; j++) {
					gf_isom_dump_hint_sample(the_file, i+1, j+1, dump);
				}
#endif
				fprintf(dump, "</%s>\n", name);
				fmt_handled = GF_TRUE;
#endif /*GPAC_DISABLE_ISOM_HINTING*/
			}
			else if (gf_isom_get_avc_svc_type(the_file, i+1, 1) || gf_isom_get_hevc_lhvc_type(the_file, i+1, 1)) {
				dump_isom_nal_ex(the_file, trackID, dump, GF_FALSE);
				fmt_handled = GF_TRUE;
			} else if ((mtype==GF_ISOM_MEDIA_TEXT) || (mtype==GF_ISOM_MEDIA_SUBT) ) {

				if (msubtype==GF_ISOM_SUBTYPE_WVTT) {
					gf_webvtt_dump_iso_track(&dumper, i+1, merge_vtt_cues, GF_TRUE);
					fmt_handled = GF_TRUE;
				} else if ((msubtype==GF_ISOM_SUBTYPE_TX3G) || (msubtype==GF_ISOM_SUBTYPE_TEXT)) {
					gf_isom_text_dump(the_file, i+1, dump, GF_TEXTDUMPTYPE_TTXT_BOXES);
					fmt_handled = GF_TRUE;
				}
			}

			if (!fmt_handled) {
				dumper.flags = GF_EXPORT_NHML | GF_EXPORT_NHML_FULL;
				dumper.print_stats_graph = fs_dump_flags;
				gf_media_export(&dumper);
			}
		}
#else
		return GF_NOT_SUPPORTED;
#endif /*GPAC_DISABLE_MEDIA_EXPORT*/
		gf_isom_delete(the_file);
		fprintf(dump, "</Tracks>\n");
		fprintf(dump, "</ISOBaseMediaFileTrace>\n");
	}
	if (do_close) gf_fclose(dump);


#endif

static void dump_isom_obu(GF_ISOFile *file, GF_ISOTrackID trackID, FILE *dump, Bool dump_crc)
{
#ifndef GPAC_DISABLE_AV_PARSERS
	u32 i, count, track, timescale;
	AV1State av1;
	ObuType obu_type;
	u64 obu_size;
	u32 hdr_size;
	GF_BitStream *bs;
	u32 idx;

	track = gf_isom_get_track_by_id(file, trackID);

	gf_av1_init_state(&av1);
	av1.config = gf_isom_av1_config_get(file, track, 1);
	if (!av1.config) {
		M4_LOG(GF_LOG_ERROR, ("Error: Track #%d is not AV1!\n", trackID));
		return;
	}

	count = gf_isom_get_sample_count(file, track);
	timescale = gf_isom_get_media_timescale(file, track);

	fprintf(dump, "<OBUTrack trackID=\"%d\" SampleCount=\"%d\" TimeScale=\"%d\">\n", trackID, count, timescale);

	fprintf(dump, " <OBUConfig>\n");

	for (i=0; i<gf_list_count(av1.config->obu_array); i++) {
		GF_AV1_OBUArrayEntry *obu = gf_list_get(av1.config->obu_array, i);
		bs = gf_bs_new(obu->obu, (u32) obu->obu_length, GF_BITSTREAM_READ);
		gf_av1_parse_obu(bs, &obu_type, &obu_size, &hdr_size, &av1);
		gf_inspect_dump_obu(dump, &av1, obu->obu, obu->obu_length, obu_type, obu_size, hdr_size, dump_crc);
		gf_bs_del(bs);
	}
	fprintf(dump, " </OBUConfig>\n");

	fprintf(dump, " <OBUSamples>\n");

	for (i=0; i<count; i++) {
		u64 dts, cts;
		u32 size;
		u8 *ptr;
		GF_ISOSample *samp = gf_isom_get_sample(file, track, i+1, NULL);
		if (!samp) {
			fprintf(dump, "<!-- Unable to fetch sample %d -->\n", i+1);
			continue;
		}
		dts = samp->DTS;
		cts = dts + (s32) samp->CTS_Offset;

		fprintf(dump, "  <Sample number=\"%d\" DTS=\""LLD"\" CTS=\""LLD"\" size=\"%d\" RAP=\"%d\" >\n", i+1, dts, cts, samp->dataLength, samp->IsRAP);
		if (cts<dts) fprintf(dump, "<!-- NEGATIVE CTS OFFSET! -->\n");

		idx = 1;
		ptr = samp->data;
		size = samp->dataLength;

		bs = gf_bs_new(ptr, size, GF_BITSTREAM_READ);
		while (size) {
			gf_av1_parse_obu(bs, &obu_type, &obu_size, &hdr_size, &av1);
			if (obu_size > size) {
				fprintf(dump, "   <!-- OBU number %d is corrupted: size is %d but only %d remains -->\n", idx, (u32) obu_size, size);
				break;
			}
			gf_inspect_dump_obu(dump, &av1, ptr, obu_size, obu_type, obu_size, hdr_size, dump_crc);
			ptr += obu_size;
			size -= (u32)obu_size;
			idx++;
		}
		gf_bs_del(bs);
		fprintf(dump, "  </Sample>\n");
		gf_isom_sample_del(&samp);

		fprintf(dump, "\n");
		gf_set_progress("Analysing Track OBUs", i+1, count);
	}
	fprintf(dump, " </OBUSamples>\n");
	fprintf(dump, "</OBUTrack>\n");

	if (av1.config) gf_odf_av1_cfg_del(av1.config);
	gf_av1_reset_state(&av1, GF_TRUE);


GF_Err dump_isom_chapters(GF_ISOFile *file, char *inName, Bool is_final_name, u32 dump_mode)
{
	FILE *t;
	u32 i, count;
	u32 chap_tk = 0;
	count = gf_isom_get_chapter_count(file, 0);

	if (dump_mode==2) dump_mode = GF_TEXTDUMPTYPE_OGG_CHAP;
	else if (dump_mode==3) dump_mode = GF_TEXTDUMPTYPE_ZOOM_CHAP;
	else dump_mode = GF_TEXTDUMPTYPE_TTXT_CHAP;

	if (!count) {
		for (i=0; i<gf_isom_get_track_count(file); i++) {
			if (gf_isom_get_reference_count(file, i+1, GF_ISOM_REF_CHAP)) {
				GF_Err e = gf_isom_get_reference(file, i+1, GF_ISOM_REF_CHAP, 1, &chap_tk);
				if (!e) break;
			}
		}
		if (!chap_tk) {
			M4_LOG(GF_LOG_WARNING, ("No chapters or chapters track found in file\n"));
			return GF_OK;
		}

		fprintf(stderr, "Dumping chapter track %d\n", chap_tk);
		dump_isom_timed_text(file, gf_isom_get_track_id(file, chap_tk), inName, is_final_name, GF_FALSE, dump_mode);
		return GF_OK;

	}
	if (inName) {
		char szName[1024];
		strcpy(szName, inName);
		if (!is_final_name) {
			if (dump_mode==GF_TEXTDUMPTYPE_OGG_CHAP) {
				strcat(szName, ".txt");
			} else if (dump_mode==GF_TEXTDUMPTYPE_ZOOM_CHAP) {
				strcat(szName, ".txt");
			} else {
				strcat(szName, ".ttxt");
			}
		}
		t = gf_fopen(szName, "wt");
		if (!t) return GF_IO_ERR;
	} else {
		t = stdout;
	}

	if (dump_mode==GF_TEXTDUMPTYPE_TTXT_CHAP) {
		fprintf(t, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
		fprintf(t, "<TextStream version=\"1.1\">\n");
		fprintf(t, "<TextStreamHeader width=\"0\" height=\"0\" layer=\"0\" translation_x=\"0\" translation_y=\"0\">\n");
		fprintf(t, "<TextSampleDescription horizontalJustification=\"left\" backColor=\"0 0 0\" scroll=\"None\"/>\n");
		fprintf(t, "</TextStreamHeader>\n");
	}

	for (i=0; i<count; i++) {
		char szDur[20];
		u64 chapter_time;
		const char *name;
		gf_isom_get_chapter(file, 0, i+1, &chapter_time, &name);
		if (dump_mode==GF_TEXTDUMPTYPE_OGG_CHAP) {
			fprintf(t, "CHAPTER%02d=%s\n", i+1, format_duration(chapter_time, 1000, szDur));
			fprintf(t, "CHAPTER%02dNAME=%s\n", i+1, name);
		} else if (dump_mode==GF_TEXTDUMPTYPE_ZOOM_CHAP) {
			chapter_time /= 1000;
			fprintf(t, "AddChapterBySecond("LLD",%s)\n", chapter_time, name);
		} else {
			fprintf(t, "<TextSample sampleTime=\"%s\" sampleDescriptionIndex=\"1\" xml:space=\"preserve\">%s</TextSample>\n"
				, format_duration(chapter_time, 1000, szDur), name);
		}
	}
	if (dump_mode==GF_TEXTDUMPTYPE_TTXT_CHAP) {
		fprintf(t, "</TextStream>\n");
	}
	if (inName) gf_fclose(t);


static u32 read_nal_size_hdr(u8 *ptr, u32 nalh_size)
{
	u32 nal_size=0;
	u32 v = nalh_size;
	while (v) {
		nal_size |= (u8) *ptr;
		ptr++;
		v-=1;
		if (v) nal_size <<= 8;
	}
	return nal_size;
}


static void dump_isom_nal_ex(GF_ISOFile *file, GF_ISOTrackID trackID, FILE *dump, u32 dump_flags)
{
	u32 i, j, count, nb_descs, track, nalh_size, timescale, cur_extract_mode;
	s32 countRef;
	Bool is_adobe_protected = GF_FALSE;
	Bool is_cenc_protected = GF_FALSE;
	Bool is_hevc = GF_FALSE;
	Bool is_vvc = GF_FALSE;
#ifndef GPAC_DISABLE_AV_PARSERS
	AVCState *avc_state = NULL;
	HEVCState *hevc_state = NULL;
	VVCState *vvc_state = NULL;
#endif
	GF_AVCConfig *avccfg, *svccfg;
	GF_HEVCConfig *hevccfg, *lhvccfg;
	GF_VVCConfig *vvccfg;
	GF_NALUFFParam *slc;
	Bool has_svcc = GF_FALSE;

	track = gf_isom_get_track_by_id(file, trackID);

	count = gf_isom_get_sample_count(file, track);

	timescale = gf_isom_get_media_timescale(file, track);

	cur_extract_mode = gf_isom_get_nalu_extract_mode(file, track);

	nb_descs = gf_isom_get_sample_description_count(file, track);
	if (!nb_descs) {
		M4_LOG(GF_LOG_ERROR, ("Error: Track #%d has no sample description so is likely not NALU-based!\n", trackID));
		return;
	}

	fprintf(dump, "<NALUTrack trackID=\"%d\" SampleCount=\"%d\" TimeScale=\"%d\">\n", trackID, count, timescale);

#ifndef GPAC_DISABLE_AV_PARSERS

#define DUMP_ARRAY(arr, name, loc, _is_svc)\
	if (arr) {\
		fprintf(dump, "  <%sArray location=\"%s\">\n", name, loc);\
		for (i=0; i<gf_list_count(arr); i++) {\
			slc = gf_list_get(arr, i);\
			fprintf(dump, "   <NALU size=\"%d\" ", slc->size);\
			gf_inspect_dump_nalu(dump, (u8 *) slc->data, slc->size, _is_svc, is_hevc ? hevc_state : NULL, avc_state, is_vvc ? vvc_state : NULL, nalh_size, (dump_flags&1) ? GF_TRUE : GF_FALSE, GF_FALSE);\
		}\
		fprintf(dump, "  </%sArray>\n", name);\
	}\

#else

#define DUMP_ARRAY(arr, name, loc, _is_svc)\
	if (arr) {\
		fprintf(dump, "  <%sArray location=\"%s\">\n", name, loc);\
		for (i=0; i<gf_list_count(arr); i++) {\
			slc = gf_list_get(arr, i);\
			fprintf(dump, "   <NALU size=\"%d\" ", slc->size);\
			fprintf(dump, "/>\n");\
		}\
		fprintf(dump, "  </%sArray>\n", name);\
	}\

#endif

	nalh_size = 0;

	for (j=0; j<nb_descs; j++) {
		GF_AVCConfig *mvccfg;
		Bool is_svc;

		avccfg = gf_isom_avc_config_get(file, track, j+1);
		svccfg = gf_isom_svc_config_get(file, track, j+1);
		mvccfg = gf_isom_mvc_config_get(file, track, j+1);
		hevccfg = gf_isom_hevc_config_get(file, track, j+1);
		lhvccfg = gf_isom_lhvc_config_get(file, track, j+1);
		vvccfg = gf_isom_vvc_config_get(file, track, j+1);

		is_svc = (svccfg!=NULL) ? 1:0;

		if (hevccfg || lhvccfg) {
			is_hevc = 1;
#ifndef GPAC_DISABLE_AV_PARSERS
			GF_SAFEALLOC(hevc_state, HEVCState)
#endif
		} else if (vvccfg) {
			is_vvc = 1;
#ifndef GPAC_DISABLE_AV_PARSERS
			GF_SAFEALLOC(vvc_state, VVCState)
#endif
		} else if (avccfg || svccfg || mvccfg) {
#ifndef GPAC_DISABLE_AV_PARSERS
			GF_SAFEALLOC(avc_state, AVCState)
#endif
		}

		//for tile tracks the hvcC is stored in the 'tbas' track
		if (!hevccfg && gf_isom_get_reference_count(file, track, GF_ISOM_REF_TBAS)) {
			u32 tk = 0;
			gf_isom_get_reference(file, track, GF_ISOM_REF_TBAS, 1, &tk);
			hevccfg = gf_isom_hevc_config_get(file, tk, 1);
		}

		fprintf(dump, " <NALUConfig>\n");

		if (!avccfg && !svccfg && !hevccfg && !lhvccfg && !vvccfg) {
			M4_LOG(GF_LOG_ERROR, ("Error: Track #%d is not NALU or OBU based!\n", trackID));
			return;
		}

		if (avccfg) {
			nalh_size = avccfg->nal_unit_size;

			DUMP_ARRAY(avccfg->sequenceParameterSets, "AVCSPS", "avcC", is_svc);
			DUMP_ARRAY(avccfg->pictureParameterSets, "AVCPPS", "avcC", is_svc)
			DUMP_ARRAY(avccfg->sequenceParameterSetExtensions, "AVCSPSEx", "avcC", is_svc)
		}
		if (is_svc) {
			if (!nalh_size) nalh_size = svccfg->nal_unit_size;
			DUMP_ARRAY(svccfg->sequenceParameterSets, "SVCSPS", "svcC", is_svc)
			DUMP_ARRAY(svccfg->pictureParameterSets, "SVCPPS", "svcC", is_svc)
		}
		if (mvccfg) {
			if (!nalh_size) nalh_size = mvccfg->nal_unit_size;
			DUMP_ARRAY(mvccfg->sequenceParameterSets, "SVCSPS", "mvcC", is_svc)
			DUMP_ARRAY(mvccfg->pictureParameterSets, "SVCPPS", "mvcC", is_svc)
		}
		if (hevccfg) {
			u32 idx;
			nalh_size = hevccfg->nal_unit_size;
			for (idx=0; idx<gf_list_count(hevccfg->param_array); idx++) {
				GF_NALUFFParamArray *ar = gf_list_get(hevccfg->param_array, idx);
				if (ar->type==GF_HEVC_NALU_SEQ_PARAM) {
					DUMP_ARRAY(ar->nalus, "HEVCSPS", "hvcC", 0)
				} else if (ar->type==GF_HEVC_NALU_PIC_PARAM) {
					DUMP_ARRAY(ar->nalus, "HEVCPPS", "hvcC", 0)
				} else if (ar->type==GF_HEVC_NALU_VID_PARAM) {
					DUMP_ARRAY(ar->nalus, "HEVCVPS", "hvcC", 0)
				} else {
					DUMP_ARRAY(ar->nalus, "HEVCUnknownPS", "hvcC", 0)
				}
			}
		}
		if (vvccfg) {
			u32 idx;
			nalh_size = vvccfg->nal_unit_size;
			for (idx=0; idx<gf_list_count(vvccfg->param_array); idx++) {
				GF_NALUFFParamArray *ar = gf_list_get(vvccfg->param_array, idx);
				if (ar->type==GF_VVC_NALU_SEQ_PARAM) {
					DUMP_ARRAY(ar->nalus, "VVCSPS", "vvcC", 0)
				} else if (ar->type==GF_VVC_NALU_PIC_PARAM) {
					DUMP_ARRAY(ar->nalus, "VVCPPS", "vvcC", 0)
				} else if (ar->type==GF_VVC_NALU_VID_PARAM) {
					DUMP_ARRAY(ar->nalus, "VVCVPS", "vvcC", 0)
				} else {
					DUMP_ARRAY(ar->nalus, "VVCUnknownPS", "vvcC", 0)
				}
			}
		}
		if (lhvccfg) {
			u32 idx;
			nalh_size = lhvccfg->nal_unit_size;
			for (idx=0; idx<gf_list_count(lhvccfg->param_array); idx++) {
				GF_NALUFFParamArray *ar = gf_list_get(lhvccfg->param_array, idx);
				if (ar->type==GF_HEVC_NALU_SEQ_PARAM) {
					DUMP_ARRAY(ar->nalus, "HEVCSPS", "lhcC", 0)
				} else if (ar->type==GF_HEVC_NALU_PIC_PARAM) {
					DUMP_ARRAY(ar->nalus, "HEVCPPS", "lhcC", 0)
				} else if (ar->type==GF_HEVC_NALU_VID_PARAM) {
					DUMP_ARRAY(ar->nalus, "HEVCVPS", "lhcC", 0)
				} else {
					DUMP_ARRAY(ar->nalus, "HEVCUnknownPS", "lhcC", 0)
				}
			}
		}
		fprintf(dump, " </NALUConfig>\n");

		if (avccfg) gf_odf_avc_cfg_del(avccfg);
		if (svccfg) {
			gf_odf_avc_cfg_del(svccfg);
			has_svcc = GF_TRUE;
		}
		if (hevccfg) gf_odf_hevc_cfg_del(hevccfg);
		if (vvccfg) gf_odf_vvc_cfg_del(vvccfg);
		if (lhvccfg) gf_odf_hevc_cfg_del(lhvccfg);
	}

	/*fixme: for dumping encrypted track: we don't have neither avccfg nor svccfg*/
	if (!nalh_size) nalh_size = 4;

	/*for testing dependency*/
	countRef = gf_isom_get_reference_count(file, track, GF_ISOM_REF_SCAL);
	if (countRef > 0)
	{
		GF_ISOTrackID refTrackID;
		fprintf(dump, " <SCALReferences>\n");
		for (i = 1; i <= (u32) countRef; i++)
		{
			gf_isom_get_reference_ID(file, track, GF_ISOM_REF_SCAL, i, &refTrackID);
			fprintf(dump, "  <SCALReference number=\"%d\" refTrackID=\"%d\"/>\n", i, refTrackID);
		}

		fprintf(dump, " </SCALReferences>\n");
	}

	fprintf(dump, " <NALUSamples>\n");
	gf_isom_set_nalu_extract_mode(file, track, GF_ISOM_NALU_EXTRACT_INSPECT);
	is_adobe_protected = gf_isom_is_adobe_protection_media(file, track, 1);
	is_cenc_protected = gf_isom_is_cenc_media(file, track, 1);
	for (i=0; i<count; i++) {
		u64 dts, cts;
		Bool is_rap;
		u32 size, nal_size, idx, di;
		u8 *ptr;
		GF_ISOSample *samp = gf_isom_get_sample(file, track, i+1, &di);
		if (!samp) {
			fprintf(dump, "<!-- Unable to fetch sample %d -->\n", i+1);
			continue;
		}
		dts = samp->DTS;
		cts = dts + (s32) samp->CTS_Offset;
		is_rap = samp->IsRAP;
		if (!is_rap) gf_isom_get_sample_rap_roll_info(file, track, i+1, &is_rap, NULL, NULL);

		if (dump_flags&2) {
			fprintf(dump, "  <Sample size=\"%d\" RAP=\"%d\"", samp->dataLength, is_rap);
		} else {
			fprintf(dump, "  <Sample DTS=\""LLD"\" CTS=\""LLD"\" size=\"%d\" RAP=\"%d\"", dts, cts, samp->dataLength, is_rap);
		}
		if (nb_descs>1)
			fprintf(dump, " sample_description=\"%d\"", di);
		fprintf(dump, " >\n");

		if (cts<dts) fprintf(dump, "<!-- NEGATIVE CTS OFFSET! -->\n");

		idx = 1;
		ptr = samp->data;
		size = samp->dataLength;
		if (is_adobe_protected) {
			u8 encrypted_au = ptr[0];
			if (encrypted_au) {
				fprintf(dump, "   <!-- Sample number %d is an Adobe's protected sample: can not be dumped -->\n", i+1);
				fprintf(dump, "  </Sample>\n\n");
				continue;
			}
			else {
				ptr++;
				size--;
			}
		}
		while (size) {
			nal_size = read_nal_size_hdr(ptr, nalh_size);
			ptr += nalh_size;

			if (nal_size >= UINT_MAX-nalh_size || nalh_size + nal_size > size) {
				fprintf(dump, "   <!-- NALU number %d is corrupted: size is %d but only %d remains -->\n", idx, nal_size, size);
				break;
			} else {
				fprintf(dump, "   <NALU size=\"%d\" ", nal_size);
#ifndef GPAC_DISABLE_AV_PARSERS
				Bool is_encrypted = 0;
				if (is_cenc_protected) {
					GF_Err e = gf_isom_get_sample_cenc_info(file, track, i + 1, &is_encrypted, NULL, NULL, NULL, NULL);
					if (e != GF_OK) {
						fprintf(dump, "dump_msg=\"Error %s while fetching encryption info for sample, assuming sample is encrypted\" ", gf_error_to_string(e) );
						is_encrypted = GF_TRUE;
					}
				}
				gf_inspect_dump_nalu(dump, ptr, nal_size, has_svcc ? 1 : 0, hevc_state, avc_state, vvc_state, nalh_size, dump_flags, is_encrypted);
#else
				fprintf(dump, "/>\n");
#endif
			}
			idx++;
			ptr+=nal_size;
			size -= nal_size + nalh_size;
		}
		fprintf(dump, "  </Sample>\n");
		gf_isom_sample_del(&samp);

		fprintf(dump, "\n");
		gf_set_progress("Analysing Track NALUs", i+1, count);
	}
	fprintf(dump, " </NALUSamples>\n");
	fprintf(dump, "</NALUTrack>\n");

	gf_isom_set_nalu_extract_mode(file, track, cur_extract_mode);

#ifndef GPAC_DISABLE_AV_PARSERS
	if (hevc_state) gf_free(hevc_state);
	if (vvc_state) gf_free(vvc_state);
	if (avc_state) gf_free(avc_state);


static const char *GetLanguage(char *lcode)
{
	s32 idx = gf_lang_find(lcode);
	if (idx>=0) return gf_lang_get_name(idx);
	return lcode;
}


void dump_isom_timed_text(GF_ISOFile *file, GF_ISOTrackID trackID, char *inName, Bool is_final_name, Bool is_convert, GF_TextDumpType dump_type)
{
	FILE *dump;
	GF_Err e;
	u32 track;

	track = gf_isom_get_track_by_id(file, trackID);
	if (!track) {
		M4_LOG(GF_LOG_ERROR, ("Cannot find track ID %d\n", trackID));
		return;
	}

	switch (gf_isom_get_media_type(file, track)) {
	case GF_ISOM_MEDIA_TEXT:
	case GF_ISOM_MEDIA_SUBT:
		break;
	default:
		M4_LOG(GF_LOG_ERROR, ("Track ID %d is not a 3GPP text track\n", trackID));
		return;
	}

	if (inName) {
		char szBuf[1024];
		char *ext;
		ext = ((dump_type==GF_TEXTDUMPTYPE_SVG) ? "svg" : ((dump_type==GF_TEXTDUMPTYPE_SRT) ? "srt" : "ttxt"));
		if (is_final_name) {
			strcpy(szBuf, inName) ;
		} else if (is_convert)
			sprintf(szBuf, "%s.%s", inName, ext) ;
		else
			sprintf(szBuf, "%s_%d_text.%s", inName, trackID, ext);

		dump = gf_fopen(szBuf, "wt");
		if (!dump) {
			M4_LOG(GF_LOG_ERROR, ("Failed to open %s for dumping\n", szBuf));
			return;
		}
	} else {
		dump = stdout;
	}
	e = gf_isom_text_dump(file, track, dump, dump_type);
	if (inName) gf_fclose(dump);

	if (e) {
		M4_LOG(GF_LOG_ERROR, ("Conversion failed (%s)\n", gf_error_to_string(e)));
	} else {
		fprintf(stderr, "Conversion done\n");


void gf_inspect_format_timecode(const u8 *data, u32 size, u32 tmcd_flags, u32 tc_num, u32 tc_den, u32 tmcd_fpt, char szFmt[100]);

void DumpTrackInfo(GF_ISOFile *file, GF_ISOTrackID trackID, Bool full_dump, Bool is_track_num, Bool dump_m4sys)
{
	char szCodec[RFC6381_CODEC_NAME_SIZE_MAX];
	Double scale, max_rate, rate;
	Bool is_od_track = 0;
	u32 trackNum, i, j, ts, mtype, msub_type, timescale, sr, nb_ch, count, alt_group, nb_groups, nb_edits, cdur, csize, bps, pfmt, codecid;
	u64 time_slice, dur, size;
	s32 cts_shift;
	GF_ESD *esd;
	char szDur[50];
	char *lang;

	if (!is_track_num) {
		trackNum = gf_isom_get_track_by_id(file, trackID);
	} else {
		trackNum = trackID;
		trackID = gf_isom_get_track_id(file, trackNum);
	}
	if (!trackNum) {
		M4_LOG(GF_LOG_ERROR, ("No track with ID %d found\n", trackID));
		return;
	}

	timescale = gf_isom_get_media_timescale(file, trackNum);
	fprintf(stderr, "# Track %d Info - ID %d - TimeScale %d\n", trackNum, trackID, timescale);

	dur = gf_isom_get_media_original_duration(file, trackNum);
	size = gf_isom_get_media_duration(file, trackNum);
	fprintf(stderr, "Media Duration %s ", format_duration(dur, timescale, szDur));
	if (dur != size)
		fprintf(stderr, " (recomputed %s)", format_duration(size, timescale, szDur));
	fprintf(stderr, "\n");

	if (gf_isom_check_data_reference(file, trackNum, 1) != GF_OK) {
		M4_LOG(GF_LOG_WARNING, ("Track uses external data reference not supported by GPAC!\n"));
	}

	nb_edits = gf_isom_get_edits_count(file, trackNum);
	if (nb_edits)
		fprintf(stderr, "Track has %d edits: track duration is %s\n", nb_edits, format_duration(gf_isom_get_track_duration(file, trackNum), gf_isom_get_timescale(file), szDur));

	cts_shift = gf_isom_get_composition_offset_shift(file, trackNum);
	if (cts_shift)
		fprintf(stderr, "Track composition offset shift (negative CTS offset): %d\n", cts_shift);

	if (gf_isom_is_track_in_root_od(file, trackNum) ) fprintf(stderr, "Track is present in Root OD\n");
	if (!gf_isom_is_track_enabled(file, trackNum))  fprintf(stderr, "Track is disabled\n");
	gf_isom_get_media_language(file, trackNum, &lang);
	fprintf(stderr, "Media Info: Language \"%s (%s)\" - ", GetLanguage(lang), lang );
	gf_free(lang);
	mtype = gf_isom_get_media_type(file, trackNum);
	fprintf(stderr, "Type \"%s:", gf_4cc_to_str(mtype));
	msub_type = gf_isom_get_mpeg4_subtype(file, trackNum, 1);
	if (!msub_type) msub_type = gf_isom_get_media_subtype(file, trackNum, 1);
	fprintf(stderr, "%s\" - %d samples\n", gf_4cc_to_str(msub_type), gf_isom_get_sample_count(file, trackNum));

	pfmt = gf_pixel_fmt_from_qt_type(msub_type);
	codecid = gf_codec_id_from_isobmf(msub_type);

	count = gf_isom_get_track_kind_count(file, trackNum);
	for (i = 0; i < count; i++) {
		char *kind_scheme, *kind_value;
		gf_isom_get_track_kind(file, trackNum, i, &kind_scheme, &kind_value);
		fprintf(stderr, "Kind: %s - %s\n", kind_scheme ? kind_scheme : "null", kind_value ? kind_value : "null");
		if (kind_scheme) gf_free(kind_scheme);
		if (kind_value) gf_free(kind_value);
	}

	if (gf_isom_is_track_fragmented(file, trackID) ) {
		u32 defaultDuration, defaultSize, defaultDescriptionIndex, defaultRandomAccess;
		u8 defaultPadding;
		u16 defaultDegradationPriority;
		u32 frag_samples;
		u64 frag_duration;
		gf_isom_get_fragmented_samples_info(file, trackID, &frag_samples, &frag_duration);
		fprintf(stderr, "Fragmented track: %d samples - Media Duration %s\n", frag_samples, format_duration(frag_duration, timescale, szDur));

		gf_isom_get_fragment_defaults(file, trackNum, &defaultDuration, &defaultSize, &defaultDescriptionIndex, &defaultRandomAccess, &defaultPadding, &defaultDegradationPriority);

		fprintf(stderr, "Fragment sample defaults: duration %d size %d stsd %d sync %d padding %d degradation_priority %d\n",
				defaultDuration, defaultSize, defaultDescriptionIndex, defaultRandomAccess,
				(u32) defaultPadding, (u32) defaultDegradationPriority
		);
	}

	if (!gf_isom_is_self_contained(file, trackNum, 1)) {
		const char *url, *urn;
		gf_isom_get_data_reference(file, trackNum, 1, &url, &urn);
		fprintf(stderr, "Media Data Location: %s\n", url ? url : urn);
	}

	if (full_dump) {
		const char *handler_name;
		gf_isom_get_handler_name(file, trackNum, &handler_name);
		fprintf(stderr, "Handler name: %s\n", handler_name);
	}

	print_udta(file, trackNum, GF_FALSE);

	if (gf_isom_is_video_handler_type(mtype) ) {
		s32 tx, ty;
		u32 w, h;
		u16 bit_depth;

		gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
		gf_isom_get_visual_bit_depth(file, trackNum, 1, &bit_depth);
		fprintf(stderr, "Visual Sample Entry Info: width=%d height=%d (depth=%d bits)\n", w, h, (int)bit_depth);

		gf_isom_get_track_layout_info(file, trackNum, &w, &h, &tx, &ty, NULL);
		fprintf(stderr, "Visual Track layout: x=%d y=%d width=%d height=%d\n", tx, ty, w, h);
	}

	gf_isom_get_audio_info(file, trackNum, 1, &sr, &nb_ch, &bps);
	gf_isom_set_nalu_extract_mode(file, trackNum, GF_ISOM_NALU_EXTRACT_INSPECT);

	msub_type = gf_isom_get_media_subtype(file, trackNum, 1);
	if (msub_type==GF_ISOM_SUBTYPE_MPEG4_CRYP)
		gf_isom_get_original_format_type(file, trackNum, 1, &msub_type);

	if ((msub_type==GF_ISOM_SUBTYPE_MPEG4)
	        || (msub_type==GF_ISOM_SUBTYPE_AVC_H264)
	        || (msub_type==GF_ISOM_SUBTYPE_AVC2_H264)
	        || (msub_type==GF_ISOM_SUBTYPE_AVC3_H264)
	        || (msub_type==GF_ISOM_SUBTYPE_AVC4_H264)
	        || (msub_type==GF_ISOM_SUBTYPE_SVC_H264)
	        || (msub_type==GF_ISOM_SUBTYPE_MVC_H264)
	        || (msub_type==GF_ISOM_SUBTYPE_LSR1)
	        || (msub_type==GF_ISOM_SUBTYPE_HVC1)
	        || (msub_type==GF_ISOM_SUBTYPE_HEV1)
	        || (msub_type==GF_ISOM_SUBTYPE_HVC2)
	        || (msub_type==GF_ISOM_SUBTYPE_HEV2)
	        || (msub_type==GF_ISOM_SUBTYPE_LHV1)
	        || (msub_type==GF_ISOM_SUBTYPE_LHE1)
	        || (msub_type==GF_ISOM_SUBTYPE_HVT1)
	   )  {
		esd = gf_isom_get_esd(file, trackNum, 1);
		if (!esd || !esd->decoderConfig) {
			M4_LOG(GF_LOG_WARNING, ("WARNING: Broken MPEG-4 Track\n"));
			if (esd) gf_odf_desc_del((GF_Descriptor *)esd);
		} else {
			const char *st = gf_stream_type_name(esd->decoderConfig->streamType);
			if (dump_m4sys) {
				if (st) {
					fprintf(stderr, "MPEG-4 Config%s%s Stream - ObjectTypeIndication 0x%02x\n",
							full_dump ? "\n\t" : ": ", st, esd->decoderConfig->objectTypeIndication);
				} else {
					fprintf(stderr, "MPEG-4 Config%sStream Type 0x%02x - ObjectTypeIndication 0x%02x\n",
							full_dump ? "\n\t" : ": ", esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication);
				}
			}

			if (esd->decoderConfig->streamType==GF_STREAM_OD)
				is_od_track=1;

			if (esd->decoderConfig->streamType==GF_STREAM_VISUAL) {
				u32 w, h;
				u16 rvc_predef;
				w = h = 0;
				if (esd->decoderConfig->objectTypeIndication==GF_CODECID_MPEG4_PART2) {
#ifndef GPAC_DISABLE_AV_PARSERS
					if (!esd->decoderConfig->decoderSpecificInfo) {
#else
					gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
					fprintf(stderr, "MPEG-4 Visual Size %d x %d\n", w, h);
#endif
						M4_LOG(GF_LOG_WARNING, ("Non-compliant MPEG-4 Visual track: video_object_layer infos not found in sample description\n"));
#ifndef GPAC_DISABLE_AV_PARSERS
					} else {
						GF_M4VDecSpecInfo dsi;
						gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi);
						if (full_dump) fprintf(stderr, "\t");
						w = dsi.width;
						h = dsi.height;
						fprintf(stderr, "MPEG-4 Visual Size %d x %d - %s\n", w, h, gf_m4v_get_profile_name(dsi.VideoPL));
						if (dsi.par_den && dsi.par_num) {
							u32 tw, th;
							gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL);
							fprintf(stderr, "Pixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", dsi.par_num, dsi.par_den, tw, th);
						}
					}
#endif
				} else if (gf_isom_get_avc_svc_type(file, trackNum, 1) != GF_ISOM_AVCTYPE_NONE) {
					GF_AVCConfig *avccfg, *svccfg, *mvccfg;

					gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
					if (full_dump) fprintf(stderr, "\t");
					fprintf(stderr, "AVC/H264 Video - Visual Size %d x %d\n", w, h);

					avccfg = gf_isom_avc_config_get(file, trackNum, 1);
					svccfg = gf_isom_svc_config_get(file, trackNum, 1);
					mvccfg = gf_isom_mvc_config_get(file, trackNum, 1);
					if (!avccfg && !svccfg && !mvccfg) {
						M4_LOG(GF_LOG_ERROR, ("\tNon-compliant AVC track: SPS/PPS not found in sample description\n"));
					} else if (avccfg) {
						fprintf(stderr, "\tAVC Info: %d SPS - %d PPS", gf_list_count(avccfg->sequenceParameterSets) , gf_list_count(avccfg->pictureParameterSets) );
						fprintf(stderr, " - Profile %s @ Level %g\n", gf_avc_get_profile_name(avccfg->AVCProfileIndication), ((Double)avccfg->AVCLevelIndication)/10.0 );
						fprintf(stderr, "\tNAL Unit length bits: %d\n", 8*avccfg->nal_unit_size);

#ifndef GPAC_DISABLE_AV_PARSERS
						for (i=0; i<gf_list_count(avccfg->sequenceParameterSets); i++) {
							s32 par_n, par_d;
							GF_NALUFFParam *slc = gf_list_get(avccfg->sequenceParameterSets, i);
							gf_avc_get_sps_info(slc->data, slc->size, NULL, NULL, NULL, &par_n, &par_d);
							if ((par_n>0) && (par_d>0)) {
								u32 tw, th;
								gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL);
								fprintf(stderr, "\tPixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", par_n, par_d, tw, th);
							}
							if (!full_dump) break;
						}
#endif

						if (avccfg->chroma_bit_depth) {
							fprintf(stderr, "\tChroma format %s - Luma bit depth %d - chroma bit depth %d\n", gf_avc_hevc_get_chroma_format_name(avccfg->chroma_format), avccfg->luma_bit_depth, avccfg->chroma_bit_depth);
						}

						print_config_hash(avccfg->sequenceParameterSets, "SPS");
						print_config_hash(avccfg->pictureParameterSets, "PPS");

						gf_odf_avc_cfg_del(avccfg);
					}
					if (svccfg) {
						fprintf(stderr, "\n\tSVC Info: %d SPS - %d PPS - Profile %s @ Level %g\n", gf_list_count(svccfg->sequenceParameterSets) , gf_list_count(svccfg->pictureParameterSets), gf_avc_get_profile_name(svccfg->AVCProfileIndication), ((Double)svccfg->AVCLevelIndication)/10.0 );
						fprintf(stderr, "\tSVC NAL Unit length bits: %d\n", 8*svccfg->nal_unit_size);
#ifndef GPAC_DISABLE_AV_PARSERS
						for (i=0; i<gf_list_count(svccfg->sequenceParameterSets); i++) {
							GF_NALUFFParam *slc = gf_list_get(svccfg->sequenceParameterSets, i);
							if (slc) {
								s32 par_n, par_d;
								u32 s_w, s_h, sps_id;
								gf_avc_get_sps_info(slc->data, slc->size, &sps_id, &s_w, &s_h, &par_n, &par_d);
								fprintf(stderr, "\t\tSPS ID %d - Visual Size %d x %d\n", sps_id, s_w, s_h);
								if ((par_n>0) && (par_d>0)) {
									u32 tw, th;
									gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL);
									fprintf(stderr, "\tPixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", par_n, par_d, tw, th);
								}
							}
						}
#endif
						print_config_hash(svccfg->sequenceParameterSets, "SPS");
						print_config_hash(svccfg->pictureParameterSets, "PPS");
						print_config_hash(svccfg->sequenceParameterSetExtensions, "SPSEx");

						gf_odf_avc_cfg_del(svccfg);
					}

					if (mvccfg) {
						fprintf(stderr, "\n\tMVC Info: %d SPS - %d PPS - Profile %s @ Level %g\n", gf_list_count(mvccfg->sequenceParameterSets) , gf_list_count(mvccfg->pictureParameterSets), gf_avc_get_profile_name(mvccfg->AVCProfileIndication), ((Double)mvccfg->AVCLevelIndication)/10.0 );
						fprintf(stderr, "\tMVC NAL Unit length bits: %d\n", 8*mvccfg->nal_unit_size);
#ifndef GPAC_DISABLE_AV_PARSERS
						for (i=0; i<gf_list_count(mvccfg->sequenceParameterSets); i++) {
							GF_NALUFFParam *slc = gf_list_get(mvccfg->sequenceParameterSets, i);
							if (slc) {
								u32 s_w, s_h, sps_id;
								s32 par_n, par_d;
								gf_avc_get_sps_info(slc->data, slc->size, &sps_id, &s_w, &s_h, &par_n, &par_d);
								fprintf(stderr, "\t\tSPS ID %d - Visual Size %d x %d\n", sps_id, s_w, s_h);
								if ((par_n>0) && (par_d>0)) {
									u32 tw, th;
									gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL);
									fprintf(stderr, "\tPixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", par_n, par_d, tw, th);
								}
							}
						}
#endif
						print_config_hash(mvccfg->sequenceParameterSets, "SPS");
						print_config_hash(mvccfg->pictureParameterSets, "PPS");
						gf_odf_avc_cfg_del(mvccfg);
					}

				} else if ((esd->decoderConfig->objectTypeIndication==GF_CODECID_HEVC)
				           || (esd->decoderConfig->objectTypeIndication==GF_CODECID_LHVC)
				          ) {
					GF_HEVCConfig *hevccfg, *lhvccfg;
					GF_OperatingPointsInformation *oinf;
#if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_HEVC)
					HEVCState hevc_state;
					memset(&hevc_state, 0, sizeof(HEVCState));
					hevc_state.sps_active_idx = -1;
#endif

					gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
					if (full_dump) fprintf(stderr, "\t");
					fprintf(stderr, "HEVC Video - Visual Size %d x %d\n", w, h);
					hevccfg = gf_isom_hevc_config_get(file, trackNum, 1);
					lhvccfg = gf_isom_lhvc_config_get(file, trackNum, 1);

					if (msub_type==GF_ISOM_SUBTYPE_HVT1) {
						const u8 *data;
						u32 tsize;
						u32  is_default, tx,ty,tw,th, id, independent;
						Bool full_frame;
						if (gf_isom_get_tile_info(file, trackNum, 1, &is_default, &id, &independent, &full_frame, &tx, &ty, &tw, &th)) {
							fprintf(stderr, "\tHEVC Tile - ID %d independent %d (x,y,w,h)=%d,%d,%d,%d \n", id, independent, tx, ty, tw, th);
						} else if (gf_isom_get_sample_group_info(file, trackNum, 1, GF_ISOM_SAMPLE_GROUP_TRIF, &is_default, &data, &tsize)) {
							fprintf(stderr, "\tHEVC Tile track containing a tile set\n");
						} else {
							fprintf(stderr, "\tHEVC Tile track without tiling info\n");
						}
					} else if (!hevccfg && !lhvccfg) {
						M4_LOG(GF_LOG_ERROR, ("\tNon-compliant HEVC track: No hvcC or shcC found in sample description\n"));
					}

					if (gf_isom_get_reference_count(file, trackNum, GF_ISOM_REF_SABT)) {
						fprintf(stderr, "\tHEVC Tile base track\n");
					}
					if (hevccfg) {
						dump_hevc_track_info(file, trackNum, hevccfg
#if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_HEVC)
							, &hevc_state
#endif
						);
						gf_odf_hevc_cfg_del(hevccfg);
						fprintf(stderr, "\n");
					}
					if (lhvccfg) {
						dump_hevc_track_info(file, trackNum, lhvccfg
#if !defined(GPAC_DISABLE_AV_PARSERS) && !defined(GPAC_DISABLE_HEVC)
							, &hevc_state
#endif
						);
						gf_odf_hevc_cfg_del(lhvccfg);
					}

					if (gf_isom_get_oinf_info(file, trackNum, &oinf)) {
						fprintf(stderr, "\n\tOperating Points Information -");
						fprintf(stderr, " scalability_mask %d (", oinf->scalability_mask);
						switch (oinf->scalability_mask) {
						case 2:
							fprintf(stderr, "Multiview");
							break;
						case 4:
							fprintf(stderr, "Spatial scalability");
							break;
						case 8:
							fprintf(stderr, "Auxilary");
							break;
						default:
							fprintf(stderr, "unknown");
						}
						//TODO: need to dump more info ?
						fprintf(stderr, ") num_profile_tier_level %d ", gf_list_count(oinf->profile_tier_levels) );
						fprintf(stderr, " num_operating_points %d dependency layers %d \n", gf_list_count(oinf->operating_points), gf_list_count(oinf->dependency_layers) );
					}
				}

				/*OGG media*/
				else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_THEORA) {
					char *szName;
					gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
					if (full_dump) fprintf(stderr, "\t");
					if (!strnicmp((char *) &esd->decoderConfig->decoderSpecificInfo->data[3], "theora", 6)) szName = "Theora";
					else szName = "Unknown";
					fprintf(stderr, "Ogg/%s video / GPAC Mux  - Visual Size %d x %d\n", szName, w, h);
				}
				else {
					//check if we know this codec from its OTI
					u32 codec_id = gf_codecid_from_oti(GF_STREAM_VISUAL, esd->decoderConfig->objectTypeIndication);
					if (codec_id) {
						gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
						fprintf(stderr, "%s - Visual Size %d x %d\n", gf_codecid_name(codec_id), w, h);
					}
				}
				if (!w || !h) {
					gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
					if (full_dump) fprintf(stderr, "\t");
					fprintf(stderr, "Visual Size %d x %d\n", w, h);
				}
				if (gf_isom_get_rvc_config(file, trackNum, 1, &rvc_predef, NULL, NULL, NULL)==GF_OK) {
					fprintf(stderr, "Has RVC signaled - Predefined configuration %d\n", rvc_predef);
				}

			} else if (esd->decoderConfig->streamType==GF_STREAM_AUDIO) {
#ifndef GPAC_DISABLE_AV_PARSERS
				GF_M4ADecSpecInfo a_cfg;
				GF_Err e;
				u32 oti;
#endif
				u32 codec_id;
				Bool is_mp2 = GF_FALSE;
				switch (esd->decoderConfig->objectTypeIndication) {
				case GF_CODECID_AAC_MPEG2_MP:
				case GF_CODECID_AAC_MPEG2_LCP:
				case GF_CODECID_AAC_MPEG2_SSRP:
					is_mp2 = GF_TRUE;
				case GF_CODECID_AAC_MPEG4:
#ifndef GPAC_DISABLE_AV_PARSERS
					if (!esd->decoderConfig->decoderSpecificInfo)
						e = GF_NON_COMPLIANT_BITSTREAM;
					else
						e = gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &a_cfg);
					if (full_dump) fprintf(stderr, "\t");
					if (e) {
						M4_LOG(GF_LOG_ERROR, ("Corrupted AAC Config\n"));
					} else {
						char *signaling = "implicit";
						char *heaac = "";
						if (!is_mp2 && a_cfg.has_sbr) {
							if (a_cfg.has_ps) heaac = "(HE-AAC v2) ";
							else heaac = "(HE-AAC v1) ";
						}
						if (a_cfg.base_object_type==2) {
							if (a_cfg.has_ps || a_cfg.has_sbr)
								signaling = "backward compatible";
						} else {
							signaling = "hierarchical";
						}
						fprintf(stderr, "%s (AOT=%d %s) %s- %d Channel(s) - SampleRate %d", gf_m4a_object_type_name(a_cfg.base_object_type), a_cfg.base_object_type, signaling, heaac, a_cfg.nb_chan, a_cfg.base_sr);
						if (is_mp2) fprintf(stderr, " (MPEG-2 Signaling)");
						if (a_cfg.has_sbr) fprintf(stderr, " - SBR: SampleRate %d Type %s", a_cfg.sbr_sr, gf_m4a_object_type_name(a_cfg.sbr_object_type));
						if (a_cfg.has_ps) fprintf(stderr, " - PS");
						fprintf(stderr, "\n");
					}
#else
					fprintf(stderr, "MPEG-2/4 Audio - %d Channels - SampleRate %d\n", nb_ch, sr);
#endif
					break;
				case GF_CODECID_MPEG2_PART3:
				case GF_CODECID_MPEG_AUDIO:
					if (msub_type == GF_ISOM_SUBTYPE_MPEG4_CRYP) {
						fprintf(stderr, "MPEG-1/2 Audio - %d Channels - SampleRate %d\n", nb_ch, sr);
					} else {
#ifndef GPAC_DISABLE_AV_PARSERS
						GF_ISOSample *samp = gf_isom_get_sample(file, trackNum, 1, &oti);
						if (samp) {
							u32 mhdr = GF_4CC((u8)samp->data[0], (u8)samp->data[1], (u8)samp->data[2], (u8)samp->data[3]);
							if (full_dump) fprintf(stderr, "\t");
							fprintf(stderr, "%s Audio - %d Channel(s) - SampleRate %d - Layer %d\n",
							        gf_mp3_version_name(mhdr),
							        gf_mp3_num_channels(mhdr),
							        gf_mp3_sampling_rate(mhdr),
							        gf_mp3_layer(mhdr)
							       );
							gf_isom_sample_del(&samp);
						} else {
							M4_LOG(GF_LOG_ERROR, ("Error fetching sample: %s\n", gf_error_to_string(gf_isom_last_error(file)) ));
						}
#else
						fprintf(stderr, "MPEG-1/2 Audio - %d Channels - SampleRate %d\n", nb_ch, sr);
#endif
					}
					break;
				case GF_CODECID_EVRC:
					fprintf(stderr, "EVRC Audio - Sample Rate 8000 - 1 channel\n");
					break;
				case GF_CODECID_SMV:
					fprintf(stderr, "SMV Audio - Sample Rate 8000 - 1 channel\n");
					break;
				case GF_CODECID_QCELP:
					fprintf(stderr, "QCELP Audio - Sample Rate 8000 - 1 channel\n");
					break;
				/*packetVideo hack for EVRC...*/
				case GF_CODECID_EVRC_PV:
					if (esd->decoderConfig->decoderSpecificInfo && (esd->decoderConfig->decoderSpecificInfo->dataLength==8)
					        && !strnicmp((char *)esd->decoderConfig->decoderSpecificInfo->data, "pvmm", 4)) {
						if (full_dump) fprintf(stderr, "\t");
						fprintf(stderr, "EVRC Audio (PacketVideo Mux) - Sample Rate 8000 - 1 channel\n");
					}
					break;
				default:
					codec_id = gf_codecid_from_oti(GF_STREAM_AUDIO, esd->decoderConfig->objectTypeIndication);
					if (codec_id) {
						fprintf(stderr, "%s - Sample Rate %d - %d channel(s)\n", gf_codecid_name(codec_id), sr, nb_ch);
					}
					break;
				}
			}
			else if (esd->decoderConfig->streamType==GF_STREAM_SCENE) {
				if (esd->decoderConfig->objectTypeIndication<=4) {
					GF_BIFSConfig *b_cfg = gf_odf_get_bifs_config(esd->decoderConfig->decoderSpecificInfo, esd->decoderConfig->objectTypeIndication);
					fprintf(stderr, "BIFS Scene description - %s stream\n", b_cfg->elementaryMasks ? "Animation" : "Command");
					if (full_dump && !b_cfg->elementaryMasks) {
						fprintf(stderr, "\tWidth %d Height %d Pixel Metrics %s\n", b_cfg->pixelWidth, b_cfg->pixelHeight, b_cfg->pixelMetrics ? "yes" : "no");
					}
					gf_odf_desc_del((GF_Descriptor *)b_cfg);
				} else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_AFX) {
					u8 tag = esd->decoderConfig->decoderSpecificInfo ? esd->decoderConfig->decoderSpecificInfo->data[0] : 0xFF;
					const char *afxtype = gf_stream_type_afx_name(tag);
					fprintf(stderr, "AFX Stream - type %s (%d)\n", afxtype, tag);
				} else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_FONT) {
					fprintf(stderr, "Font Data stream\n");
				} else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_LASER) {
					GF_LASERConfig l_cfg;
					gf_odf_get_laser_config(esd->decoderConfig->decoderSpecificInfo, &l_cfg);
					fprintf(stderr, "LASER Stream - %s\n", l_cfg.newSceneIndicator ? "Full Scene" : "Scene Segment");
				} else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_TEXT_MPEG4) {
					fprintf(stderr, "MPEG-4 Streaming Text stream\n");
				} else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_SYNTHESIZED_TEXTURE) {
					fprintf(stderr, "Synthetized Texture stream stream\n");
				} else {
					M4_LOG(GF_LOG_WARNING, ("Unknown Systems stream OTI %d\n", esd->decoderConfig->objectTypeIndication));
				}
			}

			/*sync is only valid if we open all tracks to take care of default MP4 sync..*/
			if (!full_dump) {
				if (dump_m4sys) {
					if (!esd->OCRESID || (esd->OCRESID == esd->ESID))
						fprintf(stderr, "Self-synchronized\n");
					else
						fprintf(stderr, "Synchronized on stream %d\n", esd->OCRESID);
				}
			} else {
				fprintf(stderr, "\tDecoding Buffer size %d - Bitrate: avg %d - max %d kbps\n", esd->decoderConfig->bufferSizeDB, esd->decoderConfig->avgBitrate/1000, esd->decoderConfig->maxBitrate/1000);
				if (esd->dependsOnESID)
					fprintf(stderr, "\tDepends on stream %d for decoding\n", esd->dependsOnESID);
				else
					fprintf(stderr, "\tNo stream dependencies for decoding\n");

				fprintf(stderr, "\tStreamPriority %d\n", esd->streamPriority);
				if (esd->URLString) fprintf(stderr, "\tRemote Data Source %s\n", esd->URLString);
			}
			gf_odf_desc_del((GF_Descriptor *) esd);
		}
	} else if (msub_type == GF_ISOM_SUBTYPE_AV01) {
		GF_AV1Config *av1c;
		u32 w, h;
		gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
		fprintf(stderr, "\tAOM AV1 stream - Resolution %d x %d\n", w, h);

		av1c = gf_isom_av1_config_get(file, trackNum, 1);
		if (!av1c) {
			fprintf(stderr, "\tCorrupted av1 config\n");
		} else {
			fprintf(stderr, "\tversion=%u, profile=%u, level_idx0=%u, tier=%u\n", (u32)av1c->version, (u32)av1c->seq_profile, (u32)av1c->seq_level_idx_0, (u32)av1c->seq_tier_0);
			fprintf(stderr, "\thigh_bitdepth=%u, twelve_bit=%u, monochrome=%u\n", (u32)av1c->high_bitdepth, (u32)av1c->twelve_bit, (u32)av1c->monochrome);
			fprintf(stderr, "\tchroma: subsampling_x=%u, subsampling_y=%u, sample_position=%u\n", (u32)av1c->chroma_subsampling_x, (u32)av1c->chroma_subsampling_y, (u32)av1c->chroma_sample_position);

			if (av1c->initial_presentation_delay_present)
				fprintf(stderr, "\tInitial presentation delay %u\n", (u32) av1c->initial_presentation_delay_minus_one+1);

			count = gf_list_count(av1c->obu_array);
			for (i=0; i<count; i++) {
				u8 hash[20];
				GF_AV1_OBUArrayEntry *obu = gf_list_get(av1c->obu_array, i);
				gf_sha1_csum((u8*)obu->obu, (u32)obu->obu_length, hash);
				fprintf(stderr, "\tOBU#%d %s hash: ", i+1, gf_av1_get_obu_name(obu->obu_type) );
				for (j=0; j<20; j++) fprintf(stderr, "%02X", hash[j]);
				fprintf(stderr, "\n");
			}
			gf_odf_av1_cfg_del(av1c);
		}
	} else if (msub_type == GF_ISOM_SUBTYPE_3GP_H263) {
		u32 w, h;
		gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
		fprintf(stderr, "\t3GPP H263 stream - Resolution %d x %d\n", w, h);
	} else if (msub_type == GF_ISOM_SUBTYPE_MJP2) {
		u32 w, h;
		gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
		fprintf(stderr, "\tMotionJPEG2000 stream - Resolution %d x %d\n", w, h);
	} else if ((msub_type == GF_ISOM_SUBTYPE_3GP_AMR) || (msub_type == GF_ISOM_SUBTYPE_3GP_AMR_WB)) {
		fprintf(stderr, "\t3GPP AMR%s stream - Sample Rate %d - %d channel(s) %d bps\n", (msub_type == GF_ISOM_SUBTYPE_3GP_AMR_WB) ? " Wide Band" : "", sr, nb_ch, (u32) bps);
	} else if (msub_type == GF_ISOM_SUBTYPE_3GP_EVRC) {
		fprintf(stderr, "\t3GPP EVRC stream - Sample Rate %d - %d channel(s) %d bps\n", sr, nb_ch, (u32) bps);
	} else if (msub_type == GF_ISOM_SUBTYPE_3GP_QCELP) {
		fprintf(stderr, "\t3GPP QCELP stream - Sample Rate %d - %d channel(s) %d bps\n", sr, nb_ch, (u32) bps);
	} else if (msub_type == GF_ISOM_SUBTYPE_MP3) {
		fprintf(stderr, "\tMPEG 1/2 Audio stream - Sample Rate %d - %d channel(s) %d bps\n", sr, nb_ch, (u32) bps);
	} else if ((msub_type == GF_ISOM_SUBTYPE_AC3) || (msub_type == GF_ISOM_SUBTYPE_EC3)) {
		u32 br = 0;
		const char *lfe = "";
		Bool is_ec3 = (msub_type == GF_ISOM_SUBTYPE_EC3) ? GF_TRUE : GF_FALSE;
#ifndef GPAC_DISABLE_AV_PARSERS
		GF_AC3Config *ac3 = gf_isom_ac3_config_get(file, trackNum, 1);
		if (ac3) {
			nb_ch = gf_ac3_get_channels(ac3->streams[0].acmod);
			for (i=0; i<ac3->streams[0].nb_dep_sub; ++i) {
				assert(ac3->streams[0].nb_dep_sub == 1);
				nb_ch += gf_ac3_get_channels(ac3->streams[0].chan_loc);
			}
			if (ac3->streams[0].lfon) lfe = ".1";
			br = ac3->is_ec3 ? ac3->brcode : gf_ac3_get_bitrate(ac3->brcode);
			is_ec3 = ac3->is_ec3;
			gf_free(ac3);
		}
#endif
		fprintf(stderr, "\t%s stream - Sample Rate %d - %d%s channel(s) - bitrate %d\n", is_ec3 ? "EC-3" : "AC-3", sr, nb_ch, lfe, br);
	} else if (msub_type == GF_ISOM_SUBTYPE_3GP_SMV) {
		fprintf(stderr, "\t3GPP SMV stream - Sample Rate %d - %d channel(s) %d bits per samples\n", sr, nb_ch, (u32) bps);
	} else if (msub_type == GF_ISOM_SUBTYPE_3GP_DIMS) {
		u32 w, h;
		GF_DIMSDescription dims;
		gf_isom_get_visual_info(file, trackNum, 1, &w, &h);

		gf_isom_get_dims_description(file, trackNum, 1, &dims);
		fprintf(stderr, "\t3GPP DIMS stream - size %d x %d - Profile %d - Level %d\n", w, h, dims.profile, dims.level);
		fprintf(stderr, "\tpathComponents: %d - useFullRequestHost: %s\n", dims.pathComponents, dims.fullRequestHost ? "yes" : "no");
		fprintf(stderr, "\tstream type: %s - redundant: %s\n", dims.streamType ? "primary" : "secondary", (dims.containsRedundant==1) ? "main" : ((dims.containsRedundant==2) ? "redundant" : "main+redundant") );
		if (dims.textEncoding[0]) fprintf(stderr, "\ttext encoding %s\n", dims.textEncoding);
		if (dims.contentEncoding[0]) fprintf(stderr, "\tcontent encoding %s\n", dims.contentEncoding);
		if (dims.content_script_types) fprintf(stderr, "\tscript languages %s\n", dims.content_script_types);
	} else if (mtype==GF_ISOM_MEDIA_HINT) {
		u32 refTrack;
		s32 refCount = gf_isom_get_reference_count(file, trackNum, GF_ISOM_REF_HINT);
		if (refCount>0) {
			fprintf(stderr, "Streaming Hint Track for track%s ", (refCount>1) ? "s" :"");
			for (i=0; i<(u32) refCount; i++) {
				gf_isom_get_reference(file, trackNum, GF_ISOM_REF_HINT, i+1, &refTrack);
				if (i) fprintf(stderr, " - ");
				fprintf(stderr, "ID %d", gf_isom_get_track_id(file, refTrack));
			}
			fprintf(stderr, "\n");
		} else {
			fprintf(stderr, "Streaming Hint Track (no refs)\n");
		}
#ifndef GPAC_DISABLE_ISOM_HINTING
		refCount = gf_isom_get_payt_count(file, trackNum);
		if (refCount>0) {
			for (i=0; i<(u32) refCount; i++) {
				const char *name = gf_isom_get_payt_info(file, trackNum, i+1, &refTrack);
				fprintf(stderr, "\tPayload ID %d: type %s\n", refTrack, name);
			}
		}
#endif
	} else if (mtype==GF_ISOM_MEDIA_FLASH) {
		fprintf(stderr, "Macromedia Flash Movie\n");
	} else if ((mtype==GF_ISOM_MEDIA_TEXT) || (mtype==GF_ISOM_MEDIA_SUBT) || (mtype==GF_ISOM_MEDIA_MPEG_SUBT)) {
		u32 w, h;
		s16 l;
		s32 tx, ty;
		const char *content_encoding = NULL;
		const char *mime = NULL;
		const char *config  = NULL;
		const char *_namespace = NULL;
		const char *schema_loc = NULL;
		const char *auxiliary_mimes = NULL;
		gf_isom_get_track_layout_info(file, trackNum, &w, &h, &tx, &ty, &l);
		if (msub_type == GF_ISOM_SUBTYPE_SBTT) {
			gf_isom_stxt_get_description(file, trackNum, 1, &mime, &content_encoding, &config);
			fprintf(stderr, "Textual Subtitle Stream ");
			fprintf(stderr, "- mime %s", mime);
			if (content_encoding != NULL) {
				fprintf(stderr, " - encoding %s", content_encoding);
			}
			if (config != NULL) {
				fprintf(stderr, " - %d bytes config", (u32) strlen(config));
			}
		} else if (msub_type == GF_ISOM_SUBTYPE_STXT) {
			gf_isom_stxt_get_description(file, trackNum, 1, &mime, &content_encoding, &config);
			fprintf(stderr, "Simple Timed Text Stream ");
			fprintf(stderr, "- mime %s", mime);
			if (content_encoding != NULL) {
				fprintf(stderr, " - encoding %s", content_encoding);
			}
			if (config != NULL) {
				fprintf(stderr, " - %d bytes config", (u32) strlen(config));
			}
		} else if (msub_type == GF_ISOM_SUBTYPE_STPP) {
			gf_isom_xml_subtitle_get_description(file, trackNum, 1, &_namespace, &schema_loc, &auxiliary_mimes);
			fprintf(stderr, "XML Subtitle Stream ");
			fprintf(stderr, "- namespace %s", _namespace);
			if (schema_loc != NULL) {
				fprintf(stderr, " - schema-location %s", schema_loc);
			}
			if (auxiliary_mimes != NULL) {
				fprintf(stderr, " - auxiliary-mime-types %s", auxiliary_mimes);
			}
		} else {
			fprintf(stderr, "Unknown Text Stream");
		}
		fprintf(stderr, "\n Size %d x %d - Translation X=%d Y=%d - Layer %d\n", w, h, tx, ty, l);
	} else if (mtype == GF_ISOM_MEDIA_META) {
		const char *content_encoding = NULL;
		if (msub_type == GF_ISOM_SUBTYPE_METT) {
			const char *mime = NULL;
			const char *config  = NULL;
			gf_isom_stxt_get_description(file, trackNum, 1, &mime, &content_encoding, &config);
			fprintf(stderr, "Textual Metadata Stream - mime %s", mime);
			if (content_encoding != NULL) {
				fprintf(stderr, " - encoding %s", content_encoding);
			}
			if (config != NULL) {
				fprintf(stderr, " - %d bytes config", (u32) strlen(config));
			}
			fprintf(stderr, "\n");
		} else if (msub_type == GF_ISOM_SUBTYPE_METX) {
			const char *_namespace = NULL;
			const char *schema_loc = NULL;
			gf_isom_get_xml_metadata_description(file, trackNum, 1, &_namespace, &schema_loc, &content_encoding);
			fprintf(stderr, "XML Metadata Stream - namespace %s", _namespace);
			if (content_encoding != NULL) {
				fprintf(stderr, " - encoding %s", content_encoding);
			}
			if (schema_loc != NULL) {
				fprintf(stderr, " - schema-location %s", schema_loc);
			}
			fprintf(stderr, "\n");
		} else {
			fprintf(stderr, "Unknown Metadata Stream\n");
		}
	} else if ((msub_type==GF_ISOM_SUBTYPE_VVC1) || (msub_type==GF_ISOM_SUBTYPE_VVI1)) {
		GF_VVCConfig *vvccfg;
		u32 w, h;
#if !defined(GPAC_DISABLE_AV_PARSERS)
		VVCState *vvc_state;
		GF_SAFEALLOC(vvc_state, VVCState);
		if (vvc_state) vvc_state->sps_active_idx = -1;
#endif

		gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
		if (full_dump) fprintf(stderr, "\t");
		fprintf(stderr, "VVC Video - Visual Size %d x %d\n", w, h);
		vvccfg = gf_isom_vvc_config_get(file, trackNum, 1);

		if (!vvccfg) {
			M4_LOG(GF_LOG_ERROR, ("Non-compliant VVC track: No vvcC found in sample description\n"));
		} else {
			dump_vvc_track_info(file, trackNum, vvccfg
#if !defined(GPAC_DISABLE_AV_PARSERS)
				, vvc_state
#endif
			);
			gf_odf_vvc_cfg_del(vvccfg);
			fprintf(stderr, "\n");
		}
#if !defined(GPAC_DISABLE_AV_PARSERS)
		if (vvc_state) gf_free(vvc_state);
#endif
	} else if ((msub_type == GF_ISOM_SUBTYPE_MH3D_MHA1) || (msub_type == GF_ISOM_SUBTYPE_MH3D_MHA2)
			|| (msub_type == GF_ISOM_SUBTYPE_MH3D_MHM1) || (msub_type == GF_ISOM_SUBTYPE_MH3D_MHM2)
	) {
		const u8 *compat_profiles;
		u32 nb_compat_profiles;
		Bool valid = GF_FALSE;
		Bool allow_inband = GF_FALSE;
		if ( (msub_type == GF_ISOM_SUBTYPE_MH3D_MHM1) || (msub_type == GF_ISOM_SUBTYPE_MH3D_MHM2))
			allow_inband = GF_TRUE;

		fprintf(stderr, "\tMPEG-H Audio stream - Sample Rate %d\n", sr);

		esd = gf_media_map_esd(file, trackNum, 1);
		if (!esd || !esd->decoderConfig || !esd->decoderConfig->decoderSpecificInfo
			|| !esd->decoderConfig->decoderSpecificInfo->data
		) {
			if (allow_inband) {
				GF_ISOSample *samp = gf_isom_get_sample(file, trackNum, 1, NULL);
				if (samp) {
					u64 ch_layout=0;
					s32 PL = gf_mpegh_get_mhas_pl(samp->data, samp->dataLength, &ch_layout);
					if (PL>=0) {
						fprintf(stderr, "\tProfileLevelIndication: 0x%02X", PL);
						if (ch_layout)
							fprintf(stderr, " - Reference Channel Layout %s", gf_audio_fmt_get_layout_name(ch_layout) );
						fprintf(stderr, "\n");
					}
					gf_isom_sample_del(&samp);
				}
				valid = GF_TRUE;
			}
		} else if (esd->decoderConfig->decoderSpecificInfo->dataLength>=5) {
			fprintf(stderr, "\tProfileLevelIndication: 0x%02X - Reference Channel Layout %s\n", esd->decoderConfig->decoderSpecificInfo->data[1]
				, gf_audio_fmt_get_layout_name_from_cicp(esd->decoderConfig->decoderSpecificInfo->data[2])
			);
			valid = GF_TRUE;
		}
		if (!valid) {
			M4_LOG(GF_LOG_ERROR, ("Invalid MPEG-H audio config\n"));
		}
		if (esd) gf_odf_desc_del((GF_Descriptor *)esd);
		compat_profiles = gf_isom_get_mpegh_compatible_profiles(file, trackNum, 1, &nb_compat_profiles);
		for (i=0; i<nb_compat_profiles; i++) {
			if (!i)
				fprintf(stderr, "\tCompatible profiles:");
			fprintf(stderr, " 0x%02X", compat_profiles[i]);
		}
		if (i) fprintf(stderr, "\n");
	} else if (msub_type==GF_ISOM_SUBTYPE_MLPA) {
		u32 fmt, prate;
		if (gf_isom_truehd_config_get(file, trackNum, 1, &fmt, &prate) != GF_OK) {
			fprintf(stderr, "\tInvalid TrueHD audio config\n");
		}
		fprintf(stderr, "TrueHD Audio stream - Sample Rate %u - channels %u - format %u peak rate %u\n", sr, nb_ch, fmt, prate);
	} else if (codecid) {
		if (gf_isom_is_video_handler_type(mtype) ) {
			u32 w, h;
			gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
			fprintf(stderr, "%s - Resolution %d x %d\n", gf_codecid_name(codecid), w, h);
		} else if (mtype==GF_ISOM_MEDIA_AUDIO) {
			gf_isom_get_audio_info(file, trackNum, 1, &sr, &nb_ch, NULL);
			fprintf(stderr, "%s - Sample Rate %d - %d channel(s)\n", gf_codecid_name(codecid), sr, nb_ch);
		} else {
			fprintf(stderr, "%s\n", gf_codecid_name(codecid) );
		}
	} else if (pfmt) {
		u32 w, h;
		gf_isom_get_visual_info(file, trackNum, 1, &w, &h);
		fprintf(stderr, "Raw video %s - Resolution %d x %d\n", gf_pixel_fmt_name(pfmt), w, h);
	} else if (msub_type==GF_QT_SUBTYPE_TMCD) {
		u32 stsd_idx;
		GF_ISOSample *sample = gf_isom_get_sample(file, trackNum, 1, &stsd_idx);
		fprintf(stderr, "Time Code stream\n");
		if (sample) {
			char szTimecode[100];
			u32 tmcd_flags, tmcd_num, tmcd_den, tmcd_fpt;

			gf_isom_get_tmcd_config(file, trackNum, stsd_idx, &tmcd_flags, &tmcd_num, &tmcd_den, &tmcd_fpt);

			gf_inspect_format_timecode(sample->data, sample->dataLength, tmcd_flags, tmcd_num, tmcd_den, tmcd_fpt, szTimecode);

			gf_isom_sample_del(&sample);
			fprintf(stderr, "\tFirst timecode: %s\n", szTimecode);
		}
	} else {
		GF_GenericSampleDescription *udesc;

		udesc = gf_isom_get_generic_sample_description(file, trackNum, 1);
		if (udesc) {
			if (gf_isom_is_video_handler_type(mtype) ) {
                fprintf(stderr, "%s - Compressor \"%s\" - Resolution %d x %d\n",
						( (mtype == GF_ISOM_MEDIA_VISUAL ? "Visual" : "Auxiliary Video") ),
                        udesc->compressor_name, udesc->width, udesc->height);
			} else if (mtype==GF_ISOM_MEDIA_AUDIO) {
				fprintf(stderr, "Audio - Sample Rate %d - %d channel(s)\n", udesc->samplerate, udesc->nb_channels);
			} else {
				fprintf(stderr, "Unknown media type\n");
			}
			if (udesc->vendor_code)
				fprintf(stderr, "\tVendor code \"%s\" - Version %d - revision %d\n", gf_4cc_to_str(udesc->vendor_code), udesc->version, udesc->revision);

			if (udesc->extension_buf) {
				fprintf(stderr, "\tCodec configuration data size: %d bytes\n", udesc->extension_buf_size);
				gf_free(udesc->extension_buf);
			}
			gf_free(udesc);
		} else {
			fprintf(stderr, "Unknown track type\n");
		}
	}

	/*Crypto info*/
	if (gf_isom_is_track_encrypted(file, trackNum)) {
		const char *scheme_URI, *KMS_URI;
		u32 scheme_type, version;
		u32 IV_size;
		Bool use_sel_enc;

		if (gf_isom_is_ismacryp_media(file, trackNum, 1)) {
			gf_isom_get_ismacryp_info(file, trackNum, 1, NULL, &scheme_type, &version, &scheme_URI, &KMS_URI, &use_sel_enc, &IV_size, NULL);
			fprintf(stderr, "\n\tProtected by ISMA E&A scheme %s (version %d)\n", gf_4cc_to_str(scheme_type), version);
			if (scheme_URI) fprintf(stderr, "scheme location: %s\n", scheme_URI);
			if (KMS_URI) {
				if (!strnicmp(KMS_URI, "(key)", 5)) fprintf(stderr, "\tKMS location: key in file\n");
				else fprintf(stderr, "\tKMS location: %s\n", KMS_URI);
			}
			fprintf(stderr, "\tSelective Encryption: %s\n", use_sel_enc ? "Yes" : "No");
			if (IV_size) fprintf(stderr, "\tInitialization Vector size: %d bits\n", IV_size*8);
		} else if (gf_isom_is_omadrm_media(file, trackNum, 1)) {
			const char *textHdrs;
			u32 enc_type, hdr_len;
			u64 orig_len;
			gf_isom_get_omadrm_info(file, trackNum, 1, NULL, &scheme_type, &version, &scheme_URI, &KMS_URI, &textHdrs, &hdr_len, &orig_len, &enc_type, &use_sel_enc, &IV_size, NULL);
			fprintf(stderr, "\n\tProtected by OMA DRM scheme %s (version %d)\n", gf_4cc_to_str(scheme_type), version);
			fprintf(stderr, "\tRights Issuer: %s\n", KMS_URI);
			fprintf(stderr, "\tContent ID: %s\n", scheme_URI);
			if (textHdrs) {
				u32 offset;
				const char *start = textHdrs;
				fprintf(stderr, "\tOMA Textual Headers:\n");
				i=0;
				offset=0;
				while (i<hdr_len) {
					if (start[i]==0) {
						fprintf(stderr, "\t\t%s\n", start+offset);
						offset=i+1;
					}
					i++;
				}
				fprintf(stderr, "\\tt%s\n", start+offset);
			}
			if (orig_len) fprintf(stderr, "\tOriginal media size "LLD"\n", orig_len);
			fprintf(stderr, "\tEncryption algorithm %s\n", (enc_type==1) ? "AEA 128 CBC" : (enc_type ? "AEA 128 CTR" : "None"));
			fprintf(stderr, "\tSelective Encryption: %s\n", use_sel_enc ? "Yes" : "No");
			if (IV_size) fprintf(stderr, "\tInitialization Vector size: %d bits\n", IV_size*8);
		} else if(gf_isom_is_cenc_media(file, trackNum, 1)) {
			const u8 *def_key;
			u32 def_key_size;
			Bool IsEncrypted;
			u8 crypt_byte_block, skip_byte_block;
			IV_size = 0;
			gf_isom_get_cenc_info(file, trackNum, 1, NULL, &scheme_type, &version);

			gf_isom_cenc_get_default_info(file, trackNum, 1, NULL, &IsEncrypted, &crypt_byte_block, &skip_byte_block, &def_key, &def_key_size);

			fprintf(stderr, "\n\tProtected by CENC scheme %s version 0x%08X", gf_4cc_to_str(scheme_type), version);

			if (crypt_byte_block && skip_byte_block)
				fprintf(stderr, " - Pattern %d:%d", (u32) skip_byte_block, (u32) crypt_byte_block);
			if (def_key && def_key[0])
				fprintf(stderr, " - MultiKey");

			fprintf(stderr, "\n");
			dump_key_info(def_key, def_key_size, IsEncrypted);

		} else if(gf_isom_is_adobe_protection_media(file, trackNum, 1)) {
			gf_isom_get_adobe_protection_info(file, trackNum, 1, NULL, &scheme_type, &version, NULL);
			fprintf(stderr, "\nProtected by Adobe scheme %s (version %d)\n", gf_4cc_to_str(scheme_type), version);
		} else {
			fprintf(stderr, "\nProtected by unknown scheme %s\n", gf_4cc_to_str(gf_isom_is_media_encrypted(file, trackNum, 0) ));
		}
		fprintf(stderr, "\n");
	}

	if ( gf_media_get_rfc_6381_codec_name(file, trackNum, szCodec, GF_FALSE, GF_FALSE) == GF_OK) {
		fprintf(stderr, "\tRFC6381 Codec Parameters: %s\n", szCodec);
	}

	DumpMetaItem(file, 0, trackNum, "\tTrack Meta");

	gf_isom_get_track_switch_group_count(file, trackNum, &alt_group, &nb_groups);
	if (alt_group) {
		fprintf(stderr, "Alternate Group ID %d\n", alt_group);
		for (i=0; i<nb_groups; i++) {
			u32 nb_crit, switchGroupID;
			const u32 *criterias = gf_isom_get_track_switch_parameter(file, trackNum, i+1, &switchGroupID, &nb_crit);
			if (!nb_crit) {
				fprintf(stderr, "\tNo criteria in %s group\n", switchGroupID ? "switch" : "alternate");
			} else {
				if (switchGroupID) {
					fprintf(stderr, "\tSwitchGroup ID %d criterias: ", switchGroupID);
				} else {
					fprintf(stderr, "\tAlternate Group criterias: ");
				}
				for (j=0; j<nb_crit; j++) {
					if (j) fprintf(stderr, " ");
					fprintf(stderr, "%s", gf_4cc_to_str(criterias[j]) );
				}
				fprintf(stderr, "\n");
			}
		}
	}

	switch (gf_isom_has_sync_points(file, trackNum)) {
	case 0:
		fprintf(stderr, "\tAll samples are sync\n");
		break;
	case 1:
	{
		u32 nb_sync = gf_isom_get_sync_point_count(file, trackNum) - 1;
		if (! nb_sync) {
			fprintf(stderr, "\tOnly one sync sample\n");
		} else {
			fprintf(stderr, "\tAverage GOP length: %d samples\n", gf_isom_get_sample_count(file, trackNum) / nb_sync);
		}
	}
	break;
	case 2:
		fprintf(stderr, "\tNo sync sample found\n");
		break;
	}
	fprintf(stderr, "\tMax sample duration: %d / %d\n", gf_isom_get_max_sample_delta(file, trackNum), timescale);

	if (!full_dump) {
		fprintf(stderr, "\n");
		return;
	}

	dur = size = 0;
	max_rate = rate = 0;
	time_slice = 0;
	ts = gf_isom_get_media_timescale(file, trackNum);
	csize = gf_isom_get_constant_sample_size(file, trackNum);
	cdur = gf_isom_get_constant_sample_duration(file, trackNum);
	count = gf_isom_get_sample_count(file, trackNum);
	if (csize && cdur) {
		size = count * csize;
		dur = cdur * count;
	} else {

		for (j=0; j<count; j++) {
			GF_ISOSample *samp;
			if (is_od_track) {
				samp = gf_isom_get_sample(file, trackNum, j+1, NULL);
			} else {
				samp = gf_isom_get_sample_info(file, trackNum, j+1, NULL, NULL);
			}
			if (!samp) {
				M4_LOG(GF_LOG_ERROR, ("Failed to fetch sample %d\n", j+1));
				return;
			}
			dur = samp->DTS+samp->CTS_Offset;
			size += samp->dataLength;
			rate += samp->dataLength;
			if (samp->DTS - time_slice > ts) {
				Double max_tmp = rate * ts / (samp->DTS - time_slice);
				if (max_rate < max_tmp )
					max_rate = max_tmp;

				rate = 0;
				time_slice = samp->DTS;
			}
			gf_isom_sample_del(&samp);
		}
	}
	fprintf(stderr, "\nComputed info from media:\n");
	if (csize && cdur) {
		fprintf(stderr, "\tConstant sample size %d bytes and dur %d / %d\n", csize, cdur, ts);
	}
	scale = 1000.0 / ts;
	dur = (u64) (scale * dur);
	fprintf(stderr, "\tTotal size "LLU" bytes - Total samples duration "LLU" ms\n", size, dur);
	if (!dur) {
		fprintf(stderr, "\n");
		return;
	}
	/*rate in byte, dur is in ms*/
	rate = 8000.0 * size / dur;

	if (!max_rate)
		max_rate = rate;
	else
		max_rate *= 8.0;

	if (rate >= 1500) {
		fprintf(stderr, "\tAverage rate %.2f kbps - Max Rate %.2f kbps\n", rate/1000, max_rate/1000);
	} else {
		fprintf(stderr, "\tAverage rate %.2f bps - Max Rate %.2f bps\n", rate, max_rate);
	}

	{
		u32 dmin, dmax, davg, smin, smax, savg;
		gf_isom_get_chunks_infos(file, trackNum, &dmin, &davg, &dmax, &smin, &savg, &smax);
		fprintf(stderr, "\tChunk durations: min %d ms - max %d ms - average %d ms\n", (1000*dmin)/ts, (1000*dmax)/ts, (1000*davg)/ts);
		fprintf(stderr, "\tChunk sizes (bytes): min %d - max %d - average %d\n", smin, smax, savg);
	}
	fprintf(stderr, "\n");

	count = gf_isom_get_chapter_count(file, trackNum);
	if (count) {
		const char *name;
		u64 time;
		fprintf(stderr, "\nChapters:\n");
		for (j=0; j<count; j++) {
			gf_isom_get_chapter(file, trackNum, j+1, &time, &name);
			fprintf(stderr, "\tChapter #%d - %s - \"%s\"\n", j+1, format_duration(time, 1000, szDur), name);
		}


}

void dump_mpeg2_ts(char *mpeg2ts_file, char *out_name, Bool prog_num)
{
	u8 data[188];
	GF_M2TS_Dump dumper;

	u32 size;
	u64 fsize, fdone;
	GF_M2TS_Demuxer *ts;
	FILE *src;

	if (!prog_num && !out_name) {
		fprintf(stderr, "No program number nor output filename specified. No timestamp file will be generated.");
	}

	src = gf_fopen(mpeg2ts_file, "rb");
	if (!src) {
		M4_LOG(GF_LOG_ERROR, ("Cannot open %s: no such file\n", mpeg2ts_file));
		return;
	}
	ts = gf_m2ts_demux_new();
	ts->on_event = on_m2ts_dump_event;
	ts->notify_pes_timing = 1;
	memset(&dumper, 0, sizeof(GF_M2TS_Dump));
	ts->user = &dumper;
	dumper.prog_number = prog_num;

	/*PES dumping*/
	if (out_name) {
		char *pid = strrchr(out_name, '#');
		if (pid) {
			dumper.dump_pid = atoi(pid+1);
			pid[0] = 0;
			sprintf(dumper.dump, "%s_%d.raw", out_name, dumper.dump_pid);
			dumper.pes_out = gf_fopen(dumper.dump, "wb");
#if 0
			sprintf(dumper.nhml, "%s_%d.nhml", pes_out_name, dumper.dump_pid);
			dumper.pes_out_nhml = gf_fopen(dumper.nhml, "wt");
			sprintf(dumper.info, "%s_%d.info", pes_out_name, dumper.dump_pid);
			dumper.pes_out_info = gf_fopen(dumper.info, "wb");
#endif
			pid[0] = '#';
		}
	}

	gf_fseek(src, 0, SEEK_END);
	fsize = gf_ftell(src);
	gf_fseek(src, 0, SEEK_SET);

	/* first loop to process all packets between two PAT, and assume all signaling was found between these 2 PATs */
	while (!feof(src)) {
		size = (u32) gf_fread(data, 188, src);
		if (size<188) break;

		gf_m2ts_process_data(ts, data, size);
		if (dumper.has_seen_pat) break;
	}
	dumper.has_seen_pat = GF_TRUE;

	if (!prog_num) {
		GF_M2TS_Program *p = gf_list_get(ts->programs, 0);
		if (p) prog_num = p->number;
		fprintf(stderr, "No program number specified, defaulting to first program\n");
	}

	if (!prog_num && !out_name) {
		fprintf(stderr, "No program number nor output filename specified. No timestamp file will be generated\n");
	}

	if (prog_num) {
		sprintf(dumper.timestamps_info_name, "%s_prog_%d_timestamps.txt", mpeg2ts_file, prog_num/*, mpeg2ts_file*/);
		dumper.timestamps_info_file = gf_fopen(dumper.timestamps_info_name, "wt");
		if (!dumper.timestamps_info_file) {
			M4_LOG(GF_LOG_ERROR, ("Cannot open file %s\n", dumper.timestamps_info_name));
			return;
		}
		fprintf(dumper.timestamps_info_file, "PCK#\tPID\tPCR\tDTS\tPTS\tRAP\tDiscontinuity\tDTS-PCR Diff\n");
	}

	gf_m2ts_reset_parsers(ts);
	gf_fseek(src, 0, SEEK_SET);
	fdone = 0;

	while (!feof(src)) {
		size = (u32) gf_fread(data, 188, src);
		if (size<188) break;

		gf_m2ts_process_data(ts, data, size);

		fdone += size;
		gf_set_progress("MPEG-2 TS Parsing", fdone, fsize);
	}

	gf_fclose(src);
	gf_m2ts_demux_del(ts);
	if (dumper.pes_out) gf_fclose(dumper.pes_out);
#if 0
	if (dumper.pes_out_nhml) {
		if (dumper.is_info_dumped) fprintf(dumper.pes_out_nhml, "</NHNTStream>\n");
		gf_fclose(dumper.pes_out_nhml);
		gf_fclose(dumper.pes_out_info);
	}
#endif


Fix `tf.io.decode_raw` bugs and update documentation.

Fixes cases where specifying `fixed_length` resulted in data loss and even segfault and corruption of the Python interpreter. The fix is subtle but needed due to pointer arithmetic rules.

Makes sure that `fixed_length` does not change the output when present but not needed.

Eliminates needless copy and cast in the main codepath.

PiperOrigin-RevId: 371322725
Change-Id: I514ef67a2961c86422f69d05122d31615e87896c

  explicit DecodePaddedRawOp(OpKernelConstruction* context)
      : OpKernel(context) {
    OP_REQUIRES_OK(context, context->GetAttr("out_type", &out_type_));

    const bool host_is_little_endian = port::kLittleEndian;
    bool data_is_little_endian;
    OP_REQUIRES_OK(context,
                   context->GetAttr("little_endian", &data_is_little_endian));
    convert_data_endianness_ = host_is_little_endian != data_is_little_endian;
  }

TensorFlow is an end-to-end open source platform for machine learning. The implementation of `tf.io.decode_raw` produces incorrect results and crashes the Python interpreter when combining `fixed_length` and wider datatypes. The implementation of the padded version(https://github.com/tensorflow/tensorflow/blob/1d8903e5b167ed0432077a3db6e462daf781d1fe/tensorflow/core/kernels/decode_padded_raw_op.cc) is buggy due to a confusion about pointer arithmetic rules. First, the code computes(https://github.com/tensorflow/tensorflow/blob/1d8903e5b167ed0432077a3db6e462daf781d1fe/tensorflow/core/kernels/decode_padded_raw_op.cc#L61) the width of each output element by dividing the `fixed_length` value to the size of the type argument. The `fixed_length` argument is also used to determine the size needed for the output tensor(https://github.com/tensorflow/tensorflow/blob/1d8903e5b167ed0432077a3db6e462daf781d1fe/tensorflow/core/kernels/decode_padded_raw_op.cc#L63-L79). This is followed by reencoding code(https://github.com/tensorflow/tensorflow/blob/1d8903e5b167ed0432077a3db6e462daf781d1fe/tensorflow/core/kernels/decode_padded_raw_op.cc#L85-L94). The erroneous code is the last line above: it is moving the `out_data` pointer by `fixed_length * sizeof(T)` bytes whereas it only copied at most `fixed_length` bytes from the input. This results in parts of the input not being decoded into the output. Furthermore, because the pointer advance is far wider than desired, this quickly leads to writing to outside the bounds of the backing data. This OOB write leads to interpreter crash in the reproducer mentioned here, but more severe attacks can be mounted too, given that this gadget allows writing to periodically placed locations in memory. The fix will be included in TensorFlow 2.5.0. We will also cherrypick this commit on TensorFlow 2.4.2, TensorFlow 2.3.3, TensorFlow 2.2.3 and TensorFlow 2.1.4, as these are also affected and still in supported range.


  void Compute(OpKernelContext* context) override {
    const auto& input = context->input(0);
    auto flat_in = input.flat<tstring>();

    int fixed_length;
    const auto& length_input = context->input(1);
    OP_REQUIRES(context, TensorShapeUtils::IsScalar(length_input.shape()),
                errors::InvalidArgument("k must be scalar, got shape ",
                                        length_input.shape().DebugString()));
    fixed_length = length_input.scalar<int32>()();

    OP_REQUIRES(
        context, fixed_length % sizeof(T) == 0,
        errors::InvalidArgument(
            "fixed_length (", fixed_length,
            ") must be a multiple of the size of out_type (", sizeof(T), ")"));

    OP_REQUIRES(context, fixed_length > 0,
                errors::InvalidArgument("fixed_length (", fixed_length,
                                        ") must be greater than zero."));

    int width = fixed_length / sizeof(T);

    TensorShape out_shape = input.shape();
    out_shape.AddDim(width);
    Tensor* output_tensor = nullptr;
    OP_REQUIRES_OK(
        context, context->allocate_output("output", out_shape, &output_tensor));

    if (flat_in.size() == 0) {  // Empty input
      return;
    }

    auto out = output_tensor->flat_inner_dims<T>();
    T* out_data = out.data();

    // Forcibly clear memory - we're going to copy variable length strings in,
    // and need to ensure that if we don't write to byte N when we copy, that
    // we're not getting random data.
    memset(out_data, 0, fixed_length * flat_in.size());

    // If the data is already in the host's byte order, or if the width of the
    // output type is a single byte (meaning the ordering doesn't matter), we
    // can copy the memory directly.
    if (!convert_data_endianness_ || sizeof(T) == 1) {
      for (int64 i = 0; i < flat_in.size(); ++i) {
        const auto to_copy =
            std::min(flat_in(i).size(), static_cast<size_t>(fixed_length));
        memcpy(out_data, flat_in(i).data(), to_copy);
        // Note: increase out_data by width since it's already of type T* so
        // each shift amount is implicitly multiplied by sizeof(T) according to
        // pointer arithmetic rules.
        out_data += width;
      }
    } else {
      // Otherwise, the data is not in the host's byte order, and rather than a
      // direct copy, we need to reverse the byte ordering of each element.
      for (int64 i = 0; i < flat_in.size(); ++i) {
        const char* in_data_bytes =
            reinterpret_cast<const char*>(flat_in(i).data());
        char* out_data_bytes = reinterpret_cast<char*>(out_data);
        const char* p_in = in_data_bytes;
        char* p_out = out_data_bytes;
        for (; p_in < in_data_bytes + fixed_length;
             p_in += sizeof(T), p_out += sizeof(T)) {
          std::reverse_copy(p_in, p_in + sizeof(T), p_out);
        }
        // Note: increase out_data by width since it's already of type T* so
        // each shift amount is implicitly multiplied by sizeof(T) according to
        // pointer arithmetic rules.
        out_data += width;
      }
    }
  }