fixes in dash client SRD and tiling handling (see #1491) and added tile adaptation in custom algos (see #1634)

This commit is contained in:
jeanlf
2020-11-20 18:27:37 +01:00
parent 3ed13ae45b
commit 8f9506c58d
14 changed files with 529 additions and 161 deletions

View File

@@ -475,7 +475,7 @@ Bool gf_dash_group_enum_descriptor(GF_DashClient *dash, u32 group_idx, GF_DashDe
\param has_next_segment set to GF_TRUE if next segment location is known (unthreaded mode) or next segment is downloaded (threaded mode) (optional, may be NULL)
\param key_url set to the key URL of the next segment for MPEG-2 TS full segment encryption (optional, may be NULL)
\param key_IV set to the key initialization vector of the next segment for MPEG-2 TS full segment encryption (optional, may be NULL)
\return GF_BUFFER_TOO_SMALL if no segment found, GF_EOS if end of session or error if any
\return GF_BUFFER_TOO_SMALL if no segment found, GF_EOS if end of session, GF_URL_REMOVED if segment is disabled (but all output info is OK, this can be ignored and considered as GF_OK by the user) or error if any
*/
GF_Err gf_dash_group_get_next_segment_location(GF_DashClient *dash, u32 group_idx, u32 dependent_representation_index, const char **url, u64 *start_range, u64 *end_range,
s32 *switching_index, const char **switching_url, u64 *switching_start_range, u64 *switching_end_range,
@@ -867,6 +867,13 @@ typedef enum
*/
void gf_dash_set_tile_adaptation_mode(GF_DashClient *dash, GF_DASHTileAdaptationMode mode, u32 tile_rate_decrease);
/*! consider tile with highest quality degradation hints (not visible ones or not gazed at) as lost, triggering a GF_URL_REMOVE upon \ref gf_dash_group_get_next_segment_location calls. Mostly used to debug tiling adaptation
\param dash the target dash client
\param disable_tiles if GF_TRUE, tiles with highest quality degradation hints will not be played.
*/
void gf_dash_disable_low_quality_tiles(GF_DashClient *dash, Bool disable_tiles);
/*! gets current tile adaptation mode
\param dash the target dash client
\return tile adaptation mode*/
@@ -1000,27 +1007,50 @@ u32 gf_dash_get_min_wait_ms(GF_DashClient *dash);
*/
s32 gf_dash_group_get_as_id(GF_DashClient *dash, u32 group_idx);
//any change to the structure below MUST be reflected in libgpac.py !!
/*! Information passed to DASH custom algorithm*/
typedef struct
{
/*! last segment download rate in bits per second */
u32 download_rate;
/*! size of last downloaded segment*/
u32 file_size;
/*! current playback speed*/
Double speed;
/*! max supported playback speed according to associated decoder stats*/
Double max_available_speed;
/*! display width of the video in pixels, 0 if audio stream*/
u32 disp_width;
/*! display height of the video in pixels, 0 if audio stream*/
u32 disp_height;
/*! index of currently selected quality*/
u32 active_quality_idx;
/*! minimum buffer level in milliseconds below witch rebuffer will happen*/
u32 buffer_min_ms;
/*! maximum buffer level allowed in milliseconds. Packets won't get dropped if overflow, but the algorithm should try not to overflow this buffer*/
u32 buffer_max_ms;
/*! current buffer level in milliseconds*/
u32 buffer_occupancy_ms;
/*! degradation hint, 0 means no degradation, 100 means tile completely hidden*/
u32 quality_degradation_hint;
/*! cumulated download rate of all active groups - 0 means all files are local*/
u32 total_rate;
} GF_DASHCustomAlgoInfo;
/*! Callback function for custom rate adaptation
\param udta user data
\param group_idx index of group to adapt
\param base_group_idx index of associated base group if group is a dependent group
\param download_rate last segment download rate in bits per second
\param speed current playback speed
\param max_available_speed max supported playback speed according to associated decoder stats
\param display_width display width of the video in pixels, 0 if audio stream
\param display_height display height of the video in pixels, 0 if audio stream
\param force_lower_complexity set to true if the dash client would like a lower complexity
\param active_quality_idx index of currently selected quality
\param buffer_min_ms minimum buffer level in milliseconds below witch rebuffer will happen
\param buffer_max_ms maximum buffer level allowed in milliseconds. Packets won't get dropped if overflow, but the algorithm should try not to overflow this buffer
\param buffer_occupancy_ms current buffer level in milliseconds
\return the index of the new quality to select (as listed in group.reps[]), or -1 to not take decision now and postpon it until dependent groups are done
\param stats statistics for last downloaded segment
\return value can be:
- the index of the new quality to select (as listed in group.reps[])
- `-1` to not take decision now and postpone it until dependent groups are done
- `-2` to disable quality
- any other negative value means error
*/
typedef s32 (*gf_dash_rate_adaptation)(void *udta, u32 group_idx, u32 base_group_idx,
u32 download_rate, u32 file_size, Double speed, Double max_available_speed,
u32 disp_width, u32 disp_height, Bool force_lower_complexity,
u32 active_quality_idx, u32 buffer_min_ms, u32 buffer_max_ms, u32 buffer_occupancy_ms);
typedef s32 (*gf_dash_rate_adaptation)(void *udta, u32 group_idx, u32 base_group_idx, Bool force_lower_complexity, GF_DASHCustomAlgoInfo *stats);
/*! Callback function for custom rate monitor, not final yet
\param udta user data

View File

@@ -59,7 +59,11 @@ attribute void period_reset(unsigned long reset_type);
\param force_lower_complexity set to true if the dash client would like a lower complexity
\param stats current statistics for the group JSDASHStats
\return the index of the new quality to select (as listed in group.reps[]), or -1 to not take decision now and postpon it until dependent groups are done
\return value can be:
- the index of the new quality to select (as listed in group.reps[])
- -1 to not take decision now
- -2 to disable the group (debug, will drop segment)
- other negative values are handled as error
*/
attribute int rate_adaptation(unsigned long group_idx, unsigned long base_group_idx, Boolean force_lower_complexity, JSDASHStats stats);
@@ -90,6 +94,10 @@ attribute readonly unsigned long idx;
/*! group duration in milliseconds, 0 if unknown (live) */
attribute readonly unsigned long long duration;
/*! Spatial Relationship Description, or null if none*/
attribute readonly JSDASHSRD SRD;
/*! list of available qualites, array of JSDASHQuality objects*/
attribute readonly Array qualities;
@@ -209,5 +217,36 @@ attribute readonly unsigned long current_segment_duration;
};
/*!\brief JSDASHSRD
The JSDASHSRD object provides the SRD description of a group.
*/
interface JSDASHSRD {
/*! ID of SRD source - all SRD with same source describe the same video composition, possibly with different grid sizes*/
attribute readonly unsigned long ID;
/*! X coordinate of SRD for this tile*/
attribute readonly long x;
/*! Y coordinate of SRD for this tile*/
attribute readonly long y;
/*! width of SRD for this tile - 0 for tile base track*/
attribute readonly long w;
/*! height of SRD for this tile - 0 for tile base track*/
attribute readonly long h;
/*! total width of SRD descriptor for this tile*/
attribute readonly long fw;
/*! total height of SRD descriptor for this tile*/
attribute readonly long fh;
};
/*! @} */

View File

@@ -724,6 +724,12 @@ drv (enum, default: auto): indicate if graphics driver should be used. Ignor
.br
src (cstr): URL of source content
.br
gaze_x (sint, default: 0, updatable): horizontal gaze coordinate (0=left, width=right)
.br
gaze_y (sint, default: 0, updatable): vertical gaze coordinate (0=top, height=bottom)
.br
gazer_enabled (bool, default: false, updatable): enable gaze event dispatch
.br
.br
.SH mp4dmx
@@ -1630,6 +1636,8 @@ filemode (bool, default: no): do not demux files and forward them as file pids
.br
fmodefwd (bool, default: yes): forward packet rather than copy them in .I filemode. Packet copy might improve performances in low latency mode
.br
skip_lqt (bool, default: no): disable decoding of tiles with highest degradation hints (not visible, not gazed at) for debug purposes
.br
.br
.SH cdcrypt
@@ -6804,7 +6812,7 @@ Authors: GPAC developers, see git repo history (-log)
.br
For bug reports, feature requests, more information and source code, visit http://github.com/gpac/gpac
.br
build: 1.1.0-DEV-rev263-g3bea3b4d9-master
build: 1.1.0-DEV-rev272-g3ed13ae45-master
.br
Copyright: (c) 2000-2020 Telecom Paris distributed under LGPL v2.1+ - http://gpac.io
.br

View File

@@ -3883,7 +3883,7 @@ Authors: GPAC developers, see git repo history (-log)
.br
For bug reports, feature requests, more information and source code, visit http://github.com/gpac/gpac
.br
build: 1.1.0-DEV-rev263-g3bea3b4d9-master
build: 1.1.0-DEV-rev272-g3ed13ae45-master
.br
Copyright: (c) 2000-2020 Telecom Paris distributed under LGPL v2.1+ - http://gpac.io
.br

View File

@@ -2393,7 +2393,7 @@ Authors: GPAC developers, see git repo history (-log)
.br
For bug reports, feature requests, more information and source code, visit http://github.com/gpac/gpac
.br
build: 1.1.0-DEV-rev263-g3bea3b4d9-master
build: 1.1.0-DEV-rev272-g3ed13ae45-master
.br
Copyright: (c) 2000-2020 Telecom Paris distributed under LGPL v2.1+ - http://gpac.io
.br

View File

@@ -261,7 +261,7 @@ Authors: GPAC developers, see git repo history (-log)
.br
For bug reports, feature requests, more information and source code, visit http://github.com/gpac/gpac
.br
build: 1.1.0-DEV-rev263-g3bea3b4d9-master
build: 1.1.0-DEV-rev272-g3ed13ae45-master
.br
Copyright: (c) 2000-2020 Telecom Paris distributed under LGPL v2.1+ - http://gpac.io
.br

View File

@@ -997,6 +997,9 @@ extension = {
var url_arg = scene.get_option('temp', 'gui_load_url');
var prog_name = Sys.args[0];
var check_gpac_args = 1;
if (url_arg && (url_arg.indexOf('://') < 0) )
url_arg = 'gpac://' + url_arg;
//check mp4client or MP4Client
if (prog_name && (prog_name.indexOf('lient')>=0))
check_gpac_args = 0;

View File

@@ -365,15 +365,23 @@ def sys_clock():
def sys_clock_high_res():
return _libgpac.gf_sys_clock_high_res()
##\cond private
#keep args at libgpac level to avoid python GC
_libgpac._args=None
##\endcond private
## set libgpac arguments - see \ref gf_sys_set_args
# \param args list of strings
# \return
def set_args(args):
p = (POINTER(c_char)*len(args))()
nb_args = len(args)
_libgpac._args = (POINTER(c_char)*nb_args)()
for i, arg in enumerate(args):
enc_arg = arg.encode('utf-8')
p[i] = create_string_buffer(enc_arg)
_libgpac.gf_sys_set_args(len(args), cast(p, POINTER(POINTER(c_char))) )
_libgpac._args[i] = create_string_buffer(enc_arg)
_libgpac.gf_sys_set_args(nb_args, cast(_libgpac._args, POINTER(POINTER(c_char))) )
##\cond private
@@ -1721,6 +1729,7 @@ _libgpac.gf_dash_group_get_num_qualities.argtypes = [c_void_p, c_uint]
_libgpac.gf_dash_get_period_duration.argtypes = [c_void_p]
_libgpac.gf_dash_group_get_quality_info.argtypes = [c_void_p, c_uint, c_uint, POINTER(DASHQualityInfoNat) ]
_libgpac.gf_dash_group_get_srd_info.argtypes = [c_void_p, c_uint, POINTER(c_uint), POINTER(c_uint), POINTER(c_uint), POINTER(c_uint), POINTER(c_uint), POINTER(c_uint), POINTER(c_uint) ]
_libgpac.gf_list_count.argtypes = [c_void_p]
_libgpac.gf_list_count.restype = c_uint
@@ -1785,6 +1794,42 @@ class DASHQualityInfo:
self.sizes.append( surl.media_range.contents.end - surl.media_range.contents.start + 1)
## \endcond priv
##DASH Spatial Relation Descriptor object, used for tiling
class DASHSRD:
## \cond priv
def __init__(self, id, x, y, w, h, fw, fh):
## \endcond
## ID of SRD source - all SRD with same source describe the same video composition, possibly with different grid sizes
self.id = id
## X coordinate of SRD for this tile
self.x = x
## Y coordinate of SRD for this tile
self.y = y
## width of SRD for this tile - 0 for tile base track
self.w = w
## height of SRD for this tile - 0 for tile base track
self.h = h
## total width of SRD descriptor for this tile
self.fw = fw
## total height of SRD descriptor for this tile
self.fh = fh
##\cond priv
def make_srd(dashptr, groupidx):
srd_id=c_uint(0)
srd_x=c_uint(0)
srd_y=c_uint(0)
srd_w=c_uint(0)
srd_h=c_uint(0)
srd_fw=c_uint(0)
srd_fh=c_uint(0)
_libgpac.gf_dash_group_get_srd_info(dashptr, groupidx, byref(srd_id), byref(srd_x), byref(srd_y), byref(srd_w), byref(srd_h), byref(srd_fw), byref(srd_fh) )
if not srd_fw.value or not srd_fh.value:
return None
return DASHSRD(srd_id.value, srd_x.value, srd_y.value, srd_w.value, srd_h.value, srd_fw.value, srd_fh.value)
##\endcond
## DASH group object
class DASHGroup:
## \cond priv
@@ -1796,6 +1841,8 @@ class DASHGroup:
self.qualities = []
## period duration in milliseconds, 0 if unknwon
self.duration = _libgpac.gf_dash_get_period_duration(ptr_dash)
## SRD object or None if no SRD defined
self.SRD = make_srd(ptr_dash, groupidx)
## \cond priv
self._dash = ptr_dash
nb_qualities = _libgpac.gf_dash_group_get_num_qualities(ptr_dash, groupidx)
@@ -1831,6 +1878,10 @@ class DASHGroupStatistics(Structure):
self.buffer_max = 0
##current buffer in milliseconds
self.buffer = 0
##degradation hint, 0 means no degradation, 100 means tile completely hidden
self.quality_degradation_hint = 0
##cumulated download rate of all active groups - 0 means all files are local
self.total_rate = 0
## \cond private
_fields_ = [
@@ -1844,6 +1895,8 @@ class DASHGroupStatistics(Structure):
("buffer_min", c_uint),
("buffer_max", c_uint),
("buffer", c_uint),
("degradation_hint", c_uint),
("total_rate", c_uint)
]
def __str__(self):
res = 'active_quality_idx ' + str(self.active_quality_idx)
@@ -1915,8 +1968,12 @@ class DASHCustomAlgorithm:
#\param base_group the associated base \ref DASHGroup (tiling only), or None if no base group
#\param force_low_complexity indicates that the client would like a lower complexity (typically because it is dropping frames)
#\param stats the \ref DASHGroupStatistics for the downloaded segment
#\return new quality index, or -1 to postpone (for tiling)
def on_new_group(self, group, base_group, force_low_complexity, stats):
#\return value can be:
# - new quality index,
# - -1 to take no decision
# - -2 to disable quality (debug, will drop segment)
# - other negative values are handled as error
def on_rate_adaptation(self, group, base_group, force_low_complexity, stats):
pass
##Callback (optional) called on regular basis during a segment download

View File

@@ -9,11 +9,39 @@ print("Hello DASH custom algo !");
let groups = [];
function get_group(group_idx)
{
for (let i=0; i < groups.length; i++) {
if (groups[i].idx == group_idx)
return groups[i];
}
return null;
}
let nb_adapt=0;
dashin.rate_adaptation = function (group_idx, base_group_idx, force_lower_complexity, stats)
{
print(`Getting called in custom algo ! group ${group_idx} base_group ${base_group_idx} force_lower_complexity ${force_lower_complexity}`);
print('Stats: ' + JSON.stringify(stats));
// print(`Getting called in custom algo ! group ${group_idx} base_group ${base_group_idx} force_lower_complexity ${force_lower_complexity}`);
// print('Stats: ' + JSON.stringify(stats));
let g = get_group(group_idx);
if (!g) return -1;
if (g.SRD && !g.SRD.w) {
nb_adapt++;
print('\n\nnb_adapt ' + nb_adapt);
}
else if (g.SRD && g.SRD.w) {
//if (!g.SRD.x || !g.SRD.y) {
if (stats.degradation_hint) {
print('disabling hidden tile ' + g.SRD.x + 'x' + g.SRD.y);
return -2;
}
print('use high quality for tile ' + g.SRD.x + 'x' + g.SRD.y);
return 1;
}
//always use lowest quality
return 0;
}

View File

@@ -2220,6 +2220,7 @@
#pragma comment (linker, EXPORT_SYMBOL(gf_dash_get_dependent_group_index) )
#pragma comment (linker, EXPORT_SYMBOL(gf_dash_set_tile_adaptation_mode) )
#pragma comment (linker, EXPORT_SYMBOL(gf_dash_get_tile_adaptation_mode) )
#pragma comment (linker, EXPORT_SYMBOL(gf_dash_disable_low_quality_tiles) )
#pragma comment (linker, EXPORT_SYMBOL(gf_dash_group_get_srd_max_size_info) )
#pragma comment (linker, EXPORT_SYMBOL(gf_dash_group_get_srd_info) )
#pragma comment (linker, EXPORT_SYMBOL(gf_dash_group_set_quality_degradation_hint) )

View File

@@ -925,6 +925,10 @@ static GF_FilterArgs CompositorArgs[] =
"- auto: decides based on the loaded content"\
, GF_PROP_UINT, "auto", "no|yes|auto", GF_FS_ARG_HINT_EXPERT},
{ OFFS(src), "URL of source content", GF_PROP_NAME, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
{ OFFS(gaze_x), "horizontal gaze coordinate (0=left, width=right)", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
{ OFFS(gaze_y), "vertical gaze coordinate (0=top, height=bottom)", GF_PROP_SINT, "0", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
{ OFFS(gazer_enabled), "enable gaze event dispatch", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT|GF_FS_ARG_UPDATE},
{0}
};

View File

@@ -41,7 +41,7 @@ typedef struct
s32 shift_utc, debug_as, route_shift;
u32 max_buffer, auto_switch, tiles_rate, segstore, delay40X, exp_threshold, switch_count, bwcheck;
s32 init_timeshift;
Bool server_utc, screen_res, aggressive, speedadapt, filemode, fmodefwd;
Bool server_utc, screen_res, aggressive, speedadapt, filemode, fmodefwd, skip_lqt;
GF_DASHInitialSelectionMode start_with;
GF_DASHTileAdaptationMode tile_mode;
char *algo;
@@ -124,7 +124,9 @@ typedef struct
u32 last_bw_check;
u64 us_at_seg_start;
Bool signal_seg_name;
u32 seg_discard_state;
char *template;
} GF_DASHGroup;
@@ -504,12 +506,30 @@ u32 dashdmx_io_get_bytes_done(GF_DASHFileIO *dashio, GF_DASHFileIOSession sessio
void dashdmx_js_declare_group(GF_DASHDmxCtx *ctx, u32 group_idx)
{
u32 i, count;
u32 srd_id, srd_x, srd_y, srd_w, srd_h, srd_fw, srd_fh;
JSValue res, reps;
JSValue obj = JS_NewObject(ctx->js_ctx);
JS_SetPropertyStr(ctx->js_ctx, obj, "idx", JS_NewInt32(ctx->js_ctx, group_idx));
JS_SetPropertyStr(ctx->js_ctx, obj, "duration", JS_NewInt64(ctx->js_ctx, gf_dash_get_period_duration(ctx->dash) ));
srd_id = srd_x = srd_y = srd_w = srd_h = srd_fw = srd_fh = 0;
gf_dash_group_get_srd_info(ctx->dash, group_idx, &srd_id, &srd_x, &srd_y, &srd_w, &srd_h, &srd_fw, &srd_fh);
if (srd_fw && srd_fh) {
JSValue srd = JS_NewObject(ctx->js_ctx);
JS_SetPropertyStr(ctx->js_ctx, srd, "ID", JS_NewInt32(ctx->js_ctx, srd_id));
JS_SetPropertyStr(ctx->js_ctx, srd, "x", JS_NewInt32(ctx->js_ctx, srd_x));
JS_SetPropertyStr(ctx->js_ctx, srd, "y", JS_NewInt32(ctx->js_ctx, srd_y));
JS_SetPropertyStr(ctx->js_ctx, srd, "w", JS_NewInt32(ctx->js_ctx, srd_w));
JS_SetPropertyStr(ctx->js_ctx, srd, "h", JS_NewInt32(ctx->js_ctx, srd_h));
JS_SetPropertyStr(ctx->js_ctx, srd, "fw", JS_NewInt32(ctx->js_ctx, srd_fw));
JS_SetPropertyStr(ctx->js_ctx, srd, "fh", JS_NewInt32(ctx->js_ctx, srd_fh));
JS_SetPropertyStr(ctx->js_ctx, obj, "SRD", srd);
} else {
JS_SetPropertyStr(ctx->js_ctx, obj, "SRD", JS_NULL);
}
reps = JS_NewArray(ctx->js_ctx);
count = gf_dash_group_get_num_qualities(ctx->dash, group_idx);
@@ -575,6 +595,18 @@ void dashdmx_js_declare_group(GF_DASHDmxCtx *ctx, u32 group_idx)
}
#endif
static void dashdmx_declare_group(GF_DASHDmxCtx *ctx, u32 group_idx)
{
if (ctx->on_new_group)
ctx->on_new_group(ctx->rt_udta, group_idx, ctx->dash);
#ifdef GPAC_HAS_QJS
else if (ctx->js_ctx && JS_IsFunction(ctx->js_ctx, ctx->new_group_fun)) {
dashdmx_js_declare_group(ctx, group_idx);
}
#endif
}
void dashdmx_io_manifest_updated(GF_DASHFileIO *dashio, const char *manifest_name, const char *cache_url, s32 group_idx)
{
u8 *manifest_payload;
@@ -720,6 +752,7 @@ GF_Err dashdmx_io_on_dash_event(GF_DASHFileIO *dashio, GF_DASHEventType dash_evt
if (gf_dash_group_has_dependent_group(ctx->dash, i) >=0 ) {
gf_dash_group_select(ctx->dash, i, GF_TRUE);
dashdmx_declare_group(ctx, i);
continue;
}
@@ -741,13 +774,7 @@ GF_Err dashdmx_io_on_dash_event(GF_DASHFileIO *dashio, GF_DASHEventType dash_evt
ctx->width = w;
ctx->height = h;
}
if (ctx->on_new_group)
ctx->on_new_group(ctx->rt_udta, i, ctx->dash);
#ifdef GPAC_HAS_QJS
else if (ctx->js_ctx && JS_IsFunction(ctx->js_ctx, ctx->new_group_fun)) {
dashdmx_js_declare_group(ctx, i);
}
#endif
dashdmx_declare_group(ctx, i);
}
}
@@ -1401,12 +1428,8 @@ static GF_Err dashdmx_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool i
#ifdef GPAC_HAS_QJS
static s32 dashdmx_algo_js(void *udta, u32 group, u32 base_group,
u32 dl_rate, u32 file_size, Double speed, Double max_available_speed,
u32 disp_width, u32 disp_height,
Bool force_lower_complexity,
u32 active_quality_idx, u32 buffer_min_ms, u32 buffer_max_ms, u32 buffer_occupancy_ms
) {
static s32 dashdmx_algo_js(void *udta, u32 group, u32 base_group, Bool force_lower_complexity, GF_DASHCustomAlgoInfo *stats)
{
s32 res;
JSValue ret, args[4];
GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx *)udta;
@@ -1416,22 +1439,24 @@ static s32 dashdmx_algo_js(void *udta, u32 group, u32 base_group,
args[1] = JS_NewInt32(ctx->js_ctx, base_group);
args[2] = JS_NewBool(ctx->js_ctx, force_lower_complexity);
args[3] = JS_NewObject(ctx->js_ctx);
JS_SetPropertyStr(ctx->js_ctx, args[3], "rate", JS_NewInt32(ctx->js_ctx, dl_rate) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "filesize", JS_NewInt32(ctx->js_ctx, file_size) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "speed", JS_NewFloat64(ctx->js_ctx, speed) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "max_speed", JS_NewFloat64(ctx->js_ctx, max_available_speed) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "display_width", JS_NewInt32(ctx->js_ctx, disp_width) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "display_height", JS_NewInt32(ctx->js_ctx, disp_height) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "active_quality", JS_NewInt32(ctx->js_ctx, active_quality_idx) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "buffer_min", JS_NewInt32(ctx->js_ctx, buffer_min_ms) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "buffer_max", JS_NewInt32(ctx->js_ctx, buffer_max_ms) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "buffer", JS_NewInt32(ctx->js_ctx, buffer_occupancy_ms) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "rate", JS_NewInt32(ctx->js_ctx, stats->download_rate) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "filesize", JS_NewInt32(ctx->js_ctx, stats->file_size) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "speed", JS_NewFloat64(ctx->js_ctx, stats->speed) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "max_speed", JS_NewFloat64(ctx->js_ctx, stats->max_available_speed) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "display_width", JS_NewInt32(ctx->js_ctx, stats->disp_width) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "display_height", JS_NewInt32(ctx->js_ctx, stats->disp_height) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "active_quality", JS_NewInt32(ctx->js_ctx, stats->active_quality_idx) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "buffer_min", JS_NewInt32(ctx->js_ctx, stats->buffer_min_ms) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "buffer_max", JS_NewInt32(ctx->js_ctx, stats->buffer_max_ms) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "buffer", JS_NewInt32(ctx->js_ctx, stats->buffer_occupancy_ms) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "degradation_hint", JS_NewInt32(ctx->js_ctx, stats->quality_degradation_hint) );
JS_SetPropertyStr(ctx->js_ctx, args[3], "total_rate", JS_NewInt32(ctx->js_ctx, stats->total_rate) );
ret = JS_Call(ctx->js_ctx, ctx->rate_fun, ctx->js_obj, 4, args);
JS_FreeValue(ctx->js_ctx, args[3]);
if (JS_IsException(ret)) {
js_dump_error(ctx->js_ctx);
res = -2;
res = -3;
} else if (JS_ToInt32(ctx->js_ctx, &res, ret))
res = -1;
@@ -1731,6 +1756,7 @@ static GF_Err dashdmx_initialize(GF_Filter *filter)
gf_dash_set_low_latency_mode(ctx->dash, ctx->lowlat);
if (ctx->split_as)
gf_dash_split_adaptation_sets(ctx->dash);
gf_dash_disable_low_quality_tiles(ctx->dash, ctx->skip_lqt);
//in test mode, we disable seeking inside the segment: this initial seek range is dependent from tune-in time and would lead to different start range
@@ -2032,6 +2058,7 @@ static void dashdmx_update_group_stats(GF_DASHDmxCtx *ctx, GF_DASHGroup *group)
{
u32 bytes_per_sec = 0;
u64 file_size = 0;
u32 dep_rep_idx;
const GF_PropertyValue *p;
GF_PropertyEntry *pe=NULL;
Bool broadcast_flag = GF_FALSE;
@@ -2080,8 +2107,12 @@ static void dashdmx_update_group_stats(GF_DASHDmxCtx *ctx, GF_DASHGroup *group)
if (p && p->value.string && !strcmp(p->value.string, "yes")) {
broadcast_flag = GF_TRUE;
}
if (group->nb_group_deps)
dep_rep_idx = group->current_group_dep ? (group->current_group_dep-1) : group->nb_group_deps;
else
dep_rep_idx = group->current_dependent_rep_idx;
gf_dash_group_store_stats(ctx->dash, group->idx, group->current_dependent_rep_idx, bytes_per_sec, file_size, broadcast_flag, gf_sys_clock_high_res() - group->us_at_seg_start);
gf_dash_group_store_stats(ctx->dash, group->idx, dep_rep_idx, bytes_per_sec, file_size, broadcast_flag, gf_sys_clock_high_res() - group->us_at_seg_start);
p = gf_filter_get_info(group->seg_filter_src, GF_PROP_PID_FILE_CACHED, &pe);
if (p && p->value.boolean)
@@ -2093,23 +2124,40 @@ static void dashdmx_update_group_stats(GF_DASHDmxCtx *ctx, GF_DASHGroup *group)
static void dashdmx_switch_segment(GF_DASHDmxCtx *ctx, GF_DASHGroup *group)
{
u32 dependent_representation_index = 0;
u32 dependent_representation_index;
GF_Err e;
Bool has_scalable_next;
Bool seg_disabled;
GF_FilterEvent evt;
const char *next_url, *next_url_init_or_switch_segment, *src_url, *key_url;
u64 start_range, end_range, switch_start_range, switch_end_range;
bin128 key_IV;
u32 group_idx;
fetch_next:
assert(group->nb_eos || group->seg_was_not_ready || group->in_error);
group->wait_for_pck = GF_TRUE;
group->in_error = GF_FALSE;
if (group->segment_sent) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] group %d drop current segment\n", group->idx));
if (!group->current_group_dep && !group->next_dependent_rep_idx)
if (!group->current_group_dep && !group->next_dependent_rep_idx) {
gf_dash_group_discard_segment(ctx->dash, group->idx);
//special case for group dependencies (tiling): signal segment switch
//so that tileagg may detect losses
if (group->nb_group_deps) {
GF_FilterPid *opid = dashdmx_opid_from_group(ctx, group);
if (opid) {
GF_FilterEvent evt;
GF_FEVT_INIT(evt, GF_FEVT_PLAY_HINT, opid);
evt.play.forced_dash_segment_switch = GF_TRUE;
gf_filter_pid_send_event(opid, &evt);
}
}
if (group->seg_discard_state==1)
group->seg_discard_state = 2;
}
group->segment_sent = GF_FALSE;
//no thread mode, we work with at most one entry in cache, call process right away to get the group next URL ready
gf_dash_process(ctx->dash);
@@ -2123,12 +2171,31 @@ static void dashdmx_switch_segment(GF_DASHDmxCtx *ctx, GF_DASHGroup *group)
}
#endif
//special case if we had a discard of a dependent seg: we wait for the output PIDs to be flushed
//so that we don't send a GF_FEVT_PLAY_HINT event before the previous segment is completely produced
//this is mostly for tileagg for the time being and could need further refinements
if (group->seg_discard_state == 2) {
u32 i;
for (i=0; i < gf_filter_get_opid_count(ctx->filter); i++) {
GF_FilterPid *opid = gf_filter_get_opid(ctx->filter, i );
GF_DASHGroup *g = gf_filter_pid_get_udta( opid );
if (g != group) continue;
if (gf_filter_pid_would_block(opid)) {
group->seg_was_not_ready = GF_TRUE;
group->stats_uploaded = GF_TRUE;
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] some pids blocked on group with discard, waiting before fetching next segment\n"));
return;
}
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] all pids unblocked on group with discard, fetching next segment\n"));
group->seg_discard_state = 0;
}
dependent_representation_index = 0;
group_idx = group->idx;
if (group->nb_group_deps) {
if (group->current_group_dep) {
dependent_representation_index = group->current_group_dep;
// s32 res = gf_dash_get_dependent_group_index(ctx->dash, group->idx, group->current_group_dep-
}
dependent_representation_index = group->current_group_dep;
} else if (group->next_dependent_rep_idx) {
dashdmx_update_group_stats(ctx, group);
dependent_representation_index = group->current_dependent_rep_idx = group->next_dependent_rep_idx;
@@ -2147,7 +2214,12 @@ static void dashdmx_switch_segment(GF_DASHDmxCtx *ctx, GF_DASHGroup *group)
group->eos_detected = GF_TRUE;
return;
}
seg_disabled = GF_FALSE;
group->eos_detected = GF_FALSE;
if (e == GF_URL_REMOVED) {
seg_disabled = GF_TRUE;
e = GF_OK;
}
if (e != GF_OK) {
if (e == GF_BUFFER_TOO_SMALL) {
@@ -2196,17 +2268,31 @@ static void dashdmx_switch_segment(GF_DASHDmxCtx *ctx, GF_DASHGroup *group)
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASHDmx] group %d queuing next media segment %s\n", group->idx, next_url));
group->segment_sent = GF_TRUE;
group->prev_is_init_segment = GF_FALSE;
group->init_switch_seg_sent = GF_FALSE;
group->signal_seg_name = ctx->filemode;
group->us_at_seg_start = gf_sys_clock_high_res();
if (seg_disabled) {
u32 dep_rep_idx;
if (group->nb_group_deps)
dep_rep_idx = group->current_group_dep ? (group->current_group_dep-1) : group->nb_group_deps;
else
dep_rep_idx = group->current_dependent_rep_idx;
gf_dash_group_store_stats(ctx->dash, group->idx, dep_rep_idx, 0, 0, 0, 0);
if (!group->seg_discard_state)
group->seg_discard_state = 1;
goto fetch_next;
}
GF_FEVT_INIT(evt, GF_FEVT_SOURCE_SWITCH, NULL);
evt.seek.source_switch = next_url;
evt.seek.start_offset = start_range;
evt.seek.end_offset = end_range;
evt.seek.previous_is_init_segment = group->prev_is_init_segment;
group->segment_sent = GF_TRUE;
group->prev_is_init_segment = GF_FALSE;
group->init_switch_seg_sent = GF_FALSE;
group->signal_seg_name = ctx->filemode;
gf_filter_send_event(group->seg_filter_src, &evt, GF_FALSE);
group->us_at_seg_start = gf_sys_clock_high_res();
}
GF_Err dashin_abort(GF_DASHDmxCtx *ctx)
@@ -2548,6 +2634,8 @@ static const GF_FilterArgs DASHDmxArgs[] =
"- early: allow fetching segments earlier than their AST in low latency when input demux is empty", GF_PROP_UINT, "early", "no|strict|early", GF_FS_ARG_HINT_EXPERT},
{ OFFS(filemode), "do not demux files and forward them as file pids (imply `segstore=mem`)", GF_PROP_BOOL, "no", NULL, GF_FS_ARG_HINT_ADVANCED},
{ OFFS(fmodefwd), "forward packet rather than copy them in [-filemode](). Packet copy might improve performances in low latency mode", GF_PROP_BOOL, "yes", NULL, GF_FS_ARG_HINT_EXPERT},
{ OFFS(skip_lqt), "disable decoding of tiles with highest degradation hints (not visible, not gazed at) for debug purposes", GF_PROP_BOOL, "no", NULL, GF_FS_ARG_HINT_EXPERT},
{0}
};
@@ -2601,40 +2689,10 @@ const GF_FilterRegister *dashdmx_register(GF_FilterSession *session)
#endif
}
typedef struct
{
u32 download_rate;
u32 filesize;
Double speed;
Double max_available_speed;
u32 display_width;
u32 display_height;
u32 active_quality_idx;
u32 buffer_min_ms;
u32 buffer_max_ms;
u32 buffer_occupancy_ms;
} GF_DASHAlgoStats;
static s32 dashdmx_rate_adaptation_ext(void *udta, u32 group_idx, u32 base_group_idx,
u32 download_rate, u32 filesize, Double speed, Double max_available_speed,
u32 display_width, u32 display_height, Bool force_lower_complexity,
u32 active_quality_idx, u32 buffer_min_ms, u32 buffer_max_ms, u32 buffer_occupancy_ms
)
static s32 dashdmx_rate_adaptation_ext(void *udta, u32 group_idx, u32 base_group_idx, Bool force_lower_complexity, GF_DASHCustomAlgoInfo *stats)
{
GF_DASHDmxCtx *ctx = (GF_DASHDmxCtx*) udta;
GF_DASHAlgoStats stats;
stats.download_rate = download_rate;
stats.filesize = filesize;
stats.speed = speed;
stats.max_available_speed = max_available_speed;
stats.display_width = display_width;
stats.display_height = display_height;
stats.active_quality_idx = active_quality_idx;
stats.buffer_min_ms = buffer_min_ms;
stats.buffer_max_ms = buffer_max_ms;
stats.buffer_occupancy_ms = buffer_occupancy_ms;
return ctx->on_rate_adaptation(ctx->rt_udta, group_idx, base_group_idx, force_lower_complexity, &stats);
return ctx->on_rate_adaptation(ctx->rt_udta, group_idx, base_group_idx, force_lower_complexity, stats);
}

View File

@@ -42,6 +42,9 @@ typedef struct
u32 base_id;
GF_BitStream *bs_r;
u32 flush_packets;
const GF_PropertyValue *sabt;
} GF_TileAggCtx;
@@ -112,6 +115,8 @@ static GF_Err tileagg_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool i
if (!p)
return GF_NOT_SUPPORTED;
ctx->base_id = p->value.uint;
ctx->sabt = gf_filter_pid_get_property_str(pid, "isom:sabt");
} else {
p = gf_filter_pid_get_property(pid, GF_PROP_PID_DEPENDENCY_ID);
if (!p) return GF_NOT_SUPPORTED;
@@ -129,11 +134,7 @@ static GF_Err tileagg_set_eos(GF_Filter *filter, GF_TileAggCtx *ctx)
u32 i, count = gf_filter_get_ipid_count(filter);
for (i=0; i<count; i++) {
GF_FilterPid *pid = gf_filter_get_ipid(filter, i);
while (1) {
GF_FilterPacket *pck = gf_filter_pid_get_packet(pid);
if (!pck) break;
gf_filter_pid_drop_packet(pid);
}
gf_filter_pid_set_discard(pid, GF_TRUE);
}
gf_filter_pid_set_eos(ctx->opid);
@@ -147,7 +148,8 @@ static GF_Err tileagg_process(GF_Filter *filter)
GF_FilterPacket *dst_pck, *base_pck;
u64 min_cts = GF_FILTER_NO_TS;
u32 pck_size, size = 0;
u32 pos;
u32 pos, nb_ready=0;
u32 sabt_idx;
Bool has_sei_suffix = GF_FALSE;
const char *data;
u8 *output;
@@ -155,6 +157,7 @@ static GF_Err tileagg_process(GF_Filter *filter)
base_pck = gf_filter_pid_get_packet(ctx->base_ipid);
if (!base_pck) {
ctx->flush_packets = 0;
if (gf_filter_pid_is_eos(ctx->base_ipid)) {
return tileagg_set_eos(filter, ctx);
}
@@ -176,18 +179,24 @@ static GF_Err tileagg_process(GF_Filter *filter)
if (gf_filter_pid_is_eos(pid)) {
return tileagg_set_eos(filter, ctx);
}
return GF_OK;
//if we are flushing a segment, consider the PID discarded if no packet
//otherwise wait for packet
if (! ctx->flush_packets)
return GF_OK;
break;
}
cts = gf_filter_pck_get_cts(pck);
if (cts < min_cts) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[TileAggr] Tiled pid %s with cts "LLU" less than base tile pid cts "LLU" - discarding packet\n", gf_filter_pid_get_name(pid), cts, min_cts ));
GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[TileAggr] Tiled pid %s with cts "LLU" less than base tile pid cts "LLU" - discarding packet\n", gf_filter_pid_get_name(pid), cts, min_cts ));
gf_filter_pid_drop_packet(pid);
} else {
break;
}
}
assert(pck);
if (!pck) continue;
if (cts > min_cts) continue;
for (j=0; j<ctx->tiledrop.nb_items; j++) {
@@ -201,7 +210,13 @@ static GF_Err tileagg_process(GF_Filter *filter)
gf_filter_pck_get_data(pck, &pck_size);
size += pck_size;
nb_ready++;
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[TileAggr] reaggregating CTS "LLU" %d ready %d pids (nb flush pck %d)\n", min_cts, nb_ready+1, count, ctx->flush_packets));
if (ctx->flush_packets)
ctx->flush_packets--;
dst_pck = gf_filter_pck_new_alloc(ctx->opid, size, &output);
gf_filter_pck_merge_properties(base_pck, dst_pck);
@@ -227,23 +242,42 @@ static GF_Err tileagg_process(GF_Filter *filter)
pos += nal_size + ctx->nalu_size_length;
}
for (i=0; i<count; i++) {
u64 cts;
GF_FilterPacket *pck;
GF_FilterPid *pid = gf_filter_get_ipid(filter, i);
if (pid==ctx->base_ipid) continue;
pck = gf_filter_pid_get_packet(pid);
//can happen if we drop one tile
if (!pck) continue;
sabt_idx = 0;
while (1) {
u32 pid_id = 0;
if (ctx->sabt) {
if (sabt_idx >= ctx->sabt->value.uint_list.nb_items)
break;
pid_id = ctx->sabt->value.uint_list.vals[sabt_idx];
sabt_idx++;
}
for (i=0; i<count; i++) {
u64 cts;
GF_FilterPacket *pck;
GF_FilterPid *pid = gf_filter_get_ipid(filter, i);
if (pid==ctx->base_ipid) continue;
if (pid_id) {
const GF_PropertyValue *p = gf_filter_pid_get_property(pid, GF_PROP_PID_ID);
if (!p || (p->value.uint != pid_id))
continue;
}
pck = gf_filter_pid_get_packet(pid);
//can happen if we drop one tile
if (!pck) continue;
cts = gf_filter_pck_get_cts(pck);
if (cts != min_cts) continue;
cts = gf_filter_pck_get_cts(pck);
if (cts != min_cts) continue;
data = gf_filter_pck_get_data(pck, &pck_size);
memcpy(output+size, data, pck_size);
size += pck_size;
data = gf_filter_pck_get_data(pck, &pck_size);
memcpy(output+size, data, pck_size);
size += pck_size;
gf_filter_pid_drop_packet(pid);
gf_filter_pid_drop_packet(pid);
if (pid_id)
break;
}
if (!pid_id)
break;
}
//append all SEI suffixes
@@ -287,6 +321,19 @@ static void tileagg_finalize(GF_Filter *filter)
gf_bs_del(ctx->bs_r);
}
static Bool tileagg_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
{
GF_TileAggCtx *ctx = (GF_TileAggCtx *) gf_filter_get_udta(filter);
if (evt->base.type != GF_FEVT_PLAY_HINT) return GF_FALSE;
if (evt->play.forced_dash_segment_switch) {
//this assumes the dashin module performs regulation of output in case of losses
//otherwise it may dispatch more than one segment in the input buffer
assert(!ctx->flush_packets);
gf_filter_pid_get_buffer_occupancy(ctx->base_ipid, NULL, &ctx->flush_packets, NULL, NULL);
}
return GF_TRUE;
}
static const GF_FilterCapability TileAggCaps[] =
{
CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
@@ -321,6 +368,7 @@ GF_FilterRegister TileAggRegister = {
.args = TileAggArgs,
.configure_pid = tileagg_configure_pid,
.process = tileagg_process,
.process_event = tileagg_process_event,
.max_extra_pids = (u32) (-1),
};

View File

@@ -160,6 +160,7 @@ struct __dash_client
Bool period_groups_setup;
u32 tile_rate_decrease;
GF_DASHTileAdaptationMode tile_adapt_mode;
Bool disable_low_quality_tiles;
GF_List *SRDs;
@@ -171,7 +172,8 @@ struct __dash_client
s32 (*rate_adaptation_download_monitor)(GF_DashClient *dash, GF_DASH_Group *group, u32 bits_per_sec, u64 total_bytes, u64 bytes_done, u64 us_since_start, u32 buffer_dur_ms, u32 current_seg_dur);
//for custom algo, total rate of all active groups being downloaded
u32 total_rate;
gf_dash_rate_adaptation rate_adaptation_algo_custom;
gf_dash_download_monitor rate_adaptation_download_monitor_custom;
@@ -181,6 +183,13 @@ struct __dash_client
static void gf_dash_seek_group(GF_DashClient *dash, GF_DASH_Group *group, Double seek_to, Bool is_dynamic);
enum
{
SEG_FLAG_LOOP_DETECTED = 1,
SEG_FLAG_DEP_FOLLOWING = 1<<1,
SEG_FLAG_DISABLED = 1<<2,
};
typedef struct
{
char *cache;
@@ -188,14 +197,14 @@ typedef struct
u64 start_range, end_range;
/*representation index in adaptation_set->representations*/
u32 representation_index;
Bool loop_detected;
u32 duration;
char *key_url;
bin128 key_IV;
Bool has_dep_following;
u32 seg_number;
const char *seg_name_start;
GF_Fraction64 time;
u32 flags;
} segment_cache_entry;
typedef enum
@@ -341,6 +350,10 @@ struct __dash_group
u32 quality_degradation_hint;
Bool rate_adaptation_postponed;
Bool update_tile_qualities;
//for dash custom, allows temporary disabling a group
Bool disabled;
/* current segment index in BBA and BOLA algorithm */
u32 current_index;
@@ -3639,7 +3652,9 @@ static void dash_do_rate_adaptation(GF_DashClient *dash, GF_DASH_Group *group)
All adaptation algorithms should use this value */
speed = dash->speed;
if (speed<0) speed = -speed;
dl_rate = (u32) (8*group->bytes_per_sec / speed);
dl_rate = (u32) (8 * (u64) group->bytes_per_sec / speed);
if ((s32) dl_rate < 0)
dl_rate = GF_INT_MAX;
/* Get the active representation in the AdaptationSet */
rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
@@ -3719,13 +3734,16 @@ static void dash_do_rate_adaptation(GF_DashClient *dash, GF_DASH_Group *group)
return;
}
group->rate_adaptation_postponed = GF_FALSE;
if (new_index < 0) {
if (new_index == -2) {
group->disabled = GF_TRUE;
}
group->active_rep_index = old_index;
return;
}
if (new_index != group->active_rep_index) {
GF_MPD_Representation *new_rep = gf_list_get(group->adaptation_set->representations, (u32)new_index);
group->disabled = GF_FALSE;
if (!new_rep) {
group->active_rep_index = old_index;
GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Cannot find new representation index %d, using previous one\n", new_index));
@@ -4979,8 +4997,11 @@ static u32 gf_dash_get_tiles_quality_rank(GF_DashClient *dash, GF_DASH_Group *ti
if (tile_group->quality_degradation_hint) {
u32 v = tile_group->quality_degradation_hint * MAX(srd->srd_nb_rows, srd->srd_nb_cols);
v/=100;
if (dash->disable_low_quality_tiles)
tile_group->disabled = GF_TRUE;
return v;
}
tile_group->disabled = GF_FALSE;
switch (dash->tile_adapt_mode) {
@@ -5677,7 +5698,7 @@ static DownloadGroupStatus on_group_download_error(GF_DashClient *dash, GF_DASH_
if (!will_retry) {
if (rep->dependency_id) {
segment_cache_entry *cache_entry = &base_group->cached[base_group->nb_cached_segments];
cache_entry->has_dep_following = 0;
cache_entry->flags &= ~SEG_FLAG_DEP_FOLLOWING;
}
if (group->base_rep_index_plus_one) {
@@ -5945,8 +5966,10 @@ static DownloadGroupStatus dash_download_group_download(GF_DashClient *dash, GF_
}
cache_entry->representation_index = representation_index;
cache_entry->duration = (u32) group->current_downloaded_segment_duration;
cache_entry->loop_detected = group->loop_detected;
cache_entry->has_dep_following = has_dep_following;
cache_entry->flags = group->loop_detected ? SEG_FLAG_LOOP_DETECTED : 0;
if (has_dep_following) cache_entry->flags |= SEG_FLAG_DEP_FOLLOWING;
if (group->disabled)
cache_entry->flags |= SEG_FLAG_DISABLED;
if (key_url) {
cache_entry->key_url = key_url;
memcpy(cache_entry->key_IV, key_iv, sizeof(bin128));
@@ -6049,6 +6072,7 @@ static void dash_global_rate_adaptation(GF_DashClient *dash, Bool for_postponed_
u32 min_bandwidth = 0;
Bool force_rep_idx = GF_FALSE;
Bool local_file_mode = GF_FALSE;
Bool use_custom_algo = GF_FALSE;
GF_MPD_Representation *rep, *rep_new;
u32 total_rate, max_fsize, bandwidths[20], groups_per_quality[20], max_level;
u32 q_idx, nb_qualities = 0;
@@ -6075,12 +6099,22 @@ static void dash_global_rate_adaptation(GF_DashClient *dash, Bool for_postponed_
if (group->selection != GF_DASH_GROUP_SELECTED) continue;
if (group->local_files) local_files ++;
if (!group->bytes_per_sec) {
if (!for_postponed_only)
if (!for_postponed_only && !group->disabled)
return;
continue;
}
if (group->done) continue;
//change of tile qualities
if (group->update_tile_qualities) {
group->update_tile_qualities = GF_FALSE;
if (!dash->rate_adaptation_algo_custom) {
if (group->srd_desc)
gf_dash_set_tiles_quality(dash, group->srd_desc);
}
}
group->backup_Bps = group->bytes_per_sec;
//only count broadband ones
if (dash->route_clock_state && !gf_list_count(group->period->base_URLs) && !gf_list_count(group->adaptation_set->base_URLs) && !group->period->origin_base_url) {
@@ -6104,6 +6138,7 @@ static void dash_global_rate_adaptation(GF_DashClient *dash, Bool for_postponed_
}
}
}
if (total_rate == (u32) -1) {
total_rate = 0;
}
@@ -6114,7 +6149,13 @@ static void dash_global_rate_adaptation(GF_DashClient *dash, Bool for_postponed_
return;
}
for (q_idx=0; q_idx<nb_qualities; q_idx++) {
if (dash->rate_adaptation_algo_custom) {
use_custom_algo = GF_TRUE;
dash->total_rate = total_rate;
goto custom_algo;
}
for (q_idx=0; q_idx<nb_qualities; q_idx++) {
bandwidths[q_idx] = 0;
groups_per_quality[q_idx] = 0;
@@ -6258,6 +6299,8 @@ static void dash_global_rate_adaptation(GF_DashClient *dash, Bool for_postponed_
}
}
custom_algo:
//GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("DEBUG. 2. dowload at %d \n", 8*bandwidths[q_idx]/1000));
//bandwitdh sharing done, perform rate adaptation with theses new numbers
for (i=0; i<count; i++) {
@@ -6265,17 +6308,20 @@ static void dash_global_rate_adaptation(GF_DashClient *dash, Bool for_postponed_
if (group->selection != GF_DASH_GROUP_SELECTED) continue;
if (group->done) continue;
if (force_rep_idx) {
rep = gf_list_get(group->adaptation_set->representations, group->target_new_rep);
//add 100 bytes/sec to make sure we select the target one
group->bytes_per_sec = 100 + rep->bandwidth / 8;
}
//decrease by quality level
else if (dash->tile_rate_decrease) {
quality_rank = gf_dash_get_tiles_quality_rank(dash, group);
if (quality_rank >= nb_qualities) quality_rank = nb_qualities-1;
assert(groups_per_quality[quality_rank]);
group->bytes_per_sec = bandwidths[quality_rank] / groups_per_quality[quality_rank];
//in custom algo case, we don't change the bitrate of the group
if (!use_custom_algo) {
if (force_rep_idx) {
rep = gf_list_get(group->adaptation_set->representations, group->target_new_rep);
//add 100 bytes/sec to make sure we select the target one
group->bytes_per_sec = 100 + rep->bandwidth / 8;
}
//decrease by quality level
else if (dash->tile_rate_decrease) {
quality_rank = gf_dash_get_tiles_quality_rank(dash, group);
if (quality_rank >= nb_qualities) quality_rank = nb_qualities-1;
assert(groups_per_quality[quality_rank]);
group->bytes_per_sec = bandwidths[quality_rank] / groups_per_quality[quality_rank];
}
}
if (for_postponed_only) {
@@ -6284,6 +6330,16 @@ static void dash_global_rate_adaptation(GF_DashClient *dash, Bool for_postponed_
group->bytes_per_sec = group->backup_Bps;
} else {
dash_do_rate_adaptation(dash, group);
//reset/restore bytes_per_sec once all groups have been called
}
}
if (!for_postponed_only) {
for (i=0; i<count; i++) {
GF_DASH_Group *group = gf_list_get(dash->groups, i);
if (group->selection != GF_DASH_GROUP_SELECTED) continue;
if (group->done) continue;
if (!group->rate_adaptation_postponed)
group->bytes_per_sec = 0;
else
@@ -7135,16 +7191,25 @@ static s32 dash_do_rate_adaptation_custom(GF_DashClient *dash, GF_DASH_Group *gr
u32 dl_rate, Double speed, Double max_available_speed, Bool force_lower_complexity,
GF_MPD_Representation *rep, Bool go_up_bitrate)
{
GF_DASHCustomAlgoInfo stats;
u32 g_idx = gf_list_find(dash->groups, group);
u32 b_idx = gf_list_find(dash->groups, base_group);
return dash->rate_adaptation_algo_custom(dash->udta_custom_algo, g_idx, b_idx, dl_rate, group->total_size,
speed, max_available_speed,
group->hint_visible_width, group->hint_visible_height,
force_lower_complexity,
group->active_rep_index,
group->buffer_min_ms, group->buffer_max_ms, group->buffer_occupancy_ms
);
stats.download_rate = dl_rate;
stats.file_size = group->total_size;
stats.speed = speed;
stats.max_available_speed = max_available_speed;
stats.disp_width = group->hint_visible_width;
stats.disp_height = group->hint_visible_height;
stats.active_quality_idx = group->active_rep_index;
stats.buffer_min_ms = group->buffer_min_ms;
stats.buffer_max_ms = group->buffer_max_ms;
stats.buffer_occupancy_ms = group->buffer_occupancy_ms;
stats.quality_degradation_hint = group->quality_degradation_hint;
stats.total_rate = dash->total_rate;
return dash->rate_adaptation_algo_custom(dash->udta_custom_algo, g_idx, b_idx, force_lower_complexity, &stats);
}
static s32 dash_do_rate_monitor_custom(GF_DashClient *dash, GF_DASH_Group *group, u32 bits_per_sec, u64 total_bytes, u64 bytes_done, u64 us_since_start, u32 buffer_dur_ms, u32 current_seg_dur)
@@ -7778,7 +7843,7 @@ discard_segment:
if (!group->nb_cached_segments) {
return;
}
delete_next = group->cached[0].has_dep_following ? GF_TRUE : GF_FALSE;
delete_next = (group->cached[0].flags & SEG_FLAG_DEP_FOLLOWING) ? GF_TRUE : GF_FALSE;
if (group->cached[0].cache) {
assert(group->cached[0].url);
@@ -7870,7 +7935,7 @@ GF_Err gf_dash_group_get_next_segment_location(GF_DashClient *dash, u32 idx, u32
}
/*check the dependent rep is in the cache and does not target next segment (next in time)*/
has_dep_following = group->cached[0].has_dep_following;
has_dep_following = (group->cached[0].flags & SEG_FLAG_DEP_FOLLOWING) ? GF_TRUE : GF_FALSE;
index = 0;
while (dependent_representation_index) {
GF_Err err = GF_OK;
@@ -7878,7 +7943,7 @@ GF_Err gf_dash_group_get_next_segment_location(GF_DashClient *dash, u32 idx, u32
if (has_dep_following) {
if (index+1 >= group->nb_cached_segments)
err = GF_BUFFER_TOO_SMALL;
else if (! group->cached[index].has_dep_following)
else if (! (group->cached[index].flags & SEG_FLAG_DEP_FOLLOWING) )
err = GF_BAD_PARAM;
} else {
GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, group->cached[index].representation_index);
@@ -7918,7 +7983,7 @@ GF_Err gf_dash_group_get_next_segment_location(GF_DashClient *dash, u32 idx, u32
group->force_segment_switch = 0;
if (has_next_segment) {
if (group->cached[index].has_dep_following) {
if (group->cached[index].flags & SEG_FLAG_DEP_FOLLOWING) {
*has_next_segment = GF_TRUE;
} else if ((index+1<group->max_cached_segments) && group->cached[index+1].cache && group->adaptation_set) {
GF_MPD_Representation *rep;
@@ -7931,6 +7996,8 @@ GF_Err gf_dash_group_get_next_segment_location(GF_DashClient *dash, u32 idx, u32
*has_next_segment = GF_TRUE;
}
}
if (group->cached[index].flags & SEG_FLAG_DISABLED)
return GF_URL_REMOVED;
return GF_OK;
}
@@ -8025,7 +8092,9 @@ GF_EXPORT
Bool gf_dash_group_loop_detected(GF_DashClient *dash, u32 idx)
{
GF_DASH_Group *group = gf_list_get(dash->groups, idx);
return (group && group->nb_cached_segments) ? group->cached[0].loop_detected : GF_FALSE;
if (!group || !group->nb_cached_segments)
return GF_FALSE;
return (group->cached[0].flags & SEG_FLAG_LOOP_DETECTED) ? GF_TRUE : GF_FALSE;
}
GF_EXPORT
@@ -8679,6 +8748,12 @@ void gf_dash_set_tile_adaptation_mode(GF_DashClient *dash, GF_DASHTileAdaptation
}
}
GF_EXPORT
void gf_dash_disable_low_quality_tiles(GF_DashClient *dash, Bool disable_tiles)
{
dash->disable_low_quality_tiles = disable_tiles;
}
GF_EXPORT
Bool gf_dash_group_get_srd_info(GF_DashClient *dash, u32 idx, u32 *srd_id, u32 *srd_x, u32 *srd_y, u32 *srd_w, u32 *srd_h, u32 *srd_width, u32 *srd_height)
{
@@ -8744,11 +8819,10 @@ GF_Err gf_dash_group_set_visible_rect(GF_DashClient *dash, u32 idx, u32 min_x, u
group->quality_degradation_hint = 0;
}
//for both regular or tiled, store visible width/height
group->hint_visible_width = max_x - min_x;
group->hint_visible_height = max_y - min_y;
//TODO - single video, we may want to switch down quality if not a lot of the video is visible
//we will need the zoom factor as well
if (!group->groups_depending_on) return GF_OK;
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Group Visible rect %d,%d,%d,%d \n", min_x, max_x, min_y, max_y));
@@ -8756,8 +8830,10 @@ GF_Err gf_dash_group_set_visible_rect(GF_DashClient *dash, u32 idx, u32 min_x, u
for (i=0; i<count; i++) {
Bool is_visible = GF_TRUE;
GF_DASH_Group *a_group = gf_list_get(group->groups_depending_on, i);
u32 old_hint;
if (!a_group->srd_w || !a_group->srd_h) continue;
old_hint = a_group->quality_degradation_hint;
if (is_gaze) {
if (min_x < a_group->srd_x)
@@ -8783,10 +8859,13 @@ GF_Err gf_dash_group_set_visible_rect(GF_DashClient *dash, u32 idx, u32 min_x, u
else if (a_group->srd_y+a_group->srd_h < min_y) is_visible = GF_FALSE;
}
a_group->quality_degradation_hint = is_visible ? 0 : 100;
if (old_hint != a_group->quality_degradation_hint) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Group SRD %d,%d,%d,%d is %s\n", a_group->srd_x, a_group->srd_w, a_group->srd_y, a_group->srd_h, is_visible ? "visible" : "hidden"));
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Group SRD %d,%d,%d,%d is %s\n", a_group->srd_x, a_group->srd_w, a_group->srd_y, a_group->srd_h, is_visible ? "visible" : "hidden"));
//remember to update tile quality for non-custom algo
group->update_tile_qualities = GF_TRUE;
}
}
return GF_OK;
}
@@ -8820,7 +8899,7 @@ void gf_dash_set_group_download_state(GF_DashClient *dash, u32 idx, u32 cur_dep_
if (!group->nb_cached_segments)
break;
}
has_dep_following = group->cached[0].has_dep_following;
has_dep_following = (group->cached[0].flags & SEG_FLAG_DEP_FOLLOWING) ? GF_TRUE : GF_FALSE;
key_url = group->cached[0].key_url;
url = group->cached[0].url;
gf_free(group->cached[0].cache);
@@ -8847,9 +8926,22 @@ void gf_dash_group_store_stats(GF_DashClient *dash, u32 idx, u32 dep_rep_idx, u3
if (!group) return;
if (!group->nb_cached_segments) return;
dash_store_stats(dash, group, bytes_per_sec, (u32) file_size, is_broadcast, 1+dep_rep_idx, us_since_start);
if (group->groups_depending_on) {
Bool is_last = (dep_rep_idx == gf_list_count(group->groups_depending_on)) ? GF_TRUE : GF_FALSE;
if (dep_rep_idx) {
group = gf_list_get(group->groups_depending_on, dep_rep_idx-1);
if (!group)
return;
}
dash_store_stats(dash, group, bytes_per_sec, (u32) file_size, is_broadcast, 1+dep_rep_idx, us_since_start);
dash_global_rate_adaptation(dash, GF_FALSE);
if (is_last)
dash_global_rate_adaptation(dash, GF_FALSE);
} else {
dash_store_stats(dash, group, bytes_per_sec, (u32) file_size, is_broadcast, 1+dep_rep_idx, us_since_start);
dash_global_rate_adaptation(dash, GF_FALSE);
}
}
u32 gf_dash_get_min_wait_ms(GF_DashClient *dash)