<commit_before>/*
 *			GPAC - Multimedia Framework C SDK
 *
 *			Authors: <NAME>
 *			Copyright (c) Telecom ParisTech 2017-2018
 *					All rights reserved
 *
 *  This file is part of GPAC / NHML stream to file filter
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <gpac/filters.h>
#include <gpac/constants.h>
#include <gpac/bitstream.h>
#include <gpac/base_coding.h>

#include <gpac/internal/isomedia_dev.h>

#ifndef GPAC_DISABLE_ZLIB
#include <zlib.h>
#endif

typedef struct
{
	//opts
	const char *name;
	Bool exporter, dims, full, nhmlonly, chksum;
	FILE *filep;


	//only one input pid declared
	GF_FilterPid *ipid;
	//only one output pid declared
	GF_FilterPid *opid_nhml, *opid_mdia, *opid_info;

	u32 codecid;
	u32 streamtype;
	u32 oti;
	u32 chan, sr, bps, w, h;
	const char *dcfg;
	u32 dcfg_size;
	char *media_file, *info_file;
	const char *szRootName;

	GF_Fraction duration;
	Bool first;
	Bool uncompress, is_dims, is_stpp;

	GF_BitStream *bs_w, *bs_r;
	char *nhml_buffer;
	u32 nhml_buffer_size;
	char *b64_buffer;
	u32 b64_buffer_size;
	u64 mdia_pos;
} GF_NHMLDumpCtx;

GF_Err nhmldump_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
{
	u32 cid;
	char *mime, *name;
	char fileName[1024];
	const GF_PropertyValue *p;
	GF_NHMLDumpCtx *ctx = gf_filter_get_udta(filter);

	if (is_remove) {
		ctx->ipid = NULL;
		gf_filter_pid_remove(ctx->opid_nhml);
		gf_filter_pid_remove(ctx->opid_mdia);
		if (ctx->opid_info) gf_filter_pid_remove(ctx->opid_info);
		return GF_OK;
	}
	if (! gf_filter_pid_check_caps(pid))
		return GF_NOT_SUPPORTED;

	p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
	if (!p) return GF_NOT_SUPPORTED;
	cid = p->value.uint;

	if (ctx->codecid == cid) {
		return GF_OK;
	}
	ctx->codecid = cid;

	if (ctx->codecid<GF_CODECID_LAST_MPEG4_MAPPING) ctx->oti = ctx->codecid;
	else {
		ctx->oti = gf_codecid_oti(ctx->codecid);
	}
	if (!ctx->oti) {
		GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("CodecID %s has no mapping to MPEG-4 systems, cannot use NHNT. Use NHML instead\n", gf_4cc_to_str(cid) ));
		return GF_NOT_SUPPORTED;
	}

	p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
	ctx->streamtype = p ? p->value.uint : GF_STREAM_UNKNOWN;

	if (!ctx->opid_nhml && !ctx->filep)
		ctx->opid_nhml = gf_filter_pid_new(filter);

	ctx->is_dims = GF_FALSE;
	if ((ctx->codecid == GF_CODECID_DIMS) && ctx->dims) {
		gf_filter_pid_remove(ctx->opid_mdia);
		ctx->opid_mdia = NULL;
		ctx->is_dims = GF_TRUE;
	} else if (!ctx->opid_mdia && !ctx->nhmlonly)
		ctx->opid_mdia = gf_filter_pid_new(filter);

	//file pointer set, we act as a sink, send play
	if (!ctx->ipid && ctx->filep) {
		GF_FilterEvent evt;
		GF_FEVT_INIT(evt, GF_FEVT_PLAY, pid);
		gf_filter_pid_send_event(pid, &evt);
	}
	ctx->ipid = pid;


	p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
	ctx->sr = p ? p->value.uint : 0;
	p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
	ctx->chan = p ? p->value.uint : 0;
	p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
	ctx->bps = p ? gf_audio_fmt_bit_depth(p->value.uint) : 16;
	p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
	ctx->w = p ? p->value.uint : 0;
	p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
	ctx->h = p ? p->value.uint : 0;

	p = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
	if (p) {
		ctx->dcfg = p->value.data.ptr;
		ctx->dcfg_size = p->value.data.size;

		if (!ctx->opid_info && !ctx->nhmlonly)
			ctx->opid_info = gf_filter_pid_new(filter);

	} else if (ctx->opid_info) {
		if (ctx->opid_info) gf_filter_pid_remove(ctx->opid_info);
	}
	if (ctx->info_file) gf_free(ctx->info_file);
	ctx->info_file = NULL;

	name = (char*) gf_codecid_name(ctx->codecid);
	if (ctx->exporter) {
		if (ctx->w && ctx->h) {
			GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("Exporting %s - Size %dx%d\n", name, ctx->w, ctx->h));
		} else if (ctx->sr && ctx->chan) {
			GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("Exporting %s - SampleRate %d %d channels %d bits per sample\n", name, ctx->sr, ctx->chan, ctx->bps));
		} else {
			GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("Exporting %s\n", name));
		}
	}

	if (ctx->opid_nhml) {
		mime = ctx->dims ? "application/dims" : "application/x-nhnt";
		strncpy(fileName, ctx->name, 1024);
		name = strrchr(fileName, '.');
		if (name) {
			name[0] = 0;
			gf_filter_pid_set_property(ctx->opid_nhml, GF_PROP_PID_OUTPATH, &PROP_STRING(ctx->name) );
		} else {
			strcat(fileName, ".nhml");
			gf_filter_pid_set_property(ctx->opid_nhml, GF_PROP_PID_OUTPATH, &PROP_STRING(fileName) );
			name = strrchr(fileName, '.');
		}

		gf_filter_pid_set_property(ctx->opid_nhml, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
		gf_filter_pid_set_property(ctx->opid_nhml, GF_PROP_PID_MIME, &PROP_STRING(mime) );
	}

	if (ctx->opid_mdia) {
		gf_filter_pid_set_property(ctx->opid_mdia, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
		gf_filter_pid_set_property(ctx->opid_mdia, GF_PROP_PID_MIME, &PROP_STRING(mime) );
		name[0] = 0;
		strcat(fileName, ".media");
		gf_filter_pid_set_property(ctx->opid_mdia, GF_PROP_PID_OUTPATH, &PROP_STRING(fileName) );
		if (ctx->media_file) gf_free(ctx->media_file);
		ctx->media_file = gf_strdup(fileName);
	}

	if (ctx->opid_info) {
		gf_filter_pid_set_property(ctx->opid_info, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
		gf_filter_pid_set_property(ctx->opid_info, GF_PROP_PID_MIME, &PROP_STRING(mime) );
		name[0] = 0;
		strcat(fileName, ".info");
		gf_filter_pid_set_property(ctx->opid_info, GF_PROP_PID_OUTPATH, &PROP_STRING(fileName) );
		ctx->info_file = gf_strdup(fileName);
	}

	ctx->first = GF_TRUE;
	ctx->is_stpp = (cid==GF_CODECID_SUBS_XML) ? GF_TRUE : GF_FALSE;

	p = gf_filter_pid_get_property(pid, GF_PROP_PID_DURATION);
	if (p) ctx->duration = p->value.frac;
	return GF_OK;
}

#define NHML_PRINT_4CC(_code, _pname, _name) \
		if (_code) p = gf_filter_pid_get_property(ctx->ipid, _code); \
		else p = gf_filter_pid_get_property_str(ctx->ipid, _pname); \
		if (p) { \
			sprintf(nhml, "%s=\"%s\" ", _name, gf_4cc_to_str(p->value.uint)); \
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml)); \
		}

#define NHML_PRINT_UINT(_code, _pname, _name) \
		if (_code) p = gf_filter_pid_get_property(ctx->ipid, _code); \
		else p = gf_filter_pid_get_property_str(ctx->ipid, _pname); \
		if (p) { \
			sprintf(nhml, "%s=\"%d\" ", _name, p->value.uint); \
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml)); \
		}

#define NHML_PRINT_STRING(_code, _pname, _name) \
		if (_code) p = gf_filter_pid_get_property(ctx->ipid, _code); \
		else p = gf_filter_pid_get_property_str(ctx->ipid, _pname); \
		if (p) { \
			sprintf(nhml, "%s=\"%s\" ", _name, p->value.string); \
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml)); \
		}



static void nhmldump_send_header(GF_NHMLDumpCtx *ctx)
{
	GF_FilterPacket *dst_pck;
	char nhml[1024];
	u32 size;
	char *output;
	const GF_PropertyValue *p;

	ctx->szRootName = "NHNTStream";
	if (ctx->dims) {
		ctx->szRootName = "DIMSStream";
	} else if (!ctx->filep) {
		sprintf(nhml, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
	}

	gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));

	/*write header*/
	sprintf(nhml, "<%s version=\"1.0\" ", ctx->szRootName);
	gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));


	NHML_PRINT_UINT(GF_PROP_PID_ID, NULL, "trackID")
	NHML_PRINT_UINT(GF_PROP_PID_TIMESCALE, NULL, "timeScale")

	p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_IN_IOD);
	if (p && p->value.boolean) {
		sprintf(nhml, "inRootOD=\"yes\" ");
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	}

	if (ctx->oti<GF_CODECID_LAST_MPEG4_MAPPING) {
		sprintf(nhml, "streamType=\"%d\" objectTypeIndication=\"%d\" ", ctx->streamtype, ctx->oti);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	} else {
		NHML_PRINT_4CC(0, "media_type", "mediaType")
		NHML_PRINT_4CC(0, "media_subtype", "mediaSubType")
	}

	if (ctx->w && ctx->h) {
		sprintf(nhml, "width=\"%d\" height=\"%d\" ", ctx->w, ctx->h);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	}
	else if (ctx->sr && ctx->chan) {
		sprintf(nhml, "sampleRate=\"%d\" numChannels=\"%d\" ", ctx->sr, ctx->chan);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		sprintf(nhml, "sampleRate=\"%d\" numChannels=\"%d\" ", ctx->sr, ctx->chan);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_AUDIO_FORMAT);
		sprintf(nhml, "bitsPerSample=\"%d\" ", gf_audio_fmt_bit_depth(p->value.uint));
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	}

	NHML_PRINT_4CC(0, "codec_vendor", "codecVendor")
	NHML_PRINT_UINT(0, "codec_version", "codecVersion")
	NHML_PRINT_UINT(0, "codec_revision", "codecRevision")
	NHML_PRINT_STRING(0, "compressor_name", "compressorName")
	NHML_PRINT_UINT(0, "temporal_quality", "temporalQuality")
	NHML_PRINT_UINT(0, "spatial_quality", "spatialQuality")
	NHML_PRINT_UINT(0, "hres", "horizontalResolution")
	NHML_PRINT_UINT(0, "vres", "verticalResolution")
	NHML_PRINT_UINT(GF_PROP_PID_BIT_DEPTH_Y, NULL, "bitDepth")

	NHML_PRINT_STRING(0, "meta:xmlns", "xml_namespace")
	NHML_PRINT_STRING(0, "meta:schemaloc", "xml_schema_location")
	NHML_PRINT_STRING(0, "meta:mime", "mime_type")
	NHML_PRINT_STRING(0, "meta:encoding", "encoding")
	NHML_PRINT_STRING(0, "meta:contentEncoding", "content_encoding")
	ctx->uncompress = GF_FALSE;
	if (p) {
		if (!strcmp(p->value.string, "deflate")) ctx->uncompress = GF_TRUE;
		else {
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[NHMLMx] content_encoding %s not supported\n", p->value.string ));
		}
	}
	NHML_PRINT_STRING(0, "meta:config", "config")
	NHML_PRINT_STRING(0, "meta:aux_mimes", "aux_mime_type")

	if (ctx->codecid == GF_CODECID_DIMS) {
		if (gf_filter_pid_get_property_str(ctx->ipid, "meta:xmlns")==NULL) {
			sprintf(nhml, "xmlns=\"http://www.3gpp.org/richmedia\" ");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}

		NHML_PRINT_UINT(0, "dims:profile", "profile")
		NHML_PRINT_UINT(0, "dims:level", "level")
		NHML_PRINT_UINT(0, "dims:pathComponents", "pathComponents")

		p = gf_filter_pid_get_property_str(ctx->ipid, "dims:fullRequestHost");
		if (p) {
			sprintf(nhml, "useFullRequestHost=\"%s\" ", p->value.boolean ? "yes" : "no");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		p = gf_filter_pid_get_property_str(ctx->ipid, "dims:streamType");
		if (p) {
			sprintf(nhml, "stream_type=\"%s\" ", p->value.boolean ? "primary" : "secondary");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		p = gf_filter_pid_get_property_str(ctx->ipid, "dims:redundant");
		if (p) {
			sprintf(nhml, "contains_redundant=\"%s\" ", (p->value.uint==1) ? "main" : ((p->value.uint==1) ? "redundant" : "main+redundant") );
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		NHML_PRINT_UINT(0, "dims:scriptTypes", "scriptTypes")
	}

	//send DCD
	if (ctx->opid_info) {
		sprintf(nhml, "specificInfoFile=\"%s\" ", ctx->info_file);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));

		dst_pck = gf_filter_pck_new_shared(ctx->opid_info, ctx->dcfg, ctx->dcfg_size, NULL);
		gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_TRUE);
		gf_filter_pck_send(dst_pck);
	}

	if (ctx->opid_mdia) {
		sprintf(nhml, "baseMediaFile=\"%s\" ", ctx->media_file);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	}
	sprintf(nhml, ">\n");
	gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));

	gf_bs_get_content_no_truncate(ctx->bs_w, &ctx->nhml_buffer, &size, &ctx->nhml_buffer_size);

	if (ctx->filep) {
		fwrite(ctx->nhml_buffer, 1, size, ctx->filep);
		return;
	}

	dst_pck = gf_filter_pck_new_alloc(ctx->opid_nhml, size, &output);
	memcpy(output, ctx->nhml_buffer, size);
	gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_FALSE);
	gf_filter_pck_send(dst_pck);
}

static void nhmldump_send_dims(GF_NHMLDumpCtx *ctx, char *data, u32 data_size, GF_FilterPacket *pck)
{
	char nhml[1024];
	u32 size;
	char *output;
	GF_FilterPacket *dst_pck;
	u64 dts = gf_filter_pck_get_dts(pck);
	u64 cts = gf_filter_pck_get_cts(pck);

	if (dts==GF_FILTER_NO_TS) dts = cts;
	if (cts==GF_FILTER_NO_TS) cts = dts;

	if (!ctx->bs_r) ctx->bs_r = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
	else gf_bs_reassign_buffer(ctx->bs_r, data, data_size);

	while (gf_bs_available(ctx->bs_r)) {
		u64 pos = gf_bs_get_position(ctx->bs_r);
		u16 size = gf_bs_read_u16(ctx->bs_r);
		u8 flags = gf_bs_read_u8(ctx->bs_r);
		u8 prev;

		if (pos+size+2 > data_size)
			break;

		prev = data[pos+2+size];
		data[pos+2+size] = 0;


		sprintf(nhml, "<DIMSUnit time=\""LLU"\"", cts);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));

		/*DIMS flags*/
		if (flags & GF_DIMS_UNIT_S) {
			sprintf(nhml, " is-Scene=\"yes\"");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		if (flags & GF_DIMS_UNIT_M) {
			sprintf(nhml, " is-RAP=\"yes\"");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		if (flags & GF_DIMS_UNIT_I) {
			sprintf(nhml, " is-redundant=\"yes\"");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		if (flags & GF_DIMS_UNIT_D) {
			sprintf(nhml, " redundant-exit=\"yes\"");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		if (flags & GF_DIMS_UNIT_P) {
			sprintf(nhml, " priority=\"high\"");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		if (flags & GF_DIMS_UNIT_C) {
			sprintf(nhml, " compressed=\"yes\"");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		sprintf(nhml, ">");
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		if (ctx->uncompress && (flags & GF_DIMS_UNIT_C)) {
#ifndef GPAC_DISABLE_ZLIB
			char svg_data[2049];
			int err;
			u32 done = 0;
			z_stream d_stream;
			d_stream.zalloc = (alloc_func)0;
			d_stream.zfree = (free_func)0;
			d_stream.opaque = (voidpf)0;
			d_stream.next_in  = (Bytef*) data+pos+3;
			d_stream.avail_in = size-1;
			d_stream.next_out = (Bytef*)svg_data;
			d_stream.avail_out = 2048;

			err = inflateInit(&d_stream);
			if (err == Z_OK) {
				while ((s32) d_stream.total_in < size-1) {
					err = inflate(&d_stream, Z_NO_FLUSH);
					if (err < Z_OK) break;
					svg_data[d_stream.total_out - done] = 0;
					gf_bs_write_data(ctx->bs_w, svg_data, strlen(svg_data));

					if (err== Z_STREAM_END) break;
					done = (u32) d_stream.total_out;
					d_stream.avail_out = 2048;
					d_stream.next_out = (Bytef*)svg_data;
				}
				inflateEnd(&d_stream);
			}
#else
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Error: your version of GPAC was compile with no libz support."));
			gf_bs_del(bs);
			gf_isom_sample_del(&samp);
			if (med) gf_fclose(med);
			gf_fclose(nhml);
			return GF_NOT_SUPPORTED;
#endif
		} else {
			gf_bs_write_data(ctx->bs_w, data+pos+3, size-1);
		}
		sprintf(nhml, "</DIMSUnit>\n");
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));

		data[pos+2+size] = prev;
		gf_bs_skip_bytes(ctx->bs_r, size-1);
	}

	gf_bs_get_content_no_truncate(ctx->bs_w, &ctx->nhml_buffer, &size, &ctx->nhml_buffer_size);

	if (ctx->filep) {
		fwrite(ctx->nhml_buffer, 1, size, ctx->filep);
		return;
	}

	dst_pck = gf_filter_pck_new_alloc(ctx->opid_nhml, size, &output);
	memcpy(output, ctx->nhml_buffer, size);
	gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_FALSE);
	gf_filter_pck_send(dst_pck);
}


static void nhmldump_pck_property(GF_NHMLDumpCtx *ctx, u32 p4cc, const char *pname, const GF_PropertyValue *att)
{
	u32 i;
	char nhml[1024];
	if (!pname) pname = gf_props_4cc_get_name(p4cc);

	sprintf(nhml, "%s=\"", pname ? pname : gf_4cc_to_str(p4cc));
	gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	switch (att->type) {
	case GF_PROP_SINT:
		sprintf(nhml, "%d", att->value.sint);
		break;
	case GF_PROP_UINT:
		sprintf(nhml, "%u", att->value.uint);
		break;
	case GF_PROP_LSINT:
		sprintf(nhml, ""LLD, att->value.longsint);
		break;
	case GF_PROP_LUINT:
		sprintf(nhml, ""LLU, att->value.longuint);
		break;
	case GF_PROP_FRACTION:
		sprintf(nhml, "%d/%u", att->value.frac.num, att->value.frac.den);
		break;
	case GF_PROP_BOOL:
		sprintf(nhml, "%s", att->value.boolean ? "true" : "false");
		break;
	case GF_PROP_FLOAT:
		sprintf(nhml, "%f", FIX2FLT(att->value.fnumber) );
		break;
	case GF_PROP_DOUBLE:
		sprintf(nhml, "%g", att->value.number);
		break;
	case GF_PROP_PIXFMT:
		sprintf(nhml, "%s", gf_pixfmt_name(att->value.uint));
		break;
	case GF_PROP_VEC2I:
		sprintf(nhml, "%dx%d", att->value.vec2i.x, att->value.vec2i.y);
		break;
	case GF_PROP_VEC2:
		sprintf(nhml, "%lgx%lg", att->value.vec2.x, att->value.vec2.y);
		break;
	case GF_PROP_VEC3I:
		sprintf(nhml, "%dx%dx%d", att->value.vec3i.x, att->value.vec3i.y, att->value.vec3i.z);
		break;
	case GF_PROP_VEC3:
		sprintf(nhml, "%lgx%lgx%lg", att->value.vec3.x, att->value.vec3.y, att->value.vec3.y);
		break;
	case GF_PROP_VEC4I:
		sprintf(nhml, "%dx%dx%dx%d", att->value.vec4i.x, att->value.vec4i.y, att->value.vec4i.z, att->value.vec4i.w);
		break;
	case GF_PROP_VEC4:
		sprintf(nhml, "%lgx%lgx%lgx%lg", att->value.vec4.x, att->value.vec4.y, att->value.vec4.y, att->value.vec4.w);
		break;
	case GF_PROP_NAME:
		sprintf(nhml, "%s", att->value.string);
		break;
	case GF_PROP_STRING:
	case GF_PROP_STRING_NO_COPY:
		sprintf(nhml, "%s", att->value.string);
		break;
	case GF_PROP_DATA:
	case GF_PROP_CONST_DATA:
	case GF_PROP_DATA_NO_COPY:
		sprintf(nhml, "0x");
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		for (i=0; i<att->value.data.size; i++) {
			sprintf(nhml, "%02X", (unsigned char) att->value.data.ptr[i]);
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		nhml[0] = 0;
		break;
	case GF_PROP_POINTER:
		sprintf(nhml, "%p", att->value.ptr);
		break;
	case GF_PROP_FORBIDEN:
		sprintf(nhml, "FORBIDDEN");
		break;
	}
	gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	sprintf(nhml, "\"");
	gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));<commit_msg>prop dump cleanup<commit_after>/*
 *			GPAC - Multimedia Framework C SDK
 *
 *			Authors: <NAME>
 *			Copyright (c) Telecom ParisTech 2017-2018
 *					All rights reserved
 *
 *  This file is part of GPAC / NHML stream to file filter
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <gpac/filters.h>
#include <gpac/constants.h>
#include <gpac/bitstream.h>
#include <gpac/base_coding.h>

#include <gpac/internal/isomedia_dev.h>

#ifndef GPAC_DISABLE_ZLIB
#include <zlib.h>
#endif

typedef struct
{
	//opts
	const char *name;
	Bool exporter, dims, full, nhmlonly, chksum;
	FILE *filep;


	//only one input pid declared
	GF_FilterPid *ipid;
	//only one output pid declared
	GF_FilterPid *opid_nhml, *opid_mdia, *opid_info;

	u32 codecid;
	u32 streamtype;
	u32 oti;
	u32 chan, sr, bps, w, h;
	const char *dcfg;
	u32 dcfg_size;
	char *media_file, *info_file;
	const char *szRootName;

	GF_Fraction duration;
	Bool first;
	Bool uncompress, is_dims, is_stpp;

	GF_BitStream *bs_w, *bs_r;
	char *nhml_buffer;
	u32 nhml_buffer_size;
	char *b64_buffer;
	u32 b64_buffer_size;
	u64 mdia_pos;
} GF_NHMLDumpCtx;

GF_Err nhmldump_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
{
	u32 cid;
	char *mime, *name;
	char fileName[1024];
	const GF_PropertyValue *p;
	GF_NHMLDumpCtx *ctx = gf_filter_get_udta(filter);

	if (is_remove) {
		ctx->ipid = NULL;
		gf_filter_pid_remove(ctx->opid_nhml);
		gf_filter_pid_remove(ctx->opid_mdia);
		if (ctx->opid_info) gf_filter_pid_remove(ctx->opid_info);
		return GF_OK;
	}
	if (! gf_filter_pid_check_caps(pid))
		return GF_NOT_SUPPORTED;

	p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
	if (!p) return GF_NOT_SUPPORTED;
	cid = p->value.uint;

	if (ctx->codecid == cid) {
		return GF_OK;
	}
	ctx->codecid = cid;

	if (ctx->codecid<GF_CODECID_LAST_MPEG4_MAPPING) ctx->oti = ctx->codecid;
	else {
		ctx->oti = gf_codecid_oti(ctx->codecid);
	}
	if (!ctx->oti) {
		GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("CodecID %s has no mapping to MPEG-4 systems, cannot use NHNT. Use NHML instead\n", gf_4cc_to_str(cid) ));
		return GF_NOT_SUPPORTED;
	}

	p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
	ctx->streamtype = p ? p->value.uint : GF_STREAM_UNKNOWN;

	if (!ctx->opid_nhml && !ctx->filep)
		ctx->opid_nhml = gf_filter_pid_new(filter);

	ctx->is_dims = GF_FALSE;
	if ((ctx->codecid == GF_CODECID_DIMS) && ctx->dims) {
		gf_filter_pid_remove(ctx->opid_mdia);
		ctx->opid_mdia = NULL;
		ctx->is_dims = GF_TRUE;
	} else if (!ctx->opid_mdia && !ctx->nhmlonly)
		ctx->opid_mdia = gf_filter_pid_new(filter);

	//file pointer set, we act as a sink, send play
	if (!ctx->ipid && ctx->filep) {
		GF_FilterEvent evt;
		GF_FEVT_INIT(evt, GF_FEVT_PLAY, pid);
		gf_filter_pid_send_event(pid, &evt);
	}
	ctx->ipid = pid;


	p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
	ctx->sr = p ? p->value.uint : 0;
	p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
	ctx->chan = p ? p->value.uint : 0;
	p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
	ctx->bps = p ? gf_audio_fmt_bit_depth(p->value.uint) : 16;
	p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
	ctx->w = p ? p->value.uint : 0;
	p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
	ctx->h = p ? p->value.uint : 0;

	p = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
	if (p) {
		ctx->dcfg = p->value.data.ptr;
		ctx->dcfg_size = p->value.data.size;

		if (!ctx->opid_info && !ctx->nhmlonly)
			ctx->opid_info = gf_filter_pid_new(filter);

	} else if (ctx->opid_info) {
		if (ctx->opid_info) gf_filter_pid_remove(ctx->opid_info);
	}
	if (ctx->info_file) gf_free(ctx->info_file);
	ctx->info_file = NULL;

	name = (char*) gf_codecid_name(ctx->codecid);
	if (ctx->exporter) {
		if (ctx->w && ctx->h) {
			GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("Exporting %s - Size %dx%d\n", name, ctx->w, ctx->h));
		} else if (ctx->sr && ctx->chan) {
			GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("Exporting %s - SampleRate %d %d channels %d bits per sample\n", name, ctx->sr, ctx->chan, ctx->bps));
		} else {
			GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("Exporting %s\n", name));
		}
	}

	if (ctx->opid_nhml) {
		mime = ctx->dims ? "application/dims" : "application/x-nhnt";
		strncpy(fileName, ctx->name, 1024);
		name = strrchr(fileName, '.');
		if (name) {
			name[0] = 0;
			gf_filter_pid_set_property(ctx->opid_nhml, GF_PROP_PID_OUTPATH, &PROP_STRING(ctx->name) );
		} else {
			strcat(fileName, ".nhml");
			gf_filter_pid_set_property(ctx->opid_nhml, GF_PROP_PID_OUTPATH, &PROP_STRING(fileName) );
			name = strrchr(fileName, '.');
		}

		gf_filter_pid_set_property(ctx->opid_nhml, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
		gf_filter_pid_set_property(ctx->opid_nhml, GF_PROP_PID_MIME, &PROP_STRING(mime) );
	}

	if (ctx->opid_mdia) {
		gf_filter_pid_set_property(ctx->opid_mdia, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
		gf_filter_pid_set_property(ctx->opid_mdia, GF_PROP_PID_MIME, &PROP_STRING(mime) );
		name[0] = 0;
		strcat(fileName, ".media");
		gf_filter_pid_set_property(ctx->opid_mdia, GF_PROP_PID_OUTPATH, &PROP_STRING(fileName) );
		if (ctx->media_file) gf_free(ctx->media_file);
		ctx->media_file = gf_strdup(fileName);
	}

	if (ctx->opid_info) {
		gf_filter_pid_set_property(ctx->opid_info, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
		gf_filter_pid_set_property(ctx->opid_info, GF_PROP_PID_MIME, &PROP_STRING(mime) );
		name[0] = 0;
		strcat(fileName, ".info");
		gf_filter_pid_set_property(ctx->opid_info, GF_PROP_PID_OUTPATH, &PROP_STRING(fileName) );
		ctx->info_file = gf_strdup(fileName);
	}

	ctx->first = GF_TRUE;
	ctx->is_stpp = (cid==GF_CODECID_SUBS_XML) ? GF_TRUE : GF_FALSE;

	p = gf_filter_pid_get_property(pid, GF_PROP_PID_DURATION);
	if (p) ctx->duration = p->value.frac;
	return GF_OK;
}

#define NHML_PRINT_4CC(_code, _pname, _name) \
		if (_code) p = gf_filter_pid_get_property(ctx->ipid, _code); \
		else p = gf_filter_pid_get_property_str(ctx->ipid, _pname); \
		if (p) { \
			sprintf(nhml, "%s=\"%s\" ", _name, gf_4cc_to_str(p->value.uint)); \
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml)); \
		}

#define NHML_PRINT_UINT(_code, _pname, _name) \
		if (_code) p = gf_filter_pid_get_property(ctx->ipid, _code); \
		else p = gf_filter_pid_get_property_str(ctx->ipid, _pname); \
		if (p) { \
			sprintf(nhml, "%s=\"%d\" ", _name, p->value.uint); \
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml)); \
		}

#define NHML_PRINT_STRING(_code, _pname, _name) \
		if (_code) p = gf_filter_pid_get_property(ctx->ipid, _code); \
		else p = gf_filter_pid_get_property_str(ctx->ipid, _pname); \
		if (p) { \
			sprintf(nhml, "%s=\"%s\" ", _name, p->value.string); \
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml)); \
		}



static void nhmldump_send_header(GF_NHMLDumpCtx *ctx)
{
	GF_FilterPacket *dst_pck;
	char nhml[1024];
	u32 size;
	char *output;
	const GF_PropertyValue *p;

	ctx->szRootName = "NHNTStream";
	if (ctx->dims) {
		ctx->szRootName = "DIMSStream";
	} else if (!ctx->filep) {
		sprintf(nhml, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
	}

	gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));

	/*write header*/
	sprintf(nhml, "<%s version=\"1.0\" ", ctx->szRootName);
	gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));


	NHML_PRINT_UINT(GF_PROP_PID_ID, NULL, "trackID")
	NHML_PRINT_UINT(GF_PROP_PID_TIMESCALE, NULL, "timeScale")

	p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_IN_IOD);
	if (p && p->value.boolean) {
		sprintf(nhml, "inRootOD=\"yes\" ");
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	}

	if (ctx->oti<GF_CODECID_LAST_MPEG4_MAPPING) {
		sprintf(nhml, "streamType=\"%d\" objectTypeIndication=\"%d\" ", ctx->streamtype, ctx->oti);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	} else {
		NHML_PRINT_4CC(0, "media_type", "mediaType")
		NHML_PRINT_4CC(0, "media_subtype", "mediaSubType")
	}

	if (ctx->w && ctx->h) {
		sprintf(nhml, "width=\"%d\" height=\"%d\" ", ctx->w, ctx->h);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	}
	else if (ctx->sr && ctx->chan) {
		sprintf(nhml, "sampleRate=\"%d\" numChannels=\"%d\" ", ctx->sr, ctx->chan);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		sprintf(nhml, "sampleRate=\"%d\" numChannels=\"%d\" ", ctx->sr, ctx->chan);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_AUDIO_FORMAT);
		sprintf(nhml, "bitsPerSample=\"%d\" ", gf_audio_fmt_bit_depth(p->value.uint));
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	}

	NHML_PRINT_4CC(0, "codec_vendor", "codecVendor")
	NHML_PRINT_UINT(0, "codec_version", "codecVersion")
	NHML_PRINT_UINT(0, "codec_revision", "codecRevision")
	NHML_PRINT_STRING(0, "compressor_name", "compressorName")
	NHML_PRINT_UINT(0, "temporal_quality", "temporalQuality")
	NHML_PRINT_UINT(0, "spatial_quality", "spatialQuality")
	NHML_PRINT_UINT(0, "hres", "horizontalResolution")
	NHML_PRINT_UINT(0, "vres", "verticalResolution")
	NHML_PRINT_UINT(GF_PROP_PID_BIT_DEPTH_Y, NULL, "bitDepth")

	NHML_PRINT_STRING(0, "meta:xmlns", "xml_namespace")
	NHML_PRINT_STRING(0, "meta:schemaloc", "xml_schema_location")
	NHML_PRINT_STRING(0, "meta:mime", "mime_type")
	NHML_PRINT_STRING(0, "meta:encoding", "encoding")
	NHML_PRINT_STRING(0, "meta:contentEncoding", "content_encoding")
	ctx->uncompress = GF_FALSE;
	if (p) {
		if (!strcmp(p->value.string, "deflate")) ctx->uncompress = GF_TRUE;
		else {
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[NHMLMx] content_encoding %s not supported\n", p->value.string ));
		}
	}
	NHML_PRINT_STRING(0, "meta:config", "config")
	NHML_PRINT_STRING(0, "meta:aux_mimes", "aux_mime_type")

	if (ctx->codecid == GF_CODECID_DIMS) {
		if (gf_filter_pid_get_property_str(ctx->ipid, "meta:xmlns")==NULL) {
			sprintf(nhml, "xmlns=\"http://www.3gpp.org/richmedia\" ");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}

		NHML_PRINT_UINT(0, "dims:profile", "profile")
		NHML_PRINT_UINT(0, "dims:level", "level")
		NHML_PRINT_UINT(0, "dims:pathComponents", "pathComponents")

		p = gf_filter_pid_get_property_str(ctx->ipid, "dims:fullRequestHost");
		if (p) {
			sprintf(nhml, "useFullRequestHost=\"%s\" ", p->value.boolean ? "yes" : "no");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		p = gf_filter_pid_get_property_str(ctx->ipid, "dims:streamType");
		if (p) {
			sprintf(nhml, "stream_type=\"%s\" ", p->value.boolean ? "primary" : "secondary");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		p = gf_filter_pid_get_property_str(ctx->ipid, "dims:redundant");
		if (p) {
			sprintf(nhml, "contains_redundant=\"%s\" ", (p->value.uint==1) ? "main" : ((p->value.uint==1) ? "redundant" : "main+redundant") );
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		NHML_PRINT_UINT(0, "dims:scriptTypes", "scriptTypes")
	}

	//send DCD
	if (ctx->opid_info) {
		sprintf(nhml, "specificInfoFile=\"%s\" ", ctx->info_file);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));

		dst_pck = gf_filter_pck_new_shared(ctx->opid_info, ctx->dcfg, ctx->dcfg_size, NULL);
		gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_TRUE);
		gf_filter_pck_send(dst_pck);
	}

	if (ctx->opid_mdia) {
		sprintf(nhml, "baseMediaFile=\"%s\" ", ctx->media_file);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	}
	sprintf(nhml, ">\n");
	gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));

	gf_bs_get_content_no_truncate(ctx->bs_w, &ctx->nhml_buffer, &size, &ctx->nhml_buffer_size);

	if (ctx->filep) {
		fwrite(ctx->nhml_buffer, 1, size, ctx->filep);
		return;
	}

	dst_pck = gf_filter_pck_new_alloc(ctx->opid_nhml, size, &output);
	memcpy(output, ctx->nhml_buffer, size);
	gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_FALSE);
	gf_filter_pck_send(dst_pck);
}

static void nhmldump_send_dims(GF_NHMLDumpCtx *ctx, char *data, u32 data_size, GF_FilterPacket *pck)
{
	char nhml[1024];
	u32 size;
	char *output;
	GF_FilterPacket *dst_pck;
	u64 dts = gf_filter_pck_get_dts(pck);
	u64 cts = gf_filter_pck_get_cts(pck);

	if (dts==GF_FILTER_NO_TS) dts = cts;
	if (cts==GF_FILTER_NO_TS) cts = dts;

	if (!ctx->bs_r) ctx->bs_r = gf_bs_new(data, data_size, GF_BITSTREAM_READ);
	else gf_bs_reassign_buffer(ctx->bs_r, data, data_size);

	while (gf_bs_available(ctx->bs_r)) {
		u64 pos = gf_bs_get_position(ctx->bs_r);
		u16 size = gf_bs_read_u16(ctx->bs_r);
		u8 flags = gf_bs_read_u8(ctx->bs_r);
		u8 prev;

		if (pos+size+2 > data_size)
			break;

		prev = data[pos+2+size];
		data[pos+2+size] = 0;


		sprintf(nhml, "<DIMSUnit time=\""LLU"\"", cts);
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));

		/*DIMS flags*/
		if (flags & GF_DIMS_UNIT_S) {
			sprintf(nhml, " is-Scene=\"yes\"");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		if (flags & GF_DIMS_UNIT_M) {
			sprintf(nhml, " is-RAP=\"yes\"");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		if (flags & GF_DIMS_UNIT_I) {
			sprintf(nhml, " is-redundant=\"yes\"");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		if (flags & GF_DIMS_UNIT_D) {
			sprintf(nhml, " redundant-exit=\"yes\"");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		if (flags & GF_DIMS_UNIT_P) {
			sprintf(nhml, " priority=\"high\"");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		if (flags & GF_DIMS_UNIT_C) {
			sprintf(nhml, " compressed=\"yes\"");
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		sprintf(nhml, ">");
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		if (ctx->uncompress && (flags & GF_DIMS_UNIT_C)) {
#ifndef GPAC_DISABLE_ZLIB
			char svg_data[2049];
			int err;
			u32 done = 0;
			z_stream d_stream;
			d_stream.zalloc = (alloc_func)0;
			d_stream.zfree = (free_func)0;
			d_stream.opaque = (voidpf)0;
			d_stream.next_in  = (Bytef*) data+pos+3;
			d_stream.avail_in = size-1;
			d_stream.next_out = (Bytef*)svg_data;
			d_stream.avail_out = 2048;

			err = inflateInit(&d_stream);
			if (err == Z_OK) {
				while ((s32) d_stream.total_in < size-1) {
					err = inflate(&d_stream, Z_NO_FLUSH);
					if (err < Z_OK) break;
					svg_data[d_stream.total_out - done] = 0;
					gf_bs_write_data(ctx->bs_w, svg_data, strlen(svg_data));

					if (err== Z_STREAM_END) break;
					done = (u32) d_stream.total_out;
					d_stream.avail_out = 2048;
					d_stream.next_out = (Bytef*)svg_data;
				}
				inflateEnd(&d_stream);
			}
#else
			GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Error: your version of GPAC was compile with no libz support."));
			gf_bs_del(bs);
			gf_isom_sample_del(&samp);
			if (med) gf_fclose(med);
			gf_fclose(nhml);
			return GF_NOT_SUPPORTED;
#endif
		} else {
			gf_bs_write_data(ctx->bs_w, data+pos+3, size-1);
		}
		sprintf(nhml, "</DIMSUnit>\n");
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));

		data[pos+2+size] = prev;
		gf_bs_skip_bytes(ctx->bs_r, size-1);
	}

	gf_bs_get_content_no_truncate(ctx->bs_w, &ctx->nhml_buffer, &size, &ctx->nhml_buffer_size);

	if (ctx->filep) {
		fwrite(ctx->nhml_buffer, 1, size, ctx->filep);
		return;
	}

	dst_pck = gf_filter_pck_new_alloc(ctx->opid_nhml, size, &output);
	memcpy(output, ctx->nhml_buffer, size);
	gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_FALSE);
	gf_filter_pck_send(dst_pck);
}


static void nhmldump_pck_property(GF_NHMLDumpCtx *ctx, u32 p4cc, const char *pname, const GF_PropertyValue *att)
{
	u32 i;
	char nhml[1024];
	char pval[100];
	if (!pname) pname = gf_props_4cc_get_name(p4cc);

	sprintf(nhml, "%s=\"", pname ? pname : gf_4cc_to_str(p4cc));
	gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));



	switch (att->type) {
	case GF_PROP_DATA:
	case GF_PROP_CONST_DATA:
	case GF_PROP_DATA_NO_COPY:
		sprintf(nhml, "0x");
		gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		for (i=0; i<att->value.data.size; i++) {
			sprintf(nhml, "%02X", (unsigned char) att->value.data.ptr[i]);
			gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
		}
		nhml[0] = 0;
		break;
	default:
		sprintf(nhml, "%s", gf_prop_dump_val(att, pval, GF_FALSE) );
		break;
	}
	gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
	sprintf(nhml, "\"");
	gf_bs_write_data(ctx->bs_w, nhml, strlen(nhml));
{'fraction_non_alphanumeric': 0.16241272748082866, 'fraction_numerical': 0.01104498111479913, 'mean_word_length': 4.963993174061433, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 2, '>': 462, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 12}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4790113', 'n_tokens_mistral': 16410, 'n_tokens_neox': 15275, 'n_words': 2582}