Browse Source

MT#60688 Add support of maxptime for media

Same as ptime but for the maxptime.
Also add replication of it.
This is required later to be used for the sdp_create() handling.

Additionally: fix tests, because maxptime now takes another
place within the media session, which doesn't affect functionality.

Change-Id: I058e35323849679976c60b2e9fb2555fd0168e67
pull/1848/head
Donat Zenichev 1 year ago
parent
commit
21677a39e6
5 changed files with 42 additions and 20 deletions
  1. +3
    -0
      daemon/call.c
  2. +4
    -0
      daemon/redis.c
  3. +12
    -0
      daemon/sdp.c
  4. +2
    -1
      include/call.h
  5. +21
    -19
      t/auto-daemon-tests.pl

+ 3
- 0
daemon/call.c View File

@ -2842,6 +2842,9 @@ static void __media_init_from_flags(struct call_media *other_media, struct call_
if (!MEDIA_ISSET(other_media, PTIME_OVERRIDE)) if (!MEDIA_ISSET(other_media, PTIME_OVERRIDE))
other_media->ptime = sp->ptime; other_media->ptime = sp->ptime;
} }
if (sp->maxptime > 0) {
media->maxptime = sp->maxptime;
}
if (media && flags->ptime > 0) { if (media && flags->ptime > 0) {
media->ptime = flags->ptime; media->ptime = flags->ptime;
MEDIA_SET(media, PTIME_OVERRIDE); MEDIA_SET(media, PTIME_OVERRIDE);


+ 4
- 0
daemon/redis.c View File

@ -1619,6 +1619,9 @@ static int json_medias(call_t *c, struct redis_list *medias, struct redis_list *
if (!redis_hash_get_str(&s, rh, "media_id")) if (!redis_hash_get_str(&s, rh, "media_id"))
call_str_cpy(c, &med->media_id, &s); call_str_cpy(c, &med->media_id, &s);
if (redis_hash_get_int(&med->maxptime, rh, "maxptime"))
return -1;
if (redis_hash_get_str(&s, rh, "protocol")) if (redis_hash_get_str(&s, rh, "protocol"))
return -1; return -1;
med->protocol = transport_protocol(&s); med->protocol = transport_protocol(&s);
@ -2686,6 +2689,7 @@ char* redis_encode_json(call_t *c) {
JSON_SET_SIMPLE_CSTR("desired_family", media->desired_family ? media->desired_family->rfc_name : ""); JSON_SET_SIMPLE_CSTR("desired_family", media->desired_family ? media->desired_family->rfc_name : "");
JSON_SET_SIMPLE_STR("logical_intf", &media->logical_intf->name); JSON_SET_SIMPLE_STR("logical_intf", &media->logical_intf->name);
JSON_SET_SIMPLE("ptime","%i", media->ptime); JSON_SET_SIMPLE("ptime","%i", media->ptime);
JSON_SET_SIMPLE("maxptime","%i", media->maxptime);
JSON_SET_SIMPLE("media_flags", "%" PRIu64, atomic64_get_na(&media->media_flags)); JSON_SET_SIMPLE("media_flags", "%" PRIu64, atomic64_get_na(&media->media_flags));
if (media->bandwidth_as >= 0) if (media->bandwidth_as >= 0)


+ 12
- 0
daemon/sdp.c View File

@ -1892,6 +1892,11 @@ int sdp_streams(const sdp_sessions_q *sessions, sdp_streams_q *streams, sdp_ng_f
if (attr && attr->strs.value.s) if (attr && attr->strs.value.s)
sp->ptime = str_to_i(&attr->strs.value, 0); sp->ptime = str_to_i(&attr->strs.value, 0);
// a=maxptime
attr = attr_get_by_id(&media->attributes, ATTR_MAXPTIME);
if (attr && attr->strs.value.s)
sp->maxptime = str_to_i(&attr->strs.value, 0);
sp->format_str = media->formats; sp->format_str = media->formats;
errstr = "Invalid RTP payload types"; errstr = "Invalid RTP payload types";
if (__rtp_payload_types(sp, media)) if (__rtp_payload_types(sp, media))
@ -2567,6 +2572,10 @@ static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media *
if (media->ptime) if (media->ptime)
goto strip; goto strip;
break; break;
case ATTR_MAXPTIME:
if (media->maxptime)
goto strip;
break;
case ATTR_RTCP_FB: case ATTR_RTCP_FB:
if (attr->rtcp_fb.payload_type == -1) if (attr->rtcp_fb.payload_type == -1)
break; // leave this one alone break; // leave this one alone
@ -3188,6 +3197,9 @@ static struct packet_stream *print_sdp_media_section(GString *s, struct call_med
if (media->ptime) if (media->ptime)
append_attr_int_to_gstring(s, "ptime", media->ptime, flags, append_attr_int_to_gstring(s, "ptime", media->ptime, flags,
media->type_id); media->type_id);
if (media->maxptime)
append_attr_int_to_gstring(s, "maxptime", media->maxptime, flags,
media->type_id);
} }
if (MEDIA_ISSET(media, ICE) && media->ice_agent) { if (MEDIA_ISSET(media, ICE) && media->ice_agent) {


+ 2
- 1
include/call.h View File

@ -352,7 +352,7 @@ struct stream_params {
candidate_q ice_candidates; /* slice-alloc'd */ candidate_q ice_candidates; /* slice-alloc'd */
str ice_ufrag; str ice_ufrag;
str ice_pwd; str ice_pwd;
int ptime;
int ptime, maxptime;
str media_id; str media_id;
struct t38_options t38_options; struct t38_options t38_options;
str tls_id; str tls_id;
@ -523,6 +523,7 @@ struct call_media {
#endif #endif
int ptime; /* either from SDP or overridden */ int ptime; /* either from SDP or overridden */
int maxptime; /* from SDP */
atomic64 media_flags; atomic64 media_flags;
}; };


+ 21
- 19
t/auto-daemon-tests.pl View File

@ -386,12 +386,12 @@ t=0 0
m=audio PORT RTP/AVP 8 96 m=audio PORT RTP/AVP 8 96
b=RS:0 b=RS:0
b=RR:0 b=RR:0
a=maxptime:40
a=rtpmap:8 PCMA/8000 a=rtpmap:8 PCMA/8000
a=rtpmap:96 telephone-event/8000 a=rtpmap:96 telephone-event/8000
a=fmtp:96 0-15 a=fmtp:96 0-15
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=maxptime:40
SDP SDP
answer('t-e fmtp', { flags => [qw(codec-strip-all codec-transcode-PCMA codec-transcode-telephone-event)], answer('t-e fmtp', { flags => [qw(codec-strip-all codec-transcode-PCMA codec-transcode-telephone-event)],
@ -1690,7 +1690,6 @@ m=audio PORT RTP/AVP 96 97 8 98
b=AS:80 b=AS:80
b=RS:362 b=RS:362
b=RR:1087 b=RR:1087
a=maxptime:40
a=rtpmap:96 AMR/8000 a=rtpmap:96 AMR/8000
a=fmtp:96 mode-set=0,2,4,7;mode-change-period=2;mode-change-neighbor=1;mode-change-capability=2;max-red=0 a=fmtp:96 mode-set=0,2,4,7;mode-change-period=2;mode-change-neighbor=1;mode-change-capability=2;max-red=0
a=rtpmap:97 AMR/8000 a=rtpmap:97 AMR/8000
@ -1702,6 +1701,7 @@ a=msi:mavodi-0-15b-6c6-2-ffffffff-d3c00000-6005c95738e64-171f-ffffffffffffffff-.
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:40
SDP SDP
answer('AMR asymmetric, control', {}, <<SDP); answer('AMR asymmetric, control', {}, <<SDP);
@ -1731,12 +1731,12 @@ m=audio PORT RTP/AVP 98
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
b=RR:1087 b=RR:1087
b=RS:362 b=RS:362
a=maxptime:40
a=rtpmap:98 telephone-event/8000 a=rtpmap:98 telephone-event/8000
a=fmtp:98 0-15 a=fmtp:98 0-15
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:40
SDP SDP
@ -1779,7 +1779,6 @@ m=audio PORT RTP/AVP 96 97 8 98
b=AS:80 b=AS:80
b=RS:362 b=RS:362
b=RR:1087 b=RR:1087
a=maxptime:40
a=rtpmap:96 AMR/8000 a=rtpmap:96 AMR/8000
a=fmtp:96 mode-set=0,2,4,7;mode-change-period=2;mode-change-neighbor=1;mode-change-capability=2;max-red=0 a=fmtp:96 mode-set=0,2,4,7;mode-change-period=2;mode-change-neighbor=1;mode-change-capability=2;max-red=0
a=rtpmap:97 AMR/8000 a=rtpmap:97 AMR/8000
@ -1791,6 +1790,7 @@ a=msi:mavodi-0-15b-6c6-2-ffffffff-d3c00000-6005c95738e64-171f-ffffffffffffffff-.
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:40
SDP SDP
answer('AMR asymmetric', {flags => ['allow asymmetric codecs']}, <<SDP); answer('AMR asymmetric', {flags => ['allow asymmetric codecs']}, <<SDP);
@ -1820,7 +1820,6 @@ m=audio PORT RTP/AVP 118 98
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
b=RR:1087 b=RR:1087
b=RS:362 b=RS:362
a=maxptime:40
a=rtpmap:118 AMR/8000 a=rtpmap:118 AMR/8000
a=fmtp:118 mode-set=0,2,4,7;mode-change-period=2;mode-change-capability=2;mode-change-neighbor=1;max-red=0 a=fmtp:118 mode-set=0,2,4,7;mode-change-period=2;mode-change-capability=2;mode-change-neighbor=1;max-red=0
a=rtpmap:98 telephone-event/8000 a=rtpmap:98 telephone-event/8000
@ -1828,6 +1827,7 @@ a=fmtp:98 0-15
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:40
SDP SDP
subscribe_request('AMR asymmetric', {flags => [qw/SIPREC all/]}, <<SDP); subscribe_request('AMR asymmetric', {flags => [qw/SIPREC all/]}, <<SDP);
@ -1847,6 +1847,7 @@ a=fmtp:98 0-15
a=sendonly a=sendonly
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:40
m=audio PORT RTP/AVP 118 98 m=audio PORT RTP/AVP 118 98
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
b=AS:80 b=AS:80
@ -1861,6 +1862,7 @@ a=msi:mavodi-0-15b-6c6-2-ffffffff-d3c00000-6005c95738e64-171f-ffffffffffffffff-@
a=sendonly a=sendonly
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:40
SDP SDP
@ -4740,7 +4742,6 @@ s=tester
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
t=0 0 t=0 0
m=audio PORT RTP/AVP 104 110 102 108 8 0 105 100 m=audio PORT RTP/AVP 104 110 102 108 8 0 105 100
a=maxptime:240
a=rtpmap:104 speex/16000 a=rtpmap:104 speex/16000
a=fmtp:104 max-red=0; mode-change-capability=2 a=fmtp:104 max-red=0; mode-change-capability=2
a=rtpmap:110 speex/16000 a=rtpmap:110 speex/16000
@ -4758,6 +4759,7 @@ a=fmtp:100 0-15
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:240
SDP SDP
answer('DTMF PT reduction', answer('DTMF PT reduction',
@ -5409,7 +5411,6 @@ s=TELES-SBC
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
t=0 0 t=0 0
m=audio PORT RTP/AVP 8 9 0 102 101 m=audio PORT RTP/AVP 8 9 0 102 101
a=maxptime:240
a=rtpmap:8 PCMA/8000 a=rtpmap:8 PCMA/8000
a=rtpmap:9 G722/8000 a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000 a=rtpmap:0 PCMU/8000
@ -5420,6 +5421,7 @@ a=fmtp:101 0-15
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:240
SDP SDP
answer('re-invite with unsupported primary codec', { }, <<SDP); answer('re-invite with unsupported primary codec', { }, <<SDP);
@ -5757,7 +5759,6 @@ m=audio PORT RTP/AVP 109 104 110 111 102 108 8 9 18 100
b=AS:80 b=AS:80
b=RS:625 b=RS:625
b=RR:1875 b=RR:1875
a=maxptime:240
a=rtpmap:109 EVS/16000 a=rtpmap:109 EVS/16000
a=fmtp:109 br=5.9-24.4; bw=nb-swb; max-red=0; cmr=1; ch-aw-recv=-1 a=fmtp:109 br=5.9-24.4; bw=nb-swb; max-red=0; cmr=1; ch-aw-recv=-1
a=rtpmap:104 AMR-WB/16000 a=rtpmap:104 AMR-WB/16000
@ -5779,6 +5780,7 @@ a=fmtp:100 0-15
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:240
SDP SDP
answer('unsupp codecs and dup encodings', { }, <<SDP); answer('unsupp codecs and dup encodings', { }, <<SDP);
@ -5804,7 +5806,6 @@ s=modCOM v2 Media Gateway
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
t=0 0 t=0 0
m=audio PORT RTP/AVP 9 8 18 100 m=audio PORT RTP/AVP 9 8 18 100
a=maxptime:150
a=rtpmap:9 G722/8000 a=rtpmap:9 G722/8000
a=rtpmap:8 PCMA/8000 a=rtpmap:8 PCMA/8000
a=rtpmap:18 G729/8000 a=rtpmap:18 G729/8000
@ -5814,6 +5815,7 @@ a=fmtp:100 0-16
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:150
SDP SDP
@ -11566,13 +11568,13 @@ s=SIP Media Capabilities
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
t=0 0 t=0 0
m=audio PORT RTP/AVP 0 9 101 m=audio PORT RTP/AVP 0 9 101
a=maxptime:20
a=rtpmap:0 PCMU/8000 a=rtpmap:0 PCMU/8000
a=rtpmap:9 G722/8000 a=rtpmap:9 G722/8000
a=rtpmap:101 telephone-event/8000 a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15 a=fmtp:101 0-15
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=maxptime:20
SDP SDP
answer('missing codec in re-invite', { answer('missing codec in re-invite', {
@ -11630,7 +11632,6 @@ s=SIP Media Capabilities
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
t=0 0 t=0 0
m=audio PORT RTP/AVP 0 9 101 m=audio PORT RTP/AVP 0 9 101
a=maxptime:20
a=rtpmap:0 PCMU/8000 a=rtpmap:0 PCMU/8000
a=rtpmap:9 G722/8000 a=rtpmap:9 G722/8000
a=fmtp:9 bitrate=64 a=fmtp:9 bitrate=64
@ -11638,6 +11639,7 @@ a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15 a=fmtp:101 0-15
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=maxptime:20
SDP SDP
@ -13460,7 +13462,6 @@ s=Asterisk
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
t=0 0 t=0 0
m=audio PORT RTP/AVP 8 107 9 101 m=audio PORT RTP/AVP 8 107 9 101
a=maxptime:20
a=rtpmap:8 PCMA/8000 a=rtpmap:8 PCMA/8000
a=rtpmap:107 opus/48000/2 a=rtpmap:107 opus/48000/2
a=rtpmap:9 G722/8000 a=rtpmap:9 G722/8000
@ -13469,6 +13470,7 @@ a=fmtp:101 0-16
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:20
SDP SDP
answer('gh 953 w/o flag', { ICE => 'remove', }, <<SDP); answer('gh 953 w/o flag', { ICE => 'remove', }, <<SDP);
@ -13534,7 +13536,6 @@ s=Asterisk
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
t=0 0 t=0 0
m=audio PORT RTP/AVP 8 107 9 101 m=audio PORT RTP/AVP 8 107 9 101
a=maxptime:20
a=rtpmap:8 PCMA/8000 a=rtpmap:8 PCMA/8000
a=rtpmap:107 opus/48000/2 a=rtpmap:107 opus/48000/2
a=rtpmap:9 G722/8000 a=rtpmap:9 G722/8000
@ -13543,6 +13544,7 @@ a=fmtp:101 0-16
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:20
SDP SDP
answer('gh 953 w/ flag', { ICE => 'remove', flags => ['symmetric codecs'] }, <<SDP); answer('gh 953 w/ flag', { ICE => 'remove', flags => ['symmetric codecs'] }, <<SDP);
@ -13608,7 +13610,6 @@ s=Asterisk
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
t=0 0 t=0 0
m=audio PORT RTP/AVP 8 107 9 101 m=audio PORT RTP/AVP 8 107 9 101
a=maxptime:20
a=rtpmap:8 PCMA/8000 a=rtpmap:8 PCMA/8000
a=rtpmap:107 opus/48000/2 a=rtpmap:107 opus/48000/2
a=rtpmap:9 G722/8000 a=rtpmap:9 G722/8000
@ -13617,6 +13618,7 @@ a=fmtp:101 0-16
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:20
SDP SDP
answer('gh 953 722 accepted w/o flag', { ICE => 'remove', }, <<SDP); answer('gh 953 722 accepted w/o flag', { ICE => 'remove', }, <<SDP);
@ -13683,7 +13685,6 @@ s=Asterisk
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
t=0 0 t=0 0
m=audio PORT RTP/AVP 8 107 9 101 m=audio PORT RTP/AVP 8 107 9 101
a=maxptime:20
a=rtpmap:8 PCMA/8000 a=rtpmap:8 PCMA/8000
a=rtpmap:107 opus/48000/2 a=rtpmap:107 opus/48000/2
a=rtpmap:9 G722/8000 a=rtpmap:9 G722/8000
@ -13692,6 +13693,7 @@ a=fmtp:101 0-16
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:20
SDP SDP
answer('gh 953 722 accepted w/ flag', { ICE => 'remove', flags => ['symmetric codecs'] }, <<SDP); answer('gh 953 722 accepted w/ flag', { ICE => 'remove', flags => ['symmetric codecs'] }, <<SDP);
@ -13759,7 +13761,6 @@ s=Asterisk
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
t=0 0 t=0 0
m=audio PORT RTP/AVP 8 107 9 101 m=audio PORT RTP/AVP 8 107 9 101
a=maxptime:20
a=rtpmap:8 PCMA/8000 a=rtpmap:8 PCMA/8000
a=rtpmap:107 opus/48000/2 a=rtpmap:107 opus/48000/2
a=rtpmap:9 G722/8000 a=rtpmap:9 G722/8000
@ -13768,6 +13769,7 @@ a=fmtp:101 0-16
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:20
SDP SDP
answer('gh 953 only 722 accepted w/o flag', { ICE => 'remove', }, <<SDP); answer('gh 953 only 722 accepted w/o flag', { ICE => 'remove', }, <<SDP);
@ -13826,7 +13828,6 @@ s=Asterisk
c=IN IP4 203.0.113.1 c=IN IP4 203.0.113.1
t=0 0 t=0 0
m=audio PORT RTP/AVP 8 107 9 101 m=audio PORT RTP/AVP 8 107 9 101
a=maxptime:20
a=rtpmap:8 PCMA/8000 a=rtpmap:8 PCMA/8000
a=rtpmap:107 opus/48000/2 a=rtpmap:107 opus/48000/2
a=rtpmap:9 G722/8000 a=rtpmap:9 G722/8000
@ -13835,6 +13836,7 @@ a=fmtp:101 0-16
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:20
SDP SDP
answer('gh 953 only 722 accepted w/ flag', { ICE => 'remove', flags => ['symmetric codecs'] }, <<SDP); answer('gh 953 only 722 accepted w/ flag', { ICE => 'remove', flags => ['symmetric codecs'] }, <<SDP);
@ -20804,7 +20806,6 @@ c=IN IP4 203.0.113.1
t=0 0 t=0 0
a=rtpengine:LOOPER a=rtpengine:LOOPER
m=audio PORT RTP/AVP 0 8 18 101 m=audio PORT RTP/AVP 0 8 18 101
a=maxptime:20
a=rtpmap:0 PCMU/8000 a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000 a=rtpmap:8 PCMA/8000
a=rtpmap:18 G729/8000 a=rtpmap:18 G729/8000
@ -20813,6 +20814,7 @@ a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15 a=fmtp:101 0-15
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=maxptime:20
SDP SDP
(undef, $port_b) = answer('gh 766 orig', (undef, $port_b) = answer('gh 766 orig',
@ -23200,7 +23202,6 @@ m=audio PORT RTP/AVP 99 97 9 8 0 105 100
b=AS:41 b=AS:41
b=RS:512 b=RS:512
b=RR:1537 b=RR:1537
a=maxptime:240
a=rtpmap:99 AMR-WB/16000 a=rtpmap:99 AMR-WB/16000
a=fmtp:99 mode-set=0,1,2,5,7,8; max-red=0; mode-change-capability=2 a=fmtp:99 mode-set=0,1,2,5,7,8; max-red=0; mode-change-capability=2
a=rtpmap:97 AMR/8000 a=rtpmap:97 AMR/8000
@ -23219,6 +23220,7 @@ a=curr:qos remote none
a=sendrecv a=sendrecv
a=rtcp:PORT a=rtcp:PORT
a=ptime:20 a=ptime:20
a=maxptime:240
m=application PORT UDP/DTLS/SCTP webrtc-datachannel m=application PORT UDP/DTLS/SCTP webrtc-datachannel
b=AS:500 b=AS:500
a=setup:passive a=setup:passive


Loading…
Cancel
Save