1 /*
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre
5  *			Copyright (c) Telecom ParisTech 2005-2020
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / NHML demuxer filter
9  *
10  *  GPAC is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU Lesser General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  GPAC is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; see the file COPYING.  If not, write to
22  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 
26 #include <gpac/filters.h>
27 #include <gpac/constants.h>
28 #include <gpac/thread.h>
29 #include <gpac/list.h>
30 #include <gpac/bitstream.h>
31 #include <gpac/xml.h>
32 #include <gpac/network.h>
33 #include <gpac/isomedia.h>
34 #include <gpac/base_coding.h>
35 
36 #ifndef GPAC_DISABLE_AV_PARSERS
37 #include <gpac/avparse.h>
38 #endif
39 
40 typedef struct
41 {
42 	//opts
43 	Bool reframe;
44 	Double index;
45 
46 	GF_FilterPid *ipid;
47 	GF_FilterPid *opid;
48 
49 	Bool is_dims;
50 
51 	Double start_range;
52 	u64 first_dts;
53 
54 	Bool is_playing;
55 	GF_Fraction64 duration;
56 	Bool in_seek;
57 
58 	u32 timescale;
59 	u32 sample_num;
60 
61 	FILE *mdia;
62 	char szMedia[GF_MAX_PATH];
63 
64 	GF_DOMParser *parser;
65 	GF_XMLNode *root;
66 	//0: not initialized, 1: OK, samples can be sent, 2: EOS, 3: error
67 	u32 parsing_state;
68 	u32 current_child_idx;
69 	Bool has_sap;
70 	u32 compress_type;
71 	const char *src_url;
72 	u64 last_dts;
73 	u32 dts_inc;
74 
75 	u8 *samp_buffer;
76 	u32 samp_buffer_alloc, samp_buffer_size;
77 	char *zlib_buffer;
78 	u32 zlib_buffer_alloc, zlib_buffer_size;
79 #ifndef GPAC_DISABLE_ZLIB
80 	Bool use_dict;
81 	char *dictionary;
82 #endif
83 
84 	u64 media_done;
85 	Bool is_img;
86 	u32 header_end;
87 
88 	GF_BitStream *bs_w;
89 	GF_BitStream *bs_r;
90 } GF_NHMLDmxCtx;
91 
92 
nhmldmx_configure_pid(GF_Filter * filter,GF_FilterPid * pid,Bool is_remove)93 GF_Err nhmldmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
94 {
95 	const GF_PropertyValue *p;
96 	GF_NHMLDmxCtx *ctx = gf_filter_get_udta(filter);
97 
98 	if (is_remove) {
99 		ctx->ipid = NULL;
100 		//gf_filter_pid_remove(st->opid);
101 
102 		return GF_OK;
103 	}
104 	if (! gf_filter_pid_check_caps(pid))
105 		return GF_NOT_SUPPORTED;
106 
107 	ctx->ipid = pid;
108 	gf_filter_pid_set_framing_mode(pid, GF_TRUE);
109 
110 	p = gf_filter_pid_get_property(pid, GF_PROP_PID_MIME);
111 	if (p && p->value.string && strstr(p->value.string, "dims")) ctx->is_dims = GF_TRUE;
112 	else {
113 		p = gf_filter_pid_get_property(pid, GF_PROP_PID_FILE_EXT);
114 		if (p && p->value.string && strstr(p->value.string, "dims")) ctx->is_dims = GF_TRUE;
115 	}
116 
117 	return GF_OK;
118 }
119 
nhmldmx_process_event(GF_Filter * filter,const GF_FilterEvent * evt)120 static Bool nhmldmx_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
121 {
122 	u32 i=0;
123 	u64 cur_dts = 0;
124 	u64 byte_offset = 0;
125 	u32 sample_num = 0;
126 	GF_XMLNode *node;
127 	GF_NHMLDmxCtx *ctx = gf_filter_get_udta(filter);
128 
129 	switch (evt->base.type) {
130 	case GF_FEVT_PLAY:
131 		if (ctx->is_playing && (ctx->start_range ==  evt->play.start_range)) {
132 			return GF_TRUE;
133 		}
134 
135 		ctx->start_range = evt->play.start_range;
136 		ctx->current_child_idx = 0;
137 		ctx->media_done = ctx->header_end;
138 		ctx->is_playing = GF_TRUE;
139 		//post a seek
140 		ctx->in_seek = GF_TRUE;
141 
142 		//lcoate previous RAP sample
143 		while ((node = (GF_XMLNode *) gf_list_enum(ctx->root->content, &i))) {
144 			u32 j=0;
145 			u64 dts=0;
146 			u32 datalen=0;
147 			Bool is_rap = ctx->has_sap ? GF_FALSE : GF_TRUE;
148 			s32 cts_offset=0;
149 			u64 sample_duration = 0;
150 			GF_XMLAttribute *att;
151 			if (node->type) continue;
152 			if (stricmp(node->name, ctx->is_dims ? "DIMSUnit" : "NHNTSample") ) continue;
153 
154 			while ( (att = (GF_XMLAttribute *)gf_list_enum(node->attributes, &j))) {
155 				if (!stricmp(att->name, "DTS") || !stricmp(att->name, "time")) {
156 					u32 h, m, s, ms;
157 					u64 dst_val;
158 					if (strchr(att->value, ':') && sscanf(att->value, "%u:%u:%u.%u", &h, &m, &s, &ms) == 4) {
159 						dts = (u64) ( (Double) ( ((h*3600.0 + m*60.0 + s)*1000 + ms) / 1000.0) * ctx->timescale );
160 					} else if (sscanf(att->value, ""LLU, &dst_val)==1) {
161 						dts = dst_val;
162 					}
163 				}
164 				else if (!stricmp(att->name, "CTSOffset")) cts_offset = atoi(att->value);
165 				else if (!stricmp(att->name, "duration") ) sscanf(att->value, ""LLU, &sample_duration);
166 				else if (!stricmp(att->name, "isRAP") ) {
167 					is_rap = (!stricmp(att->value, "yes")) ? GF_TRUE : GF_FALSE;
168 				}
169 				else if (!stricmp(att->name, "mediaOffset"))
170 					byte_offset = (s64) atof(att->value) ;
171 				else if (!stricmp(att->name, "dataLength"))
172 					datalen = atoi(att->value);
173 			}
174 
175 			dts += cts_offset;
176 			if ((s64) dts < 0) dts = 0;
177 
178 			if (dts) cur_dts = dts;
179 			if (sample_duration) cur_dts += sample_duration;
180 			else if (ctx->dts_inc) cur_dts += ctx->dts_inc;
181 
182 			if (cur_dts >= ctx->timescale * evt->play.start_range) {
183 				break;
184 			}
185 			if (is_rap) {
186 				ctx->current_child_idx = i-1;
187 				ctx->media_done = byte_offset;
188 				ctx->sample_num = sample_num;
189 			}
190 			byte_offset += datalen;
191 			sample_num++;
192 		}
193 
194 		//cancel event
195 		return GF_TRUE;
196 
197 	case GF_FEVT_STOP:
198 		ctx->is_playing = GF_FALSE;
199 		//don't cancel event
200 		return GF_FALSE;
201 
202 	case GF_FEVT_SET_SPEED:
203 		//cancel event
204 		return GF_TRUE;
205 	default:
206 		break;
207 	}
208 	//by default don't cancel event - to rework once we have downloading in place
209 	return GF_FALSE;
210 }
211 
212 
213 typedef struct
214 {
215 	Bool from_is_start, from_is_end, to_is_start, to_is_end;
216 	u64 from_pos, to_pos;
217 	char *from_id, *to_id;
218 	GF_List *id_stack;
219 	GF_SAXParser *sax;
220 } XMLBreaker;
221 
222 
nhml_node_start(void * sax_cbck,const char * node_name,const char * name_space,const GF_XMLAttribute * attributes,u32 nb_attributes)223 static void nhml_node_start(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes)
224 {
225 	XMLBreaker *breaker = (XMLBreaker *)sax_cbck;
226 	char *node_id;
227 	u32 i;
228 	node_id = NULL;
229 	for (i=0; i<nb_attributes; i++) {
230 		GF_XMLAttribute *att = (GF_XMLAttribute *) &attributes[i];
231 		if (stricmp(att->name, "DEF") && stricmp(att->name, "id")) continue;
232 		node_id = gf_strdup(att->value);
233 		break;
234 	}
235 	if (!node_id) {
236 		node_id = gf_strdup("__nhml__none");
237 		gf_list_add(breaker->id_stack, node_id);
238 		return;
239 	}
240 	gf_list_add(breaker->id_stack, node_id);
241 
242 	if (breaker->from_is_start && breaker->from_id && !strcmp(breaker->from_id, node_id)) {
243 		breaker->from_pos = gf_xml_sax_get_node_start_pos(breaker->sax);
244 		breaker->from_is_start = GF_FALSE;
245 	}
246 	if (breaker->to_is_start && breaker->to_id && !strcmp(breaker->to_id, node_id)) {
247 		breaker->to_pos = gf_xml_sax_get_node_start_pos(breaker->sax);
248 		breaker->to_is_start = GF_FALSE;
249 	}
250 	if (!breaker->to_is_start && !breaker->from_is_start && !breaker->to_is_end && !breaker->from_is_end) {
251 		gf_xml_sax_suspend(breaker->sax, GF_TRUE);
252 	}
253 
254 }
255 
nhml_node_end(void * sax_cbck,const char * node_name,const char * name_space)256 static void nhml_node_end(void *sax_cbck, const char *node_name, const char *name_space)
257 {
258 	XMLBreaker *breaker = (XMLBreaker *)sax_cbck;
259 	char *node_id = (char *)gf_list_last(breaker->id_stack);
260 	gf_list_rem_last(breaker->id_stack);
261 	if (breaker->from_is_end && breaker->from_id && !strcmp(breaker->from_id, node_id)) {
262 		breaker->from_pos = gf_xml_sax_get_node_end_pos(breaker->sax);
263 		breaker->from_is_end = GF_FALSE;
264 	}
265 	if (breaker->to_is_end && breaker->to_id && !strcmp(breaker->to_id, node_id)) {
266 		breaker->to_pos = gf_xml_sax_get_node_end_pos(breaker->sax);
267 		breaker->to_is_end = GF_FALSE;
268 	}
269 	gf_free(node_id);
270 	if (!breaker->to_is_start && !breaker->from_is_start && !breaker->to_is_end && !breaker->from_is_end) {
271 		gf_xml_sax_suspend(breaker->sax, GF_TRUE);
272 	}
273 }
274 
275 
nhml_sample_from_xml(GF_NHMLDmxCtx * ctx,char * xml_file,char * xmlFrom,char * xmlTo)276 static GF_Err nhml_sample_from_xml(GF_NHMLDmxCtx *ctx, char *xml_file, char *xmlFrom, char *xmlTo)
277 {
278 	GF_Err e = GF_OK;
279 	u32 read;
280 	XMLBreaker breaker;
281 	char *tmp;
282 	FILE *xml;
283 	u8 szBOM[3];
284 	if (!xml_file || !xmlFrom || !xmlTo) return GF_BAD_PARAM;
285 
286 	memset(&breaker, 0, sizeof(XMLBreaker));
287 
288 	xml = gf_fopen(xml_file, "rb");
289 	if (!xml) {
290 		GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] import failure: file %s not found", xml_file ));
291 		goto exit;
292 	}
293 	//we cannot use files with BOM since the XML position we get from the parser are offsets in the UTF-8 version of the XML.
294 	//TODO: to support files with BOM we would need to serialize on the fly the callback from the sax parser
295 	read = (u32) gf_fread(szBOM, 3, xml);
296 	if (read==3) {
297 		gf_fseek(xml, 0, SEEK_SET);
298 		if ((szBOM[0]==0xFF) || (szBOM[0]==0xFE) || (szBOM[0]==0xEF)) {
299 			GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] import failure: XML file %s uses unsupported BOM, please convert to plain UTF-8 or ANSI first", xml_file));
300 			goto exit;
301 		}
302 	}
303 
304 
305 	memset(&breaker, 0, sizeof(XMLBreaker));
306 	breaker.id_stack = gf_list_new();
307 
308 	if (strstr(xmlFrom, ".start")) breaker.from_is_start = GF_TRUE;
309 	else breaker.from_is_end = GF_TRUE;
310 	tmp = strchr(xmlFrom, '.');
311 	*tmp = 0;
312 	if (stricmp(xmlFrom, "doc")) breaker.from_id = gf_strdup(xmlFrom);
313 	/*doc start pos is 0, no need to look for it*/
314 	else if (breaker.from_is_start) breaker.from_is_start = GF_FALSE;
315 	*tmp = '.';
316 
317 	if (strstr(xmlTo, ".start")) breaker.to_is_start = GF_TRUE;
318 	else breaker.to_is_end = GF_TRUE;
319 	tmp = strchr(xmlTo, '.');
320 	*tmp = 0;
321 	if (stricmp(xmlTo, "doc")) breaker.to_id = gf_strdup(xmlTo);
322 	/*doc end pos is file size, no need to look for it*/
323 	else if (breaker.to_is_end) breaker.to_is_end = GF_FALSE;
324 	*tmp = '.';
325 
326 	breaker.sax = gf_xml_sax_new(nhml_node_start, nhml_node_end, NULL, &breaker);
327 	e = gf_xml_sax_parse_file(breaker.sax, xml_file, NULL);
328 	gf_xml_sax_del(breaker.sax);
329 	if (e<0) goto exit;
330 
331 	if (!breaker.to_id) {
332 		breaker.to_pos = gf_fsize(xml);
333 	}
334 	if (breaker.to_pos < breaker.from_pos) {
335 		GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] import failure: xmlFrom %s is located after xmlTo %s", xmlFrom, xmlTo));
336 		goto exit;
337 	}
338 
339 	assert(breaker.to_pos > breaker.from_pos);
340 
341 
342 	ctx->samp_buffer_size = (u32) (breaker.to_pos - breaker.from_pos);
343 	if (ctx->samp_buffer_alloc < ctx->samp_buffer_size) {
344 		ctx->samp_buffer_alloc = ctx->samp_buffer_size;
345 		ctx->samp_buffer = (char*)gf_realloc(ctx->samp_buffer, sizeof(char)*ctx->samp_buffer_alloc);
346 	}
347 	gf_fseek(xml, breaker.from_pos, SEEK_SET);
348 	if (0 == gf_fread(ctx->samp_buffer, ctx->samp_buffer_size, xml)) {
349 		GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[NHMLDmx] Failed to read samp->dataLength\n"));
350 	}
351 	e = GF_OK;
352 
353 exit:
354 	if (xml) gf_fclose(xml);
355 	while (gf_list_count(breaker.id_stack)) {
356 		char *id = (char *)gf_list_last(breaker.id_stack);
357 		gf_list_rem_last(breaker.id_stack);
358 		gf_free(id);
359 	}
360 	gf_list_del(breaker.id_stack);
361 	if (breaker.from_id) gf_free(breaker.from_id);
362 	if (breaker.to_id) gf_free(breaker.to_id);
363 	return e;
364 }
365 
366 
367 #ifndef GPAC_DISABLE_ZLIB
368 
369 /*since 0.2.2, we use zlib for xmt/x3d reading to handle gz files*/
370 #include <zlib.h>
371 
372 #define ZLIB_COMPRESS_SAFE	4
373 
compress_sample_data(GF_NHMLDmxCtx * ctx,u32 compress_type,char ** dict,u32 offset)374 static GF_Err compress_sample_data(GF_NHMLDmxCtx *ctx, u32 compress_type, char **dict, u32 offset)
375 {
376 	z_stream stream;
377 	int err;
378 	u32 size;
379 
380 	if (!ctx) return GF_OK;
381 
382 	size = ctx->samp_buffer_size*ZLIB_COMPRESS_SAFE;
383 	if (ctx->zlib_buffer_alloc < size) {
384 		ctx->zlib_buffer_alloc = size;
385 		ctx->zlib_buffer = gf_realloc(ctx->zlib_buffer, sizeof(char)*size);
386 	}
387 
388 	stream.next_in = (Bytef*) ctx->samp_buffer + offset;
389 	stream.avail_in = (uInt)ctx->samp_buffer_size - offset;
390 	stream.next_out = ( Bytef*)ctx->zlib_buffer;
391 	stream.avail_out = (uInt)size;
392 	stream.zalloc = (alloc_func)NULL;
393 	stream.zfree = (free_func)NULL;
394 	stream.opaque = (voidpf)NULL;
395 
396 	if (compress_type==1) {
397 		err = deflateInit(&stream, 9);
398 	} else {
399 		err = deflateInit2(&stream, 9, Z_DEFLATED, 16+MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
400 	}
401 	if (err != Z_OK) {
402 		return GF_IO_ERR;
403 	}
404 	if (dict && *dict) {
405 		err = deflateSetDictionary(&stream, (Bytef *)*dict, (u32) strlen(*dict));
406 		if (err != Z_OK) {
407 			GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Error assigning dictionary\n"));
408 			deflateEnd(&stream);
409 			return GF_IO_ERR;
410 		}
411 	}
412 	err = deflate(&stream, Z_FINISH);
413 	if (err != Z_STREAM_END) {
414 		deflateEnd(&stream);
415 		return GF_IO_ERR;
416 	}
417 	if (ctx->samp_buffer_size - offset < stream.total_out) {
418 		GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[NHMLDmx] compressed data (%d) bigger than input data (%d)\n", (u32) stream.total_out, (u32) ctx->samp_buffer_size - offset));
419 	}
420 	if (dict) {
421 		if (*dict) gf_free(*dict);
422 		*dict = (char*)gf_malloc(sizeof(char) * ctx->samp_buffer_size);
423 		memcpy(*dict, ctx->samp_buffer, ctx->samp_buffer_size);
424 	}
425 	if (ctx->samp_buffer_alloc < stream.total_out) {
426 		ctx->samp_buffer_alloc = (u32) (stream.total_out*2);
427 		ctx->samp_buffer = (char*)gf_realloc(ctx->samp_buffer, ctx->samp_buffer_alloc * sizeof(char));
428 	}
429 
430 	memcpy(ctx->samp_buffer + offset, ctx->zlib_buffer, sizeof(char)*stream.total_out);
431 	ctx->samp_buffer_size = (u32) (offset + stream.total_out);
432 
433 	deflateEnd(&stream);
434 	return GF_OK;
435 }
436 
437 #endif /*GPAC_DISABLE_ZLIB*/
438 
439 #define NHML_SCAN_INT(_fmt, _value)	\
440 	{\
441 	if (strstr(att->value, "0x")) { u32 __i; sscanf(att->value+2, "%x", &__i); _value = __i; }\
442 	else if (strstr(att->value, "0X")) { u32 __i; sscanf(att->value+2, "%X", &__i); _value = __i; }\
443 	else sscanf(att->value, _fmt, &_value); \
444 	}\
445 
446 
nhmldmx_init_parsing(GF_Filter * filter,GF_NHMLDmxCtx * ctx)447 static GF_Err nhmldmx_init_parsing(GF_Filter *filter, GF_NHMLDmxCtx *ctx)
448 {
449 	GF_Err e;
450 	Bool inRootOD;
451 	u32 i, tkID, mtype, streamType, codecid, specInfoSize, par_den, par_num;
452 	GF_XMLAttribute *att;
453 	u32 width, height, codec_tag, sample_rate, nb_channels, version, revision, vendor_code, temporal_quality, spatial_quality, h_res, v_res, bit_depth, bits_per_sample;
454 
455 	u32 dims_profile, dims_level, dims_pathComponents, dims_fullRequestHost, dims_streamType, dims_containsRedundant;
456 	char *textEncoding, *contentEncoding, *dims_content_script_types, *mime_type, *xml_schema_loc, *xmlns;
457 	FILE *nhml;
458 	const GF_PropertyValue *p;
459 	char *auxiliary_mime_types = NULL;
460 	char *ext, szName[1000], szInfo[GF_MAX_PATH], szXmlFrom[1000], szXmlHeaderEnd[1000];
461 	u8 *specInfo;
462 	char compressor_name[100];
463 	GF_XMLNode *node;
464 	FILE *finfo;
465 	u64 media_size, last_dts;
466 	char *szRootName, *szSampleName, *szImpName;
467 
468 	szRootName = ctx->is_dims ? "DIMSStream" : "NHNTStream";
469 	szSampleName = ctx->is_dims ? "DIMSUnit" : "NHNTSample";
470 	szImpName = ctx->is_dims ? "DIMS" : "NHML";
471 
472 	p = gf_filter_pid_get_property(ctx->ipid, GF_PROP_PID_FILEPATH);
473 	if (!p) {
474 		gf_filter_pid_drop_packet(ctx->ipid);
475 		return GF_NOT_SUPPORTED;
476 	}
477 	ctx->src_url = p->value.string;
478 	nhml = gf_fopen(ctx->src_url, "rt");
479 	if (!nhml) {
480 		GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Cannot find %s file %s", szImpName, ctx->src_url));
481 		return GF_URL_ERROR;
482 	}
483 
484 	szName[0] = 0;
485 	if (!strncmp(ctx->src_url, "gfio://", 7)) {
486 		char *base = gf_file_basename( gf_fileio_translate_url(ctx->src_url) );
487 		if (base) strcpy(szName, base);
488 	} else {
489 		strcpy(szName, ctx->src_url);
490 	}
491 	ext = gf_file_ext_start(szName);
492 	if (ext) ext[0] = 0;
493 	strcpy(ctx->szMedia, szName);
494 	strcpy(szInfo, szName);
495 	strcat(ctx->szMedia, ".media");
496 	strcat(szInfo, ".info");
497 
498 	ctx->parser = gf_xml_dom_new();
499 	e = gf_xml_dom_parse(ctx->parser, p->value.string, NULL, NULL);
500 	if (e) {
501 		gf_fclose(nhml);
502 		GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Error parsing %s file: Line %d - %s", szImpName, gf_xml_dom_get_line(ctx->parser), gf_xml_dom_get_error(ctx->parser) ));
503 		return GF_NON_COMPLIANT_BITSTREAM;
504 	}
505 	gf_fclose(nhml);
506 
507 	ctx->root = gf_xml_dom_get_root(ctx->parser);
508 	if (!ctx->root) {
509 		GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Error parsing %s file - no root node found", szImpName ));
510 		return GF_NON_COMPLIANT_BITSTREAM;
511 	}
512 
513 	ctx->dts_inc = 0;
514 	inRootOD = GF_FALSE;
515 	ctx->compress_type = 0;
516 	specInfo = NULL;
517 
518 #ifndef GPAC_DISABLE_ZLIB
519 	ctx->use_dict = GF_FALSE;
520 #endif
521 
522 	if (stricmp(ctx->root->name, szRootName)) {
523 		GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Error parsing %s file - \"%s\" root expected, got \"%s\"", szImpName, szRootName, ctx->root->name));
524 		return GF_NON_COMPLIANT_BITSTREAM;
525 	}
526 
527 	tkID = mtype = streamType = codecid = par_den = par_num = 0;
528 	ctx->timescale = 1000;
529 	i=0;
530 	strcpy(szXmlHeaderEnd, "");
531 	ctx->header_end = 0;
532 
533 	width = height = codec_tag = sample_rate = nb_channels = version = revision = vendor_code = temporal_quality = spatial_quality = h_res = v_res = bit_depth = bits_per_sample = 0;
534 
535 	dims_pathComponents = dims_fullRequestHost = 0;
536 	textEncoding = contentEncoding = dims_content_script_types = mime_type = xml_schema_loc = xmlns = NULL;
537 	dims_profile = dims_level = 255;
538 	dims_streamType = GF_TRUE;
539 	dims_containsRedundant = 1;
540 
541 	while ((att = (GF_XMLAttribute *)gf_list_enum(ctx->root->attributes, &i))) {
542 		if (!stricmp(att->name, "streamType")) {
543 			NHML_SCAN_INT("%u", streamType)
544 		} else if (!stricmp(att->name, "mediaType") && (strlen(att->value)==4)) {
545 			mtype = GF_4CC(att->value[0], att->value[1], att->value[2], att->value[3]);
546 		} else if (!stricmp(att->name, "mediaSubType") && (strlen(att->value)==4)) {
547 			codec_tag = GF_4CC(att->value[0], att->value[1], att->value[2], att->value[3]);
548 		} else if (!stricmp(att->name, "objectTypeIndication")) {
549 			NHML_SCAN_INT("%u", codecid)
550 		} else if (!stricmp(att->name, "codecID") && (strlen(att->value)==4)) {
551 			codecid = GF_4CC(att->value[0], att->value[1], att->value[2], att->value[3]);
552 		} else if (!stricmp(att->name, "timeScale")) {
553 			NHML_SCAN_INT("%u", ctx->timescale)
554 		} else if (!stricmp(att->name, "width")) {
555 			NHML_SCAN_INT("%u", width)
556 		} else if (!stricmp(att->name, "height")) {
557 			NHML_SCAN_INT("%u", height)
558 		} else if (!stricmp(att->name, "parNum")) {
559 			NHML_SCAN_INT("%u", par_num)
560 		} else if (!stricmp(att->name, "parDen")) {
561 			NHML_SCAN_INT("%u", par_den)
562 		} else if (!stricmp(att->name, "sampleRate")) {
563 			NHML_SCAN_INT("%u", sample_rate)
564 		} else if (!stricmp(att->name, "numChannels")) {
565 			NHML_SCAN_INT("%u", nb_channels)
566 		} else if (!stricmp(att->name, "baseMediaFile")) {
567 			char *url = gf_url_concatenate(ctx->src_url, att->value);
568 			strcpy(ctx->szMedia, url ? url : att->value);
569 			if (url) gf_free(url);
570 		} else if (!stricmp(att->name, "specificInfoFile")) {
571 			char *url = gf_url_concatenate(ctx->src_url, att->value);
572 			strcpy(szInfo, url ? url : att->value);
573 			if (url) gf_free(url);
574 		} else if (!stricmp(att->name, "headerEnd")) {
575 			NHML_SCAN_INT("%u", ctx->header_end)
576 		} else if (!stricmp(att->name, "trackID")) {
577 			NHML_SCAN_INT("%u", tkID)
578 		} else if (!stricmp(att->name, "inRootOD")) {
579 			inRootOD = (!stricmp(att->value, "yes") );
580 		} else if (!stricmp(att->name, "DTS_increment")) {
581 			NHML_SCAN_INT("%u", ctx->dts_inc)
582 		} else if (!stricmp(att->name, "gzipSamples")) {
583 			if (!stricmp(att->value, "yes") || !stricmp(att->value, "gzip"))
584 				ctx->compress_type = 2;
585 			else if (!stricmp(att->value, "deflate"))
586 				ctx->compress_type = 1;
587 		} else if (!stricmp(att->name, "auxiliaryMimeTypes")) {
588 			auxiliary_mime_types = gf_strdup(att->name);
589 		}
590 #ifndef GPAC_DISABLE_ZLIB
591 		else if (!stricmp(att->name, "gzipDictionary")) {
592 			u32 d_size;
593 			if (stricmp(att->value, "self")) {
594 				char *url = gf_url_concatenate(ctx->src_url, att->value);
595 
596 				e = gf_file_load_data(url ? url : att->value, (u8 **) &ctx->dictionary, &d_size);
597 
598 				if (url) gf_free(url);
599 				if (e) {
600 					GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Cannot open dictionary file %s: %s", att->value, gf_error_to_string(e) ));
601 					continue;
602 				}
603 			}
604 			ctx->use_dict = GF_TRUE;
605 		}
606 #endif
607 		/*unknown desc related*/
608 		else if (!stricmp(att->name, "compressorName")) {
609 			strncpy(compressor_name, att->value, 99);
610 			compressor_name[99]=0;
611 		} else if (!stricmp(att->name, "codecVersion")) {
612 			NHML_SCAN_INT("%u", version)
613 		} else if (!stricmp(att->name, "codecRevision")) {
614 			NHML_SCAN_INT("%u", revision)
615 		} else if (!stricmp(att->name, "codecVendor") && (strlen(att->value)==4)) {
616 			vendor_code = GF_4CC(att->value[0], att->value[1], att->value[2], att->value[3]);
617 		} else if (!stricmp(att->name, "temporalQuality")) {
618 			NHML_SCAN_INT("%u", temporal_quality)
619 		} else if (!stricmp(att->name, "spatialQuality")) {
620 			NHML_SCAN_INT("%u", spatial_quality)
621 		} else if (!stricmp(att->name, "horizontalResolution")) {
622 			NHML_SCAN_INT("%u", h_res)
623 		} else if (!stricmp(att->name, "verticalResolution")) {
624 			NHML_SCAN_INT("%u", v_res)
625 		} else if (!stricmp(att->name, "bitDepth")) {
626 			NHML_SCAN_INT("%u", bit_depth)
627 		} else if (!stricmp(att->name, "bitsPerSample")) {
628 			NHML_SCAN_INT("%u", bits_per_sample)
629 		}
630 		/*DIMS stuff*/
631 		else if (!stricmp(att->name, "profile")) {
632 			NHML_SCAN_INT("%u", dims_profile)
633 		} else if (!stricmp(att->name, "level")) {
634 			NHML_SCAN_INT("%u", dims_level)
635 		} else if (!stricmp(att->name, "pathComponents")) {
636 			NHML_SCAN_INT("%u", dims_pathComponents)
637 		} else if (!stricmp(att->name, "useFullRequestHost") && !stricmp(att->value, "yes")) {
638 			dims_fullRequestHost = GF_TRUE;
639 		} else if (!stricmp(att->name, "stream_type") && !stricmp(att->value, "secondary")) {
640 			dims_streamType = GF_FALSE;
641 		} else if (!stricmp(att->name, "contains_redundant")) {
642 			if (!stricmp(att->value, "main")) {
643 				dims_containsRedundant = 1;
644 			} else if (!stricmp(att->value, "redundant")) {
645 				dims_containsRedundant = 2;
646 			} else if (!stricmp(att->value, "main+redundant")) {
647 				dims_containsRedundant = 3;
648 			}
649 		} else if (!stricmp(att->name, "text_encoding") || !stricmp(att->name, "encoding")) {
650 			textEncoding = att->value;
651 		} else if (!stricmp(att->name, "content_encoding")) {
652 			if (!strcmp(att->value, "deflate")) {
653 				contentEncoding = att->value;
654 				ctx->compress_type = 1;
655 			}
656 			else if (!strcmp(att->value, "gzip")) {
657 				contentEncoding = att->value;
658 				ctx->compress_type = 2;
659 			}
660 		} else if (!stricmp(att->name, "content_script_types")) {
661 			dims_content_script_types = att->value;
662 		} else if (!stricmp(att->name, "mime_type")) {
663 			mime_type = att->value;
664 		} else if (!stricmp(att->name, "media_namespace")) {
665 			xmlns = att->value;
666 		} else if (!stricmp(att->name, "media_schema_location")) {
667 			xml_schema_loc = att->value;
668 		} else if (!stricmp(att->name, "xml_namespace")) {
669 			xmlns = att->value;
670 		} else if (!stricmp(att->name, "xml_schema_location")) {
671 			xml_schema_loc = att->value;
672 		} else if (!stricmp(att->name, "xmlHeaderEnd")) {
673 			strcpy(szXmlHeaderEnd, att->value);
674 		}
675 	}
676 	if (sample_rate && !ctx->timescale) {
677 		ctx->timescale = sample_rate;
678 	}
679 	if (!bits_per_sample) {
680 		bits_per_sample = 16;
681 	}
682 
683 	if (ctx->is_dims || (codec_tag==GF_ISOM_SUBTYPE_3GP_DIMS)) {
684 		mtype = GF_ISOM_MEDIA_DIMS;
685 		codec_tag=GF_ISOM_SUBTYPE_3GP_DIMS;
686 		codecid = GF_CODECID_DIMS;
687 		streamType = GF_STREAM_SCENE;
688 	}
689 	if (gf_file_exists_ex(ctx->szMedia, ctx->src_url))
690 		ctx->mdia = gf_fopen_ex(ctx->szMedia, ctx->src_url, "rb");
691 
692 	specInfoSize = 0;
693 	if (!streamType && !mtype && !codec_tag) {
694 		GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] parsing %s file - StreamType or MediaType not specified", szImpName));
695 		return GF_NON_COMPLIANT_BITSTREAM;
696 	}
697 
698 	finfo = NULL;
699 	if (gf_file_exists_ex(szInfo, ctx->src_url))
700 		finfo = gf_fopen_ex(szInfo, ctx->src_url, "rb");
701 
702 	if (finfo) {
703 		e = gf_file_load_data_filep(finfo, (u8 **)&specInfo, &specInfoSize);
704 		gf_fclose(finfo);
705 		if (e) return e;
706 	} else if (ctx->header_end) {
707 		/* for text based streams, the decoder specific info can be at the beginning of the file */
708 		specInfoSize = ctx->header_end;
709 		specInfo = (char*)gf_malloc(sizeof(char) * (specInfoSize+1));
710 		specInfoSize = (u32) gf_fread(specInfo, specInfoSize, ctx->mdia);
711 		specInfo[specInfoSize] = 0;
712 		ctx->header_end = specInfoSize;
713 	} else if (strlen(szXmlHeaderEnd)) {
714 		/* for XML based streams, the decoder specific info can be up to some element in the file */
715 		strcpy(szXmlFrom, "doc.start");
716 		ctx->samp_buffer_size = 0;
717 		e = nhml_sample_from_xml(ctx, ctx->szMedia, szXmlFrom, szXmlHeaderEnd);
718 		if (e) {
719 			GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] failed to load XML header: %s", gf_error_to_string(e) ));
720 			return e;
721 		}
722 
723 		specInfo = (char*)gf_malloc(sizeof(char) * (ctx->samp_buffer_size +1));
724 		memcpy(specInfo, ctx->samp_buffer, ctx->samp_buffer_size);
725 		specInfoSize = ctx->samp_buffer_size;
726 		specInfo[specInfoSize] = 0;
727 	}
728 
729 	i=0;
730 	while ((node = (GF_XMLNode *) gf_list_enum(ctx->root->content, &i))) {
731 		if (node->type) continue;
732 		if (stricmp(node->name, "DecoderSpecificInfo") ) continue;
733 
734 		e = gf_xml_parse_bit_sequence(node, ctx->src_url, &specInfo, &specInfoSize);
735 		if (e) {
736 			if (specInfo) gf_free(specInfo);
737 			return e;
738 		}
739 		break;
740 	}
741 
742 	ctx->opid = gf_filter_pid_new(filter);
743 	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(streamType) );
744 	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(codecid) );
745 	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(ctx->timescale) );
746 	if (ctx->reframe)
747 	   gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, &PROP_UINT(GF_TRUE) );
748 
749 #ifndef GPAC_DISABLE_AV_PARSERS
750 	if (!width && !height && specInfo && (codecid==GF_CODECID_MPEG4_PART2)) {
751 		GF_M4VDecSpecInfo dsi;
752 		e = gf_m4v_get_config(specInfo, specInfoSize, &dsi);
753 		if (!e) {
754 			width = dsi.width;
755 			height = dsi.height;
756 			par_num = dsi.par_num;
757 			par_den = dsi.par_den;
758 		}
759 	}
760 #endif
761 
762 	if (tkID) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_ESID, &PROP_UINT(tkID) );
763 	if (width) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_WIDTH, &PROP_UINT(width) );
764 	if (height) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_HEIGHT, &PROP_UINT(height) );
765 
766 	if (par_den) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAR, &PROP_FRAC_INT(par_num, par_den) );
767 	switch (bits_per_sample) {
768 	case 8:
769 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(GF_AUDIO_FMT_U8) );
770 		break;
771 	case 16:
772 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(GF_AUDIO_FMT_S16) );
773 		break;
774 	case 24:
775 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(GF_AUDIO_FMT_S24) );
776 		break;
777 	case 32:
778 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(GF_AUDIO_FMT_S32) );
779 		break;
780 	default:
781 		GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[NHMLDmx] Unsupported audio bit depth %d\n", bits_per_sample));
782 		break;
783 	}
784 
785 	if (sample_rate) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_SAMPLE_RATE, &PROP_UINT(sample_rate) );
786 	if (nb_channels) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_NUM_CHANNELS, &PROP_UINT(nb_channels) );
787 	if (bit_depth) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_BIT_DEPTH_Y, &PROP_UINT(bit_depth) );
788 
789 	if (ctx->is_dims) {
790 		if (dims_profile) gf_filter_pid_set_property_str(ctx->opid, "dims:profile", &PROP_UINT(dims_profile) );
791 		if (dims_level) gf_filter_pid_set_property_str(ctx->opid, "dims:level", &PROP_UINT(dims_level) );
792 		if (dims_pathComponents) gf_filter_pid_set_property_str(ctx->opid, "dims:pathComponents", &PROP_UINT(dims_pathComponents) );
793 		if (dims_fullRequestHost) gf_filter_pid_set_property_str(ctx->opid, "dims:fullRequestHost", &PROP_UINT(dims_fullRequestHost) );
794 		if (dims_streamType) gf_filter_pid_set_property_str(ctx->opid, "dims:streamType", &PROP_BOOL(dims_streamType) );
795 		if (dims_containsRedundant) gf_filter_pid_set_property_str(ctx->opid, "dims:redundant", &PROP_UINT(dims_containsRedundant) );
796 		if (textEncoding) gf_filter_pid_set_property_str(ctx->opid, "meta:encoding", &PROP_STRING(textEncoding) );
797 		if (contentEncoding) gf_filter_pid_set_property_str(ctx->opid, "meta:content_encoding", &PROP_STRING(contentEncoding) );
798 		if (dims_content_script_types) gf_filter_pid_set_property_str(ctx->opid, "dims:scriptTypes", &PROP_STRING(dims_content_script_types) );
799 		if (mime_type) gf_filter_pid_set_property_str(ctx->opid, "meta:mime", &PROP_STRING(mime_type) );
800 		if (xml_schema_loc) gf_filter_pid_set_property_str(ctx->opid, "meta:schemaloc", &PROP_STRING(xml_schema_loc) );
801 
802 	} else if (mtype == GF_ISOM_MEDIA_MPEG_SUBT || mtype == GF_ISOM_MEDIA_SUBT || mtype == GF_ISOM_MEDIA_TEXT) {
803 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(mtype) );
804 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(codec_tag) );
805 
806 		if (codec_tag == GF_ISOM_SUBTYPE_STPP) {
807 			if (xmlns) gf_filter_pid_set_property_str(ctx->opid, "meta:xmlns", &PROP_STRING(xmlns) );
808 			if (xml_schema_loc) gf_filter_pid_set_property_str(ctx->opid, "meta:schemaloc", &PROP_STRING(xml_schema_loc) );
809 			if (auxiliary_mime_types) gf_filter_pid_set_property_str(ctx->opid, "meta:aux_mimes", &PROP_STRING(auxiliary_mime_types) );
810 
811 		} else if (codec_tag == GF_ISOM_SUBTYPE_SBTT) {
812 			if (mime_type) gf_filter_pid_set_property_str(ctx->opid, "meta:mime", &PROP_STRING(mime_type) );
813 			if (textEncoding) gf_filter_pid_set_property_str(ctx->opid, "meta:encoding", &PROP_STRING(textEncoding) );
814 		} else if (codec_tag == GF_ISOM_SUBTYPE_STXT) {
815 			if (mime_type) gf_filter_pid_set_property_str(ctx->opid, "meta:mime", &PROP_STRING(mime_type) );
816 			if (textEncoding) gf_filter_pid_set_property_str(ctx->opid, "meta:encoding", &PROP_STRING(textEncoding) );
817 			if (contentEncoding) gf_filter_pid_set_property_str(ctx->opid, "meta:content_encoding", &PROP_STRING(contentEncoding) );
818 		} else {
819 			e = GF_NOT_SUPPORTED;
820 		}
821 	} else if (mtype == GF_ISOM_MEDIA_META) {
822 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(mtype) );
823 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(codec_tag) );
824 
825 		if (codec_tag == GF_ISOM_SUBTYPE_METX) {
826 			if (xmlns) gf_filter_pid_set_property_str(ctx->opid, "meta:xmlns", &PROP_STRING(xmlns) );
827 			if (xml_schema_loc) gf_filter_pid_set_property_str(ctx->opid, "meta:schemaloc", &PROP_STRING(xml_schema_loc) );
828 			if (textEncoding) gf_filter_pid_set_property_str(ctx->opid, "meta:encoding", &PROP_STRING(textEncoding) );
829 		} else if (codec_tag == GF_ISOM_SUBTYPE_METT) {
830 			if (mime_type) gf_filter_pid_set_property_str(ctx->opid, "meta:mime", &PROP_STRING(mime_type) );
831 			if (textEncoding) gf_filter_pid_set_property_str(ctx->opid, "meta:encoding", &PROP_STRING(textEncoding) );
832 		} else {
833 			e = GF_NOT_SUPPORTED;
834 		}
835 	} else if (!streamType) {
836 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(mtype) );
837 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(codec_tag) );
838 
839 		if (version) gf_filter_pid_set_property_str(ctx->opid, "gene:version", &PROP_UINT(version) );
840 		if (revision) gf_filter_pid_set_property_str(ctx->opid, "gene:revision", &PROP_UINT(revision) );
841 		if (vendor_code) gf_filter_pid_set_property_str(ctx->opid, "gene:vendor", &PROP_UINT(vendor_code) );
842 		if (temporal_quality) gf_filter_pid_set_property_str(ctx->opid, "gene:temporal_quality", &PROP_UINT(temporal_quality) );
843 		if (spatial_quality) gf_filter_pid_set_property_str(ctx->opid, "gene:spatial_quality", &PROP_UINT(spatial_quality) );
844 		if (h_res) gf_filter_pid_set_property_str(ctx->opid, "gene:horizontal_res", &PROP_UINT(h_res) );
845 		if (v_res) gf_filter_pid_set_property_str(ctx->opid, "gene:vertical_res", &PROP_UINT(v_res) );
846 	}
847 
848 
849 	if (specInfo) {
850 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, &PROP_DATA_NO_COPY(specInfo, specInfoSize) );
851 		specInfo = NULL;
852 		specInfoSize = 0;
853 	}
854 
855 	if (inRootOD) gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_IN_IOD, &PROP_BOOL(GF_TRUE) );
856 
857 	ctx->media_done = 0;
858 	ctx->current_child_idx = 0;
859 	ctx->last_dts = GF_FILTER_NO_TS;
860 
861 	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FILEPATH, & PROP_STRING(ctx->szMedia));
862 
863 	if (ctx->mdia) {
864 		media_size = gf_fsize(ctx->mdia);
865 		gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DOWN_SIZE, & PROP_LONGUINT(media_size) );
866 	}
867 
868 	if (specInfo) gf_free(specInfo);
869 	if (auxiliary_mime_types) gf_free(auxiliary_mime_types);
870 
871 	//compute duration
872 	ctx->duration.den = ctx->timescale;
873 	ctx->duration.num = 0;
874 	last_dts = 0;
875 	i=0;
876 	ctx->has_sap = GF_FALSE;
877 	while ((node = (GF_XMLNode *) gf_list_enum(ctx->root->content, &i))) {
878 		u32 j=0;
879 		u64 dts=0;
880 		s32 cts_offset=0;
881 		u64 sample_duration = 0;
882 		if (node->type) continue;
883 		if (stricmp(node->name, szSampleName) ) continue;
884 
885 		while ( (att = (GF_XMLAttribute *)gf_list_enum(node->attributes, &j))) {
886 			if (!stricmp(att->name, "DTS") || !stricmp(att->name, "time")) {
887 				u32 h, m, s, ms;
888 				u64 dst_val;
889 				if (strchr(att->value, ':') && sscanf(att->value, "%u:%u:%u.%u", &h, &m, &s, &ms) == 4) {
890 					dts = (u64) ( (Double) ( ((h*3600.0 + m*60.0 + s)*1000 + ms) / 1000.0) * ctx->timescale );
891 				} else if (sscanf(att->value, ""LLU, &dst_val)==1) {
892 					dts = dst_val;
893 				}
894 			}
895 			else if (!stricmp(att->name, "CTSOffset")) cts_offset = atoi(att->value);
896 			else if (!stricmp(att->name, "duration") ) sscanf(att->value, ""LLU, &sample_duration);
897 			else if (!stricmp(att->name, "isRAP") ) ctx->has_sap = GF_TRUE;
898 		}
899 		last_dts = ctx->duration.num;
900 		if (dts) ctx->duration.num = (u32) (dts + cts_offset);
901 		if (sample_duration) {
902 			last_dts = 0;
903 			ctx->duration.num += (u32) sample_duration;
904 		} else if (ctx->dts_inc) {
905 			last_dts = 0;
906 			ctx->duration.num += ctx->dts_inc;
907 		}
908 	}
909 	if (last_dts) {
910 		ctx->duration.num += (u32) (ctx->duration.num - last_dts);
911 	}
912 	//assume image, one sec (default for old arch)
913 	if ((streamType==4) && !ctx->duration.num) {
914 		ctx->is_img = GF_TRUE;
915 		ctx->duration.num =ctx->duration.den;
916 	}
917 
918 	gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DURATION, & PROP_FRAC64(ctx->duration) );
919 
920 	return e;
921 }
922 
nhml_get_bs(GF_BitStream ** bs,char * data,u32 size,u32 mode)923 void nhml_get_bs(GF_BitStream **bs, char *data, u32 size, u32 mode)
924 {
925 	if (*bs) gf_bs_reassign_buffer(*bs, data, size);
926 	else  (*bs) = gf_bs_new(data, size, mode);
927 }
928 
nhmldmx_send_sample(GF_Filter * filter,GF_NHMLDmxCtx * ctx)929 static GF_Err nhmldmx_send_sample(GF_Filter *filter, GF_NHMLDmxCtx *ctx)
930 {
931 	GF_XMLNode *node, *childnode;
932 	u64 sample_duration = 0;
933 	char szMediaTemp[GF_MAX_PATH], szXmlFrom[1000], szXmlTo[1000];
934 	char *szSubSampleName = ctx->is_dims ? "DIMSSubUnit" : "NHNTSubSample";
935 
936 	while ((node = (GF_XMLNode *) gf_list_enum(ctx->root->content, &ctx->current_child_idx))) {
937 		u8 *data;
938 		GF_FilterPacket *pck;
939 		u32 j, dims_flags;
940 		GF_FilterSAPType sap_type;
941 		GF_XMLAttribute *att;
942 		u64 dts=0;
943 		GF_Err e=GF_OK;
944 		s32 cts_offset;
945 		u64 offset=0, byte_offset = GF_FILTER_NO_BO;
946 		u32 nb_subsamples = 0;
947 		Bool redundant_rap, append, has_subbs, first_subsample_is_first = GF_FALSE;
948 		u32 compress_type;
949 		char *base_data = NULL;
950 		if (node->type) continue;
951 		if (stricmp(node->name, ctx->is_dims ? "DIMSUnit" : "NHNTSample") ) continue;
952 
953 		strcpy(szMediaTemp, "");
954 		strcpy(szXmlFrom, "");
955 		strcpy(szXmlTo, "");
956 
957 		/*by default handle all samples as contiguous*/
958 		ctx->samp_buffer_size = 0;
959 		dims_flags = 0;
960 		append = GF_FALSE;
961 		compress_type = ctx->compress_type;
962 		sample_duration = 0;
963 		redundant_rap = 0;
964 		sap_type = ctx->has_sap ? GF_FILTER_SAP_NONE : GF_FILTER_SAP_1;
965 
966 		cts_offset = 0;
967 		if (ctx->last_dts != GF_FILTER_NO_TS)
968 			dts = ctx->last_dts;
969 		else
970 			dts = 0;
971 
972 		ctx->sample_num++;
973 
974 
975 		j=0;
976 		while ( (att = (GF_XMLAttribute *)gf_list_enum(node->attributes, &j))) {
977 			if (!stricmp(att->name, "DTS") || !stricmp(att->name, "time")) {
978 				u32 h, m, s, ms;
979 				u64 dst_val;
980 				if (strchr(att->value, ':') && sscanf(att->value, "%u:%u:%u.%u", &h, &m, &s, &ms) == 4) {
981 					dts = (u64) ( (Double) ( ((h*3600.0 + m*60.0 + s)*1000 + ms) / 1000.0) * ctx->timescale );
982 				} else if (sscanf(att->value, ""LLU, &dst_val)==1) {
983 					dts = dst_val;
984 				}
985 			}
986 			else if (!stricmp(att->name, "CTSOffset")) cts_offset = atoi(att->value);
987 			else if (!stricmp(att->name, "isRAP") ) {
988 				sap_type = (!stricmp(att->value, "yes")) ? GF_FILTER_SAP_1 : GF_FILTER_SAP_NONE;
989 			}
990 			else if (!stricmp(att->name, "isSyncShadow")) redundant_rap = !stricmp(att->value, "yes") ? 1 : 0;
991 			else if (!stricmp(att->name, "SAPType") ) sap_type = atoi(att->value);
992 			else if (!stricmp(att->name, "mediaOffset")) offset = (s64) atof(att->value) ;
993 			else if (!stricmp(att->name, "dataLength")) ctx->samp_buffer_size = atoi(att->value);
994 			else if (!stricmp(att->name, "mediaFile")) {
995 				if (!strncmp(att->value, "data:", 5)) {
996 					char *base = strstr(att->value, "base64,");
997 					if (!base) {
998 						GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[NHMLDmx] Data encoding scheme not recognized in sample %d - skipping\n", ctx->sample_num));
999 					} else {
1000 						base_data = att->value;
1001 					}
1002 				} else {
1003 					char *url = gf_url_concatenate(ctx->src_url, att->value);
1004 					strcpy(szMediaTemp, url ? url : att->value);
1005 					if (url) gf_free(url);
1006 				}
1007 			}
1008 			else if (!stricmp(att->name, "xmlFrom")) strcpy(szXmlFrom, att->value);
1009 			else if (!stricmp(att->name, "xmlTo")) strcpy(szXmlTo, att->value);
1010 			/*DIMS flags*/
1011 			else if (!stricmp(att->name, "is-Scene") && !stricmp(att->value, "yes"))
1012 				dims_flags |= GF_DIMS_UNIT_S;
1013 			else if (!stricmp(att->name, "is-RAP") && !stricmp(att->value, "yes")) {
1014 				dims_flags |= GF_DIMS_UNIT_M;
1015 				sap_type = GF_FILTER_SAP_1;
1016 			}
1017 			else if (!stricmp(att->name, "is-redundant") && !stricmp(att->value, "yes"))
1018 				dims_flags |= GF_DIMS_UNIT_I;
1019 			else if (!stricmp(att->name, "redundant-exit") && !stricmp(att->value, "yes"))
1020 				dims_flags |= GF_DIMS_UNIT_D;
1021 			else if (!stricmp(att->name, "priority") && !stricmp(att->value, "high"))
1022 				dims_flags |= GF_DIMS_UNIT_P;
1023 			else if (!stricmp(att->name, "compress") && !stricmp(att->value, "yes"))
1024 				dims_flags |= GF_DIMS_UNIT_C;
1025 			else if (!stricmp(att->name, "duration") )
1026 				sscanf(att->value, ""LLU, &sample_duration);
1027 		}
1028 		if (sap_type==GF_FILTER_SAP_1)
1029 			dims_flags |= GF_DIMS_UNIT_M;
1030 
1031 		if (ctx->is_dims)
1032 			compress_type = (dims_flags & GF_DIMS_UNIT_C) ? 2 : 0 ;
1033 
1034 		if (ctx->is_img) sample_duration = ctx->duration.den;
1035 
1036 		has_subbs = GF_FALSE;
1037 		j=0;
1038 		while ((childnode = (GF_XMLNode *) gf_list_enum(node->content, &j))) {
1039 			if (childnode->type) continue;
1040 			if (!stricmp(childnode->name, "BS")) {
1041 				has_subbs = GF_TRUE;
1042 				break;
1043 			}
1044 		}
1045 
1046 		if (strlen(szXmlFrom) && strlen(szXmlTo)) {
1047 			char *xml_file;
1048 			if (strlen(szMediaTemp)) xml_file = szMediaTemp;
1049 			else xml_file = ctx->szMedia;
1050 			ctx->samp_buffer_size = 0;
1051 			e = nhml_sample_from_xml(ctx, xml_file, szXmlFrom, szXmlTo);
1052 		} else if (ctx->is_dims && !strlen(szMediaTemp)) {
1053 
1054 			char *content = gf_xml_dom_serialize(node, GF_TRUE);
1055 
1056 			ctx->samp_buffer_size = 3 + (u32) strlen(content);
1057 			if (ctx->samp_buffer_alloc < ctx->samp_buffer_size) {
1058 				ctx->samp_buffer_alloc = ctx->samp_buffer_size;
1059 				ctx->samp_buffer = gf_realloc(ctx->samp_buffer, ctx->samp_buffer_size);
1060 			}
1061 			nhml_get_bs(&ctx->bs_w, ctx->samp_buffer, ctx->samp_buffer_size, GF_BITSTREAM_WRITE);
1062 			gf_bs_write_u16(ctx->bs_w, ctx->samp_buffer_size - 2);
1063 			gf_bs_write_u8(ctx->bs_w, (u8) dims_flags);
1064 			gf_bs_write_data(ctx->bs_w, content, (ctx->samp_buffer_size - 3));
1065 			gf_free(content);
1066 
1067 			/*same DIMS unit*/
1068 			if (ctx->last_dts == dts)
1069 				append = GF_TRUE;
1070 
1071 		} else if (has_subbs) {
1072 			gf_bs_reassign_buffer(ctx->bs_w, ctx->samp_buffer, ctx->samp_buffer_alloc);
1073 			gf_xml_parse_bit_sequence_bs(node, ctx->src_url, ctx->bs_w);
1074 			gf_bs_get_content(ctx->bs_w, &ctx->samp_buffer, &ctx->samp_buffer_size);
1075 			if (ctx->samp_buffer_size > ctx->samp_buffer_alloc)
1076 				ctx->samp_buffer_alloc = ctx->samp_buffer_size;
1077 
1078 		} else if (base_data) {
1079 			char *start = strchr(base_data, ',');
1080 			if (start) {
1081 				u32 len = (u32)strlen(start+1);
1082 				if (len > ctx->samp_buffer_alloc) {
1083 					ctx->samp_buffer_alloc = len;
1084 					ctx->samp_buffer = gf_realloc(ctx->samp_buffer, sizeof(char)*ctx->samp_buffer_alloc);
1085 				}
1086 				ctx->samp_buffer_size = gf_base64_decode(start, len, ctx->samp_buffer, ctx->samp_buffer_alloc);
1087 			}
1088 		} else {
1089 			Bool close = GF_FALSE;
1090 			FILE *f = ctx->mdia;
1091 
1092 			j = 0;
1093 			while ((childnode = (GF_XMLNode *)gf_list_enum(node->content, &j))) {
1094 				if (childnode->type) continue;
1095 				if (!stricmp(childnode->name, szSubSampleName)) {
1096 					nb_subsamples++;
1097 				}
1098 			}
1099 
1100 			if (strlen(szMediaTemp)) {
1101 				f = gf_fopen(szMediaTemp, "rb");
1102 				if (!f) {
1103 					GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] import failure in sample %d: file %s not found", ctx->sample_num, close ? szMediaTemp : ctx->szMedia));
1104 					return GF_NON_COMPLIANT_BITSTREAM;
1105 				}
1106 				close = GF_TRUE;
1107 				if (offset) gf_fseek(f, offset, SEEK_SET);
1108 				//when using dedicated source files per samples, we don't allow for data reference yet
1109 			} else {
1110 				if (!offset) offset = ctx->media_done;
1111 				byte_offset = offset;
1112 			}
1113 
1114 			if (f) {
1115 				if (!ctx->samp_buffer_size) {
1116 					u64 ssize = gf_fsize(f);
1117 					assert(ssize < 0x80000000);
1118 					ctx->samp_buffer_size = (u32) ssize;
1119 				}
1120 				gf_fseek(f, offset, SEEK_SET);
1121 
1122 				if (ctx->is_dims) {
1123 					u32 read;
1124 					if (ctx->samp_buffer_size + 3 > ctx->samp_buffer_alloc) {
1125 						ctx->samp_buffer_alloc = ctx->samp_buffer_size + 3;
1126 						ctx->samp_buffer = (char*)gf_realloc(ctx->samp_buffer, sizeof(char) * ctx->samp_buffer_alloc);
1127 					}
1128 					nhml_get_bs(&ctx->bs_w, ctx->samp_buffer, ctx->samp_buffer_alloc, GF_BITSTREAM_WRITE);
1129 					gf_bs_write_u16(ctx->bs_w, ctx->samp_buffer_size+1);
1130 					gf_bs_write_u8(ctx->bs_w, (u8) dims_flags);
1131 					read = (u32) gf_fread( ctx->samp_buffer + 3, ctx->samp_buffer_size, f);
1132 					if (ctx->samp_buffer_size != read) {
1133 						GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Failed to fully read sample %d: dataLength %d read %d\n", ctx->sample_num, ctx->samp_buffer_size, read));
1134 					}
1135 					ctx->samp_buffer_size += 3;
1136 
1137 					/*same DIMS unit*/
1138 					if (ctx->last_dts == dts)
1139 						append = GF_TRUE;
1140 				} else {
1141 					u32 read;
1142 					if (ctx->samp_buffer_alloc < ctx->samp_buffer_size) {
1143 						ctx->samp_buffer_alloc = ctx->samp_buffer_size;
1144 						ctx->samp_buffer = (char*)gf_realloc(ctx->samp_buffer, sizeof(char) * ctx->samp_buffer_alloc);
1145 					}
1146 					read = (u32) gf_fread(ctx->samp_buffer, ctx->samp_buffer_size, f);
1147 					if (ctx->samp_buffer_size != read) {
1148 						GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Failed to fully read sample %d: dataLength %d read %d\n", ctx->sample_num, ctx->samp_buffer_size, read));
1149 					}
1150 				}
1151 				if (close) gf_fclose(f);
1152 			} else if (!nb_subsamples) {
1153 				GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] No media file associated with sample %d!\n", ctx->sample_num));
1154 				e = GF_URL_ERROR;
1155 			}
1156 		}
1157 
1158 		if (e) return e;
1159 
1160 		//override DIMS flags
1161 		if (ctx->is_dims) {
1162 			if (strstr(ctx->samp_buffer + 3, "svg ")) dims_flags |= GF_DIMS_UNIT_S;
1163 			if (dims_flags & GF_DIMS_UNIT_S) dims_flags |= GF_DIMS_UNIT_P;
1164 			ctx->samp_buffer[2] = dims_flags;
1165 		}
1166 
1167 		if (compress_type) {
1168 #ifndef GPAC_DISABLE_ZLIB
1169 			e = compress_sample_data(ctx, compress_type, ctx->use_dict ? &ctx->dictionary : NULL, ctx->is_dims ? 3 : 0);
1170 			if (e) return e;
1171 
1172 			if (ctx->is_dims) {
1173 				nhml_get_bs(&ctx->bs_w, ctx->samp_buffer, ctx->samp_buffer_size, GF_BITSTREAM_WRITE);
1174 				gf_bs_write_u16(ctx->bs_w, ctx->samp_buffer_size-2);
1175 			}
1176 #else
1177 			GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Error: your version of GPAC was compiled with no libz support. Abort."));
1178 			return GF_NOT_SUPPORTED;
1179 #endif
1180 		}
1181 
1182 		if (ctx->is_dims && (ctx->samp_buffer_size > 0xFFFF)) {
1183 			GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] DIMS import failure: sample %d data is too long - maximum size allowed: 65532 bytes", ctx->sample_num));
1184 			return GF_NON_COMPLIANT_BITSTREAM;
1185 		}
1186 		if (ctx->samp_buffer_size) {
1187 			pck = gf_filter_pck_new_alloc(ctx->opid, ctx->samp_buffer_size, &data);
1188 			memcpy(data, ctx->samp_buffer, ctx->samp_buffer_size);
1189 			gf_filter_pck_set_framing(pck, append ? GF_FALSE : GF_TRUE, nb_subsamples ? GF_FALSE : GF_TRUE);
1190 			if (!append) {
1191 				gf_filter_pck_set_sap(pck, sap_type);
1192 				gf_filter_pck_set_dts(pck, dts);
1193 				gf_filter_pck_set_cts(pck, dts+cts_offset);
1194 				if (redundant_rap && !ctx->is_dims) gf_filter_pck_set_dependency_flags(pck, 0x1);
1195 
1196 				if (sample_duration || ctx->dts_inc)
1197 					gf_filter_pck_set_duration(pck, sample_duration ? (u32) sample_duration : ctx->dts_inc);
1198 
1199 				if (byte_offset != GF_FILTER_NO_BO)
1200 					gf_filter_pck_set_byte_offset(pck, byte_offset);
1201 
1202 				if (ctx->in_seek) {
1203 					if (dts+cts_offset >= ctx->start_range * ctx->timescale)
1204 						ctx->in_seek = GF_FALSE;
1205 					else
1206 						gf_filter_pck_set_seek_flag(pck, GF_TRUE);
1207 				}
1208 			}
1209 			gf_filter_pck_send(pck);
1210 		} else {
1211 			first_subsample_is_first = GF_TRUE;
1212 		}
1213 
1214 		if (nb_subsamples) {
1215 			if (ctx->samp_buffer_alloc<14*nb_subsamples) {
1216 				ctx->samp_buffer_alloc = 14*nb_subsamples;
1217 				ctx->samp_buffer = gf_realloc(ctx->samp_buffer, ctx->samp_buffer_alloc);
1218 			}
1219 			assert(ctx->samp_buffer);
1220 			nhml_get_bs(&ctx->bs_w, ctx->samp_buffer, ctx->samp_buffer_alloc, GF_BITSTREAM_WRITE);
1221 		}
1222 
1223 		j = 0;
1224 		while (!append && nb_subsamples && (childnode = (GF_XMLNode *)gf_list_enum(node->content, &j))) {
1225 			if (childnode->type) continue;
1226 			if (!stricmp(childnode->name, szSubSampleName)) {
1227 				u32 k = 0;
1228 				while ((att = (GF_XMLAttribute *)gf_list_enum(childnode->attributes, &k))) {
1229 					if (!stricmp(att->name, "mediaFile")) {
1230 						u64 subsMediaFileSize = 0;
1231 						FILE *f = NULL;
1232 						char *sub_file_url = gf_url_concatenate(ctx->src_url, att->value);
1233 						if (sub_file_url) {
1234 							f = gf_fopen(sub_file_url, "rb");
1235 							gf_free(sub_file_url);
1236 						}
1237 
1238 						if (!f) {
1239 							GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Error: mediaFile \"%s\" not found for subsample in sample %d. Abort.\n", att->value, ctx->sample_num));
1240 							return GF_URL_ERROR;
1241 						}
1242 						subsMediaFileSize = gf_fsize(f);
1243 						assert(subsMediaFileSize < 0x80000000);
1244 
1245 						//send continuation frame
1246 						pck = gf_filter_pck_new_alloc(ctx->opid, (u32) subsMediaFileSize, &data);
1247 						subsMediaFileSize = (u32) gf_fread(data, (u32) subsMediaFileSize, f);
1248 						gf_fclose(f);
1249 
1250 						nb_subsamples--;
1251 						if (first_subsample_is_first) {
1252 							gf_filter_pck_set_framing(pck, GF_TRUE, nb_subsamples ? GF_FALSE : GF_TRUE);
1253 
1254 							gf_filter_pck_set_sap(pck, sap_type);
1255 							gf_filter_pck_set_dts(pck, dts);
1256 							gf_filter_pck_set_cts(pck, dts+cts_offset);
1257 							if (redundant_rap && !ctx->is_dims) gf_filter_pck_set_dependency_flags(pck, 0x1);
1258 
1259 							if (sample_duration || ctx->dts_inc)
1260 								gf_filter_pck_set_duration(pck, sample_duration ? (u32) sample_duration : ctx->dts_inc);
1261 
1262 							if (ctx->in_seek) {
1263 								if (dts+cts_offset >= ctx->start_range * ctx->timescale)
1264 									ctx->in_seek = GF_FALSE;
1265 								else
1266 									gf_filter_pck_set_seek_flag(pck, GF_TRUE);
1267 							}
1268 
1269 							first_subsample_is_first = GF_FALSE;
1270 						} else {
1271 							gf_filter_pck_set_framing(pck, GF_FALSE, nb_subsamples ? GF_FALSE : GF_TRUE);
1272 						}
1273 
1274 
1275 						gf_bs_write_u32(ctx->bs_w, 0); //flags
1276 						gf_bs_write_u32(ctx->bs_w, (u32) subsMediaFileSize);
1277 						gf_bs_write_u32(ctx->bs_w, 0); //reserved
1278 						gf_bs_write_u8(ctx->bs_w, 0); //priority
1279 						gf_bs_write_u8(ctx->bs_w, 0); //discardable
1280 
1281 						if (!nb_subsamples) {
1282 							u32 subs_size = (u32) gf_bs_get_position(ctx->bs_w);
1283 							gf_filter_pck_set_property(pck, GF_PROP_PCK_SUBS, &PROP_DATA(ctx->samp_buffer, subs_size) );
1284 						}
1285 
1286 						gf_filter_pck_send(pck);
1287 					}
1288 				}
1289 			}
1290 		}
1291 
1292 		ctx->last_dts = dts;
1293 
1294 		if (sample_duration)
1295 			ctx->last_dts += sample_duration;
1296 		else
1297 			ctx->last_dts += ctx->dts_inc;
1298 		ctx->media_done += ctx->samp_buffer_size;
1299 
1300 		if (gf_filter_pid_would_block(ctx->opid))
1301 			return GF_OK;
1302 	}
1303 	ctx->parsing_state = 2;
1304 	return GF_OK;
1305 }
1306 
nhmldmx_process(GF_Filter * filter)1307 GF_Err nhmldmx_process(GF_Filter *filter)
1308 {
1309 	GF_NHMLDmxCtx *ctx = gf_filter_get_udta(filter);
1310 	GF_FilterPacket *pck;
1311 	GF_Err e;
1312 	Bool start, end;
1313 
1314 	pck = gf_filter_pid_get_packet(ctx->ipid);
1315 	if (pck) {
1316 		gf_filter_pck_get_framing(pck, &start, &end);
1317 		//for now we only work with complete files
1318 		assert(end);
1319 	}
1320 
1321 
1322 	//need init ?
1323 	switch (ctx->parsing_state) {
1324 	case 0:
1325 		e = nhmldmx_init_parsing(filter, ctx);
1326 		if (e) {
1327 			ctx->parsing_state = 3;
1328 			return e;
1329 		}
1330 		ctx->parsing_state = 1;
1331 		//fall-through
1332 	case 1:
1333 		if (!ctx->is_playing) return GF_OK;
1334 
1335 		e = nhmldmx_send_sample(filter, ctx);
1336 		if (e) return e;
1337 		break;
1338 	case 2:
1339 	default:
1340 		if (pck) gf_filter_pid_drop_packet(ctx->ipid);
1341 		if (ctx->opid) {
1342 			gf_filter_pid_set_eos(ctx->opid);
1343 			return GF_EOS;
1344 		}
1345 		break;
1346 	}
1347 	return GF_OK;
1348 }
1349 
nhmldmx_initialize(GF_Filter * filter)1350 GF_Err nhmldmx_initialize(GF_Filter *filter)
1351 {
1352 //	GF_NHMLDmxCtx *ctx = gf_filter_get_udta(filter);
1353 	return GF_OK;
1354 }
1355 
nhmldmx_finalize(GF_Filter * filter)1356 void nhmldmx_finalize(GF_Filter *filter)
1357 {
1358 	GF_NHMLDmxCtx *ctx = gf_filter_get_udta(filter);
1359 	if (ctx->mdia) gf_fclose(ctx->mdia);
1360 	if (ctx->parser)
1361 		gf_xml_dom_del(ctx->parser);
1362 
1363 #ifndef GPAC_DISABLE_ZLIB
1364 	if (ctx->dictionary) gf_free(ctx->dictionary);
1365 #endif
1366 	if (ctx->bs_r) gf_bs_del(ctx->bs_r);
1367 	if (ctx->bs_w) gf_bs_del(ctx->bs_w);
1368 	if (ctx->samp_buffer) gf_free(ctx->samp_buffer);
1369 	if (ctx->zlib_buffer) gf_free(ctx->zlib_buffer);
1370 }
1371 
1372 
1373 #define OFFS(_n)	#_n, offsetof(GF_NHMLDmxCtx, _n)
1374 static const GF_FilterArgs GF_NHMLDmxArgs[] =
1375 {
1376 	{ OFFS(reframe), "force reparsing of referenced content", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1377 	{ OFFS(index), "indexing window length", GF_PROP_DOUBLE, "1.0", NULL, 0},
1378 	{0}
1379 };
1380 
1381 
1382 static const GF_FilterCapability NHMLDmxCaps[] =
1383 {
1384 	CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
1385 	CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "nhml|dims|dml"),
1386 	CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "application/x-nhml|application/dims"),
1387 	CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
1388 	CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
1389 	CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
1390 };
1391 
1392 GF_FilterRegister NHMLDmxRegister = {
1393 	.name = "nhmlr",
1394 	GF_FS_SET_DESCRIPTION("NHML parser")
1395 	GF_FS_SET_HELP("This filter reads NHML files/data to produce a media PID and frames.\n"
1396 	"NHML documentation is available at https://wiki.gpac.io/NHML-Format\n")
1397 	.private_size = sizeof(GF_NHMLDmxCtx),
1398 	.args = GF_NHMLDmxArgs,
1399 	.initialize = nhmldmx_initialize,
1400 	.finalize = nhmldmx_finalize,
1401 	SETCAPS(NHMLDmxCaps),
1402 	.configure_pid = nhmldmx_configure_pid,
1403 	.process = nhmldmx_process,
1404 	.process_event = nhmldmx_process_event
1405 };
1406 
nhmldmx_register(GF_FilterSession * session)1407 const GF_FilterRegister *nhmldmx_register(GF_FilterSession *session)
1408 {
1409 	return &NHMLDmxRegister;
1410 }
1411 
1412