GF_Err split_isomedia_file(GF_ISOFile *mp4, Double split_dur, u64 split_size_kb, char *inName, Double InterleavingTime, Double chunk_start_time, u32 adjust_split_end, char *outName, Bool force_rap_split, const char *split_range_str, u32 fs_dump_flags)
{
	Bool chunk_extraction, rap_split, split_until_end;
	GF_Err e;
	char *ext, szName[GF_MAX_PATH], szFile[GF_MAX_PATH+100], szArgs[100];
	Double chunk_start = (Double) chunk_start_time;
	char *filter_args = NULL;
	GF_FilterSession *fs;
	GF_Filter *src, *reframe, *dst;
	SplitInfo sinfo;

	sinfo.progress = -1;
	sinfo.file_idx = 1;

	chunk_extraction = (chunk_start>=0) ? GF_TRUE : GF_FALSE;
	if (split_range_str)
		chunk_extraction = GF_TRUE;
	split_until_end = GF_FALSE;
	rap_split = GF_FALSE;
	if (split_size_kb == (u64)-1) rap_split = GF_TRUE;
	if (split_dur == -1) rap_split = GF_TRUE;
	else if (split_dur <= -2) {
		split_size_kb = 0;
		split_until_end = GF_TRUE;
	}
	else if (force_rap_split)
		rap_split = GF_TRUE;

	//split in same dir as source
	strcpy(szName, inName);
	ext = strrchr(szName, '.');
	if (ext) ext[0] = 0;

	fs = gf_fs_new_defaults(0);
	if (!fs) {
		M4_LOG(GF_LOG_ERROR, ("Failed to load filter session, aborting\n"));
		return GF_IO_ERR;
	}

	//load source with all tracks processing
	sprintf(szArgs, "mp4dmx:mov=%p:alltk", mp4);
	src = gf_fs_load_filter(fs, szArgs, &e);

	if (!src) {
		M4_LOG(GF_LOG_ERROR, ("Failed to load source filter: %s\n", gf_error_to_string(e) ));
		gf_fs_del(fs);
		return e;
	}
	//default output name formatting
	if (!outName) {
		strcpy(szFile, szName);
		strcat(szFile, "_$num%03d$");
	}

	gf_dynstrcat(&filter_args, "reframer:splitrange", NULL);
	if (rap_split) {
		gf_dynstrcat(&filter_args, ":xs=SAP", NULL);
	} else if (split_size_kb) {
		sprintf(szArgs, ":xs=S"LLU"k", split_size_kb);
		gf_dynstrcat(&filter_args, szArgs, NULL);
	} else if (chunk_extraction) {
		//we adjust end: start at the iframe at or after requested time and use xadjust (move end to next I-frame)
		//so that two calls with X:Y and Y:Z have the same Y boundary
		if (adjust_split_end==1) {
			gf_dynstrcat(&filter_args, ":xadjust:xround=after", NULL);
		} else if (adjust_split_end==2) {
			gf_dynstrcat(&filter_args, ":xadjust:xround=before", NULL);
		} else if (adjust_split_end==3) {
			gf_dynstrcat(&filter_args, ":xround=seek", NULL);
		} else if (!gf_sys_find_global_arg("xround")) {
			gf_dynstrcat(&filter_args, ":xround=closest", NULL);
		}
		if (!adjust_split_end || (adjust_split_end==3)) {
			if (!gf_sys_find_global_arg("probe_ref")) {
				gf_dynstrcat(&filter_args, ":probe_ref", NULL);
			}
		}

		if (split_range_str) {
			Bool is_time = GF_FALSE;
			char c;
			//S-E syntax
			char *end = (char *) strchr(split_range_str, '-');
			if (!end) {
				//S:E syntax
				end = (char *) strchr(split_range_str, ':');
				//if another `:` assume time format
				if (end && strchr(end+1, ':'))
					is_time = GF_TRUE;
			} else if (strchr(split_range_str, ':')) {
				is_time = GF_TRUE;
			}
			if (!end) {
				gf_free(filter_args);
				M4_LOG(GF_LOG_ERROR, ("Invalid range specifer %s, expecting START-END or START:END\n", split_range_str ));
				gf_fs_del(fs);
				return GF_BAD_PARAM;
			}

			c = end[0];
			end[0] = 0;
			if (is_time) {
				sprintf(szArgs, ":xs=T%s:xe=T%s", split_range_str, end+1);
			} else {
				sprintf(szArgs, ":xs=%s:xe=%s", split_range_str, end+1);
			}
			end[0] = c;
		} else if (split_until_end) {
			Double end=0;
			if (split_dur<-2) {
				end = (Double) gf_isom_get_duration(mp4);
				end /= gf_isom_get_timescale(mp4);
				split_dur = -2 - split_dur;
				if (end > split_dur) end-=split_dur;
				else end = 0;
			}
			if (end>0) {
				sprintf(szArgs, ":xs=%u/1000:xe=%u/1000", (u32) (chunk_start*1000), (u32) (end * 1000) );
			} else {
				sprintf(szArgs, ":xs=%u/1000", (u32) (chunk_start*1000));
			}
		} else {
			sprintf(szArgs, ":xs=%u/1000:xe=%u/1000", (u32) (chunk_start*1000), (u32) ((chunk_start+split_dur) * 1000) );
		}
		gf_dynstrcat(&filter_args, szArgs, NULL);
		if (!outName) {
			sprintf(szFile, "%s_$FS$", szName);
		}
	} else if (split_dur) {
		sprintf(szArgs, ":xs=D%u", (u32) (split_dur*1000));
		gf_dynstrcat(&filter_args, szArgs, NULL);
		if (adjust_split_end) {
			gf_dynstrcat(&filter_args, ":xadjust", NULL);
		}
	} else {
		gf_fs_del(fs);
		gf_free(filter_args);
		M4_LOG(GF_LOG_ERROR, ("Unrecognized split syntax\n"));
		return GF_BAD_PARAM;
	}

	reframe = gf_fs_load_filter(fs, filter_args, &e);
	gf_free(filter_args);
	filter_args = NULL;
	if (!reframe) {
		M4_LOG(GF_LOG_ERROR, ("Failed to load reframer filter: %s\n", gf_error_to_string(e) ));
		gf_fs_del(fs);
		return e;
	}

	if (!outName) {
		strcat(szFile, ".mp4");
	} else {
		strcpy(szFile, outName);
	}
	if (gf_dir_exists(szFile)) {
		char c = szFile[strlen(szFile)-1];
		if ((c!='/') && (c!='\\'))
			strcat(szFile, "/");

		strcat(szFile, szName);
		strcat(szFile, "_$num%03d$.mp4");
		M4_LOG(GF_LOG_WARNING, ("Split output is a directory, will use template %s\n", szFile));
	}
	else if (split_size_kb || split_dur) {
		if (!strchr(szFile, '$') && (stricmp(szFile, "null") || !strcmp(szFile, "/dev/null")) ) {
			char *sep = gf_file_ext_start(szFile);
			if (sep) sep[0] = 0;
			strcat(szFile, "_$num$.mp4");
			M4_LOG(GF_LOG_WARNING, ("Split by %s but output not a template, using %s as output\n", split_size_kb ? "size" : "duration", szFile));
		}
	}
	if (do_frag) {
		sprintf(szArgs, ":cdur=%g", interleaving_time);
		strcat(szFile, ":store=frag");
		strcat(szFile, szArgs);
	}
	else if (do_flat==1) {
		strcat(szFile, ":store=flat");
	}
	else if (do_flat || interleaving_time) {
		if (do_flat==3) {
			strcat(szFile, ":store=fstart");
		}
		sprintf(szArgs, ":cdur=%g", interleaving_time);
		strcat(szFile, szArgs);
	}

	dst = gf_fs_load_destination(fs, szFile, NULL, NULL, &e);
	if (!dst) {
		M4_LOG(GF_LOG_ERROR, ("Failed to load destination filter: %s\n", gf_error_to_string(e) ));
		gf_fs_del(fs);
		return e;
	}
	//link reframer to dst
	gf_filter_set_source(dst, reframe, NULL);

	if (!gf_sys_is_test_mode()
#ifndef GPAC_DISABLE_LOG
		&& (gf_log_get_tool_level(GF_LOG_APP)!=GF_LOG_QUIET)
#endif
		&& !gf_sys_is_quiet()
	) {
		gf_fs_enable_reporting(fs, GF_TRUE);
		gf_fs_set_ui_callback(fs, on_split_event, &sinfo);
	}
#ifdef GPAC_ENABLE_COVERAGE
	else if (gf_sys_is_cov_mode()) {
		on_split_event(NULL, NULL);
	}
#endif //GPAC_ENABLE_COVERAGE


	e = gf_fs_run(fs);
	if (e>=GF_OK) {
		e = gf_fs_get_last_connect_error(fs);
		if (e>=GF_OK)
			e = gf_fs_get_last_process_error(fs);
	}
	gf_fs_print_non_connected(fs);
	if (fs_dump_flags & 1) gf_fs_print_stats(fs);
	if (fs_dump_flags & 2) gf_fs_print_connections(fs);

	gf_fs_del(fs);
	
	if (e<GF_OK)
		M4_LOG(GF_LOG_ERROR, ("Split failed: %s\n", gf_error_to_string(e) ));
	return e;
}
GF_Err split_isomedia_file(GF_ISOFile *mp4, Double split_dur, u64 split_size_kb, char *inName, Double InterleavingTime, Double chunk_start_time, u32 adjust_split_end, char *outName, Bool force_rap_split, const char *split_range_str, u32 fs_dump_flags)
{
	Bool chunk_extraction, rap_split, split_until_end;
	GF_Err e;
	char *ext, szName[GF_MAX_PATH], szFile[GF_MAX_PATH+100], szArgs[100];
	Double chunk_start = (Double) chunk_start_time;
	char *filter_args = NULL;
	GF_FilterSession *fs;
	GF_Filter *src, *reframe, *dst;
	SplitInfo sinfo;

	sinfo.progress = -1;
	sinfo.file_idx = 1;

	chunk_extraction = (chunk_start>=0) ? GF_TRUE : GF_FALSE;
	if (split_range_str)
		chunk_extraction = GF_TRUE;
	split_until_end = GF_FALSE;
	rap_split = GF_FALSE;
	if (split_size_kb == (u64)-1) rap_split = GF_TRUE;
	if (split_dur == -1) rap_split = GF_TRUE;
	else if (split_dur <= -2) {
		split_size_kb = 0;
		split_until_end = GF_TRUE;
	}
	else if (force_rap_split)
		rap_split = GF_TRUE;

	 
	strcpy(szName, inName);
	ext = strrchr(szName, '.');
	if (ext) ext[0] = 0;

	fs = gf_fs_new_defaults(0);
	if (!fs) {
		M4_LOG(GF_LOG_ERROR, ("Failed to load filter session, aborting\n"));
		return GF_IO_ERR;
	}

	 
	sprintf(szArgs, "mp4dmx:mov=%p:alltk", mp4);
	src = gf_fs_load_filter(fs, szArgs, &e);

	if (!src) {
		M4_LOG(GF_LOG_ERROR, ("Failed to load source filter: %s\n", gf_error_to_string(e) ));
		gf_fs_del(fs);
		return e;
	}
	 
	if (!outName) {
		strcpy(szFile, szName);
		strcat(szFile, "_$num%03d$");
	}

	gf_dynstrcat(&filter_args, "reframer:splitrange", NULL);
	if (rap_split) {
		gf_dynstrcat(&filter_args, ":xs=SAP", NULL);
	} else if (split_size_kb) {
		sprintf(szArgs, ":xs=S"LLU"k", split_size_kb);
		gf_dynstrcat(&filter_args, szArgs, NULL);
	} else if (chunk_extraction) {
		 
		 
		if (adjust_split_end==1) {
			gf_dynstrcat(&filter_args, ":xadjust:xround=after", NULL);
		} else if (adjust_split_end==2) {
			gf_dynstrcat(&filter_args, ":xadjust:xround=before", NULL);
		} else if (adjust_split_end==3) {
			gf_dynstrcat(&filter_args, ":xround=seek", NULL);
		} else if (!gf_sys_find_global_arg("xround")) {
			gf_dynstrcat(&filter_args, ":xround=closest", NULL);
		}
		if (!adjust_split_end || (adjust_split_end==3)) {
			if (!gf_sys_find_global_arg("probe_ref")) {
				gf_dynstrcat(&filter_args, ":probe_ref", NULL);
			}
		}

		if (split_range_str) {
			Bool is_time = GF_FALSE;
			char c;
			 
			char *end = (char *) strchr(split_range_str, '-');
			if (!end) {
				 
				end = (char *) strchr(split_range_str, ':');
				 
				if (end && strchr(end+1, ':'))
					is_time = GF_TRUE;
			} else if (strchr(split_range_str, ':')) {
				is_time = GF_TRUE;
			}
			if (!end) {
				gf_free(filter_args);
				M4_LOG(GF_LOG_ERROR, ("Invalid range specifer %s, expecting START-END or START:END\n", split_range_str ));
				gf_fs_del(fs);
				return GF_BAD_PARAM;
			}

			c = end[0];
			end[0] = 0;
			if (is_time) {
				sprintf(szArgs, ":xs=T%s:xe=T%s", split_range_str, end+1);
			} else {
				sprintf(szArgs, ":xs=%s:xe=%s", split_range_str, end+1);
			}
			end[0] = c;
		} else if (split_until_end) {
			Double end=0;
			if (split_dur<-2) {
				end = (Double) gf_isom_get_duration(mp4);
				end /= gf_isom_get_timescale(mp4);
				split_dur = -2 - split_dur;
				if (end > split_dur) end-=split_dur;
				else end = 0;
			}
			if (end>0) {
				sprintf(szArgs, ":xs=%u/1000:xe=%u/1000", (u32) (chunk_start*1000), (u32) (end * 1000) );
			} else {
				sprintf(szArgs, ":xs=%u/1000", (u32) (chunk_start*1000));
			}
		} else {
			sprintf(szArgs, ":xs=%u/1000:xe=%u/1000", (u32) (chunk_start*1000), (u32) ((chunk_start+split_dur) * 1000) );
		}
		gf_dynstrcat(&filter_args, szArgs, NULL);
		if (!outName) {
			sprintf(szFile, "%s_$FS$", szName);
		}
	} else if (split_dur) {
		sprintf(szArgs, ":xs=D%u", (u32) (split_dur*1000));
		gf_dynstrcat(&filter_args, szArgs, NULL);
		if (adjust_split_end) {
			gf_dynstrcat(&filter_args, ":xadjust", NULL);
		}
	} else {
		gf_fs_del(fs);
		gf_free(filter_args);
		M4_LOG(GF_LOG_ERROR, ("Unrecognized split syntax\n"));
		return GF_BAD_PARAM;
	}

	reframe = gf_fs_load_filter(fs, filter_args, &e);
	gf_free(filter_args);
	filter_args = NULL;
	if (!reframe) {
		M4_LOG(GF_LOG_ERROR, ("Failed to load reframer filter: %s\n", gf_error_to_string(e) ));
		gf_fs_del(fs);
		return e;
	}

	if (!outName) {
		strcat(szFile, ".mp4");
	} else {
		strcpy(szFile, outName);
	}
	if (gf_dir_exists(szFile)) {
		char c = szFile[strlen(szFile)-1];
		if ((c!='/') && (c!='\\'))
			strcat(szFile, "/");

		strcat(szFile, szName);
		strcat(szFile, "_$num%03d$.mp4");
		M4_LOG(GF_LOG_WARNING, ("Split output is a directory, will use template %s\n", szFile));
	}
	else if (split_size_kb || split_dur) {
		if (!strchr(szFile, '$') && (stricmp(szFile, "null") || !strcmp(szFile, "/dev/null")) ) {
			char *sep = gf_file_ext_start(szFile);
			if (sep) sep[0] = 0;
			strcat(szFile, "_$num$.mp4");
			M4_LOG(GF_LOG_WARNING, ("Split by %s but output not a template, using %s as output\n", split_size_kb ? "size" : "duration", szFile));
		}
	}
	if (do_frag) {
		sprintf(szArgs, ":cdur=%g", interleaving_time);
		strcat(szFile, ":store=frag");
		strcat(szFile, szArgs);
	}
	else if (do_flat==1) {
		strcat(szFile, ":store=flat");
	}
	else if (do_flat || interleaving_time) {
		if (do_flat==3) {
			strcat(szFile, ":store=fstart");
		}
		sprintf(szArgs, ":cdur=%g", interleaving_time);
		strcat(szFile, szArgs);
	}

	dst = gf_fs_load_destination(fs, szFile, NULL, NULL, &e);
	if (!dst) {
		M4_LOG(GF_LOG_ERROR, ("Failed to load destination filter: %s\n", gf_error_to_string(e) ));
		gf_fs_del(fs);
		return e;
	}
	 
	gf_filter_set_source(dst, reframe, NULL);

	if (!gf_sys_is_test_mode()
#ifndef GPAC_DISABLE_LOG
		&& (gf_log_get_tool_level(GF_LOG_APP)!=GF_LOG_QUIET)
#endif
		&& !gf_sys_is_quiet()
	) {
		gf_fs_enable_reporting(fs, GF_TRUE);
		gf_fs_set_ui_callback(fs, on_split_event, &sinfo);
	}
#ifdef GPAC_ENABLE_COVERAGE
	else if (gf_sys_is_cov_mode()) {
		on_split_event(NULL, NULL);
	}
#endif  


	e = gf_fs_run(fs);
	if (e>=GF_OK) {
		e = gf_fs_get_last_connect_error(fs);
		if (e>=GF_OK)
			e = gf_fs_get_last_process_error(fs);
	}
	gf_fs_print_non_connected(fs);
	if (fs_dump_flags & 1) gf_fs_print_stats(fs);
	if (fs_dump_flags & 2) gf_fs_print_connections(fs);

	gf_fs_del(fs);
	
	if (e<GF_OK)
		M4_LOG(GF_LOG_ERROR, ("Split failed: %s\n", gf_error_to_string(e) ));
	return e;
}