diff --git a/applications/mp4box/main.c b/applications/mp4box/main.c index e2f72f383..b63ebb31c 100644 --- a/applications/mp4box/main.c +++ b/applications/mp4box/main.c @@ -461,6 +461,7 @@ void PrintDASHUsage() " !! Do not use with multiple periods, nor when DASH duration is not a multiple of GOP size !!\n" " -cues ignores dash duration and segment according to cue times in given XML file. See tests/media/dash_cues for examples.\n" " -strict-cues throw error if something is wrong while parsing cues or applying cue-based segmentation.\n" + " -merge-last-seg merges last segment if shorter than half the target duration.\n" "\n"); } @@ -2288,6 +2289,7 @@ Bool do_bin_nhml = GF_FALSE; #endif GF_ISOFile *file; Bool frag_real_time = GF_FALSE; +Bool merge_last_seg = GF_FALSE; u64 dash_start_date=0; GF_DASH_ContentLocationMode cp_location_mode = GF_DASH_CPMODE_ADAPTATION_SET; Double mpd_update_time = GF_FALSE; @@ -3856,6 +3858,9 @@ Bool mp4box_parse_args(int argc, char **argv) else if (!stricmp(arg, "-frag-rt")) { frag_real_time = GF_TRUE; } + else if (!stricmp(arg, "-merge-last-seg")) { + merge_last_seg = GF_TRUE; + } else if (!stricmp(arg, "-start-date")) { dash_start_date = gf_net_parse_date(argv[i+1]); i++; @@ -4696,6 +4701,7 @@ int mp4boxMain(int argc, char **argv) if (!e) e = gf_dasher_set_test_mode(dasher,force_test_mode); if (!e) e = gf_dasher_enable_loop_inputs(dasher, ! no_loop); if (!e) e = gf_dasher_set_split_on_bound(dasher, split_on_bound); + if (!e) e = gf_dasher_set_last_segment_merge(dasher, merge_last_seg); if (!e) e = gf_dasher_set_split_on_closest(dasher, split_on_closest); if (!e && dash_cues) e = gf_dasher_set_cues(dasher, dash_cues, strict_cues); if (!e) e = gf_dasher_set_isobmff_options(dasher, mvex_after_traks, sdtp_in_traf); diff --git a/include/gpac/media_tools.h b/include/gpac/media_tools.h index f3bae593d..1a07e8ad1 100644 --- a/include/gpac/media_tools.h +++ b/include/gpac/media_tools.h @@ -938,6 +938,14 @@ GF_Err gf_dasher_enable_loop_inputs(GF_DASHSegmenter *dasher, Bool do_loop); */ GF_Err gf_dasher_set_split_on_bound(GF_DASHSegmenter *dasher, Bool split_on_bound); +/*! + Enable/Disable last segment merging (disabled by default). + * \param dasher the DASH segmenter object + * \param merge_last_seg if true, last segment is merged into previous if duration less than half target dur + * \return error code if any +*/ +GF_Err gf_dasher_set_last_segment_merge(GF_DASHSegmenter *dasher, Bool merge_last_seg); + /*! Enable/Disable split on closest mode. * \param dasher the DASH segmenter object diff --git a/src/export.cpp b/src/export.cpp index e78ce7895..0bef90e49 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -2203,7 +2203,7 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_dasher_set_cues) ) #pragma comment (linker, EXPORT_SYMBOL(gf_dasher_set_isobmff_options) ) #pragma comment (linker, EXPORT_SYMBOL(gf_dasher_set_test_mode) ) - +#pragma comment (linker, EXPORT_SYMBOL(gf_dasher_set_last_segment_merge) ) #pragma comment (linker, EXPORT_SYMBOL(gf_dasher_next_update_time) ) diff --git a/src/media_tools/dash_segmenter.c b/src/media_tools/dash_segmenter.c index 96f9d5cd5..32e8696e7 100644 --- a/src/media_tools/dash_segmenter.c +++ b/src/media_tools/dash_segmenter.c @@ -148,9 +148,11 @@ struct __gf_dash_segmenter /* indicates if a segment must contain its theorical boundary */ Bool split_on_bound; - /* used to segment video as close to the boundary as possible */ Bool split_on_closest; + + Bool merge_last_seg; + const char *cues_file; Bool strict_cues; @@ -2274,8 +2276,12 @@ restart_fragmentation_pass: next_sap_time = isom_get_next_sap_time(input, tf, tf->OriginalTrack, tf->SampleCount, tf->SampleNum + 2, &next_sap_is_eos); //align behaviour of -bound and -closest in legacy with master: if eos, do not create last segment - if ((dasher->split_on_bound || dasher->split_on_closest) && next_sap_is_eos) - next_sap_time = 0; + if ((dasher->split_on_bound || dasher->split_on_closest) && next_sap_is_eos && dasher->merge_last_seg) { + u64 next_seg_dur = (next_sap_time - tf->last_sample_cts); + if (2 * next_seg_dur * dasher->dash_scale < MaxSegmentDuration * tf->TimeScale) { + next_sap_time = 0; + } + } /*if no more SAP after this one, do not switch segment*/ if (next_sap_time) { @@ -6584,6 +6590,14 @@ GF_Err gf_dasher_set_split_on_bound(GF_DASHSegmenter *dasher, Bool split_on_boun return GF_OK; } +GF_EXPORT +GF_Err gf_dasher_set_last_segment_merge(GF_DASHSegmenter *dasher, Bool merge_last_seg) +{ + if (!dasher) return GF_BAD_PARAM; + dasher->merge_last_seg = merge_last_seg; + return GF_OK; +} + GF_EXPORT GF_Err gf_dasher_set_split_on_closest(GF_DASHSegmenter *dasher, Bool split_on_closest) {