Mobile Multimedia Streaming Improvements with Freeze-DCCP¶
Olivier Mehani, Roksana Boreli, Guillaume Jourjon, Thierry Ernst
To demonstrate the performance improvements of our proposed Freeze-DCCP/TFRC, we use the Orbit testbed to emulate vertical handovers and observe the impact on a CBR video stream. Rather than the intrinsic bandwidth made available to the video stream, its quality has been assessed. To this end, the peak-signal-to-noise ratio metric has been used. See also the Nicta publication page.
IMPORTANT NOTE This case study was last tested with OMF 5.3.1. It may require some updates to be reproduced with the latest OMF version.
Scenario¶
A user, initially (t1) at home, receiving a video stream on their mobile terminal connected to their home Wi-Fi network. They decide to get a coffee from the corner shop. On the way there, the terminal loses its connectivity to the home network, and hands off to the 3G network (t2). The coffee shop has a public wireless network, to which the terminal connects when it get in range (t3). With their coffee in hand, the user then heads back home, losing connectivity to the public Wi-Fi network and performing an handover to 3G at t4 , and finally reconnecting to their home network at t5.
Implementation¶
The experiment is run on two nodes of the Norbit testbed.
On the sender side, traffic is sent from a DCCP-enabled Iperf client. The packet size and frequency is adjusted to match those of the packetised video stream. This version of Iperf has also been instrumented using OML to report standard metrics such as troughput and packet loss.
On the receiver side, a custom application receives the DCCP traffic. Based on the sequence identifier in the Iperf headers, it which frames have been received properly, and which ones have been lost. It then reports the associated PSNR, which was precomputed for efficiency. For a lost frame, the PSNR is that of the initial raw frame to a image containing only noise.
Handovers disconnections are emulated using iptables, while network parameters are defined using tc and netem. The DCCP receiver is in charge of freezing and unfreezing the connection about one RTT before the handoff so that the traffic can be suspended on time.
Output¶
A PSNR graph is plotted and updated in real-time by the experiment controller as the scenario is executed. It shows a comparison of the normal protocol and the proposed improvement.
The experiment controller's dashboard, however, gives access to more information about the experiment and its current state.
Code¶
This demo is based on several components which have been specifically written or adapted:- Freeze-DCCP support was added to Linux 2.6.34-r5
- A DCCP-enabled version of IPerf was patched to support reporting via liboml (iperf-2.0.2-dccp-oml.tar.bz2)
- A specific receiver which computes the PSNR of the received video as an index of the quality of experience (freezedccp_qoe_recv.tar.bz2)
- The OEDL script which drives all those elements through OMF, it is detailled below (freezedccp-qoe.rb)
Experiment script¶
1 #!/usr/bin/omf exec
2 # $Id: freezedccp-qoe.rb 1775 2010-10-13 05:48:31Z omehani $
3 #
4 # Copyright (c) 2010, Nicta, Olivier Mehani <olivier.mehani@nicta.com.au>
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 # 1. Redistributions of source code must retain the above copyright notice, this
11 # list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright notice,
13 # this list of conditions and the following disclaimer in the documentation
14 # and/or other materials provided with the distribution.
15 # 3. Neither the name of Nicta nor the names of its contributors
16 # may be used to endorse or promote products derived from this software
17 # without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 # POSSIBILITY OF SUCH DAMAGE.
30 #
31 defProperty('src', 'omf.nicta.node10', "ID of sender node")
32 defProperty('dst', 'omf.nicta.node11', "ID of receiver node")
33 defProperty('freezesrc', 'omf.nicta.node12', "ID of Freeze sender node")
34 defProperty('freezedst', 'omf.nicta.node13', "ID of Freeze receiver node")
35 defProperty('interface', 'exp0', "The network interface to use")
36 defProperty('netid', '42', "Second byte of the class B network address (N in 10.N.x.y)")
37
38 # Traffic emulation
39 defProperty('payloadsize', "1324" , "Data payload size, in bytes")
40 defProperty('datarate', "1M", "Application datarate, in bit/second")
41 defProperty('interval', "0.5", "Interval in second for consecutive Iperf reporting") # XXX: why can't this be a float?
42 defProperty('intervalrcv', "1", "Interval in second for consecutive Iperf receiver reporting")
43
44 # Networks emulation
45 defProperty('minbuffersize', 50, "Minimal buffer size for TC shaping, in kilobytes")
46 defProperty('coredelay', 0.025, "Emulated delay of the core network in seconds")
47 defProperty('homebw', 1, "Emulated bandwidth of the home network in Mbit/second")
48 defProperty('homedelay', 0.001, "Emulated delay of the home network in seconds")
49 defProperty('wanbw', 0.5, "Emulated bandwidth of the WAN in Mbit/second")
50 defProperty('wandelay', 0.1, "Emulated delay of the WAN in seconds")
51 defProperty('cafebw', 0.7, "Emulated bandwidth of the cafe network in Mbit/second")
52 defProperty('cafedelay', 0.01, "Emulated delay of the cafe network in seconds")
53 defProperty('wlossrate', 0, "Emulated wireless packet loss rate in percent")
54
55 # Timing
56 defProperty('t2', 30, "Time of first switch to WAN in seconds")
57 defProperty('t3', 60, "Time of switch to cafe network in seconds")
58 defProperty('t4', 90, "Time of second switch to WAN in seconds")
59 defProperty('t5', 120, "Time of switch back to home network in seconds")
60 defProperty('duration', 150, "Duration of the experiment in seconds")
61
62 MEGABITS = 1048576
63
64 srcaddr="192.168.#{property.netid}.#{property.src.value.match(/[0-9]+/)[0]}"
65 dstaddr="192.168.#{property.netid}.#{property.dst.value.match(/[0-9]+/)[0]}"
66 freezesrcaddr="192.168.#{property.netid}.#{property.freezesrc.value.match(/[0-9]+/)[0]}"
67 freezedstaddr="192.168.#{property.netid}.#{property.freezedst.value.match(/[0-9]+/)[0]}"
68
69 scenario = [
70 { 'bw' => property.homebw, 'delay' => property.homedelay, 'duration' => property.t2 },
71 { 'bw' => property.wanbw, 'delay' => property.wandelay, 'duration' => property.t3 - property.t2 },
72 { 'bw' => property.cafebw, 'delay' => property.cafedelay, 'duration' => property.t4 - property.t3 },
73 { 'bw' => property.wanbw, 'delay' => property.wandelay, 'duration' => property.t5 - property.t4 },
74 { 'bw' => property.homebw, 'delay' => property.homedelay, 'duration' => property.duration - property.t5 },
75 ]
76
77 # Needs [655] as per #415
78 addTab(:defaults)
79 addTab(:graph2) do |tab|
80 opts = { :postfix => %{QoE of DCCP and Freeze-DCCP streams in mobility scenarios}, :updateEvery => 1 }
81 tab.addGraph("PSNR", opts) do |g|
82 psnr_data = {}
83 mp = ms('video')
84 mp.project(:oml_sender_id, :oml_ts_server, :psnr_avg).each do |sample|
85 id, time, psnr = sample.tuple
86 (psnr_data[id] ||= []) << [time, psnr]
87 end
88 psnr_data.each do |d,v|
89 g.addLine(v, :label => msSenderName[d])
90 end
91
92 freeze_data = {}
93 mp = ms('dccp')
94 mp.project(:oml_sender_id, :oml_ts_server, :freezestate_last).each do |sample|
95 id, time, freeze = sample.tuple
96 (freeze_data[id] ||= []) << [time, freeze]
97 end
98 freeze_data.each do |d,v|
99 g.addLine(v, :label => "frozen #{msSenderName[d]}")
100 end
101 end
102 tab.addGraph("Senders", opts) do |g|
103 x_data = {}
104 rtt_data = {}
105 p_data = {}
106 mp = ms('TFRC_Info')
107 mp.project(:oml_sender_id, :oml_ts_server, :x_avg, :rtt_avg, :p_avg).each do |sample|
108 id, time, x, rtt, p = sample.tuple
109 (x_data[id] ||= []) << [time, 8*x/2000] # XXX
110 (rtt_data[id] ||= []) << [time, rtt]
111 (p_data[id] ||= []) << [time, p==0?0:Math.log(p*10**3)]
112 end
113 x_data.each do |d,v|
114 g.addLine(v, :label => "X #{msSenderName[d]} [kbps]")
115 end
116 rtt_data.each do |d,v|
117 g.addLine(v, :label => "RTT #{msSenderName[d]} [ms]")
118 end
119 p_data.each do |d,v|
120 g.addLine(v, :label => "p #{msSenderName[d]} (*10^3, log)")
121 end
122 end
123 tab.addGraph("Receivers", opts) do |g|
124 x_recv_data = {}
125 rtt_data = {}
126 p_data = {}
127 freeze_data = {}
128 mp = ms('dccp')
129 mp.project(:oml_sender_id, :oml_ts_server, :x_recv_avg, :rtt_avg, :p_avg, :freezestate_last).each do |sample|
130 id, time, x_recv, rtt, p, freeze = sample.tuple
131 (x_recv_data[id] ||= []) << [time, 8*x_recv/1000]
132 (rtt_data[id] ||= []) << [time, rtt]
133 (p_data[id] ||= []) << [time, p==0?0:Math.log(p*10**3)]
134 (freeze_data[id] ||= []) << [time, freeze]
135 end
136 x_recv_data.each do |d,v|
137 g.addLine(v, :label => "Xrecv #{msSenderName[d]} [kbps]")
138 end
139 rtt_data.each do |d,v|
140 g.addLine(v, :label => "RTT #{msSenderName[d]} [ms]")
141 end
142 p_data.each do |d,v|
143 g.addLine(v, :label => "p #{msSenderName[d]} (*10^3, log)")
144 end
145 freeze_data.each do |d,v|
146 g.addLine(v, :label => "frozen #{msSenderName[d]}")
147 end
148 end
149 end
150
151 defApplication('iperf20','iperf20'){|a|
152 a.name = "iperf2"
153 a.version(0, 0, 1)
154 a.path = "/root/iperf"
155 a.shortDescription = "Iperf traffic generator"
156 a.description = <<TEXT
157 Iperf is a traffic generator for TCP, UDP and DCCP traffic. It contains
158 generators producing various forms of packet streams and port for sending these
159 packets via various transports, such as TCP, UDP and DCCP.
160 TEXT
161
162 # Define the properties that can be configured for this application
163 #
164 # syntax: defProperty(name, description, mnemonic = nil, options = nil)
165 #
166 a.defProperty('udp', 'Use UDP, otherwise TCP by default', 'u', {:order => 2, :dynamic => false, :type => :boolean})
167 a.defProperty('dccp', 'Use DCCP, otherwise TCP by default', 'd', {:order => 2, :dynamic => false, :type => :boolean})
168 a.defProperty('reportstyle', 'Report Style', 'y', {:dynamic => false, :type => :string})
169 a.defProperty('client', 'Run as client', 'c', {:order => 1, :dynamic => false, :type => :string})
170 a.defProperty('server', 'Client/Server', 's', {:type => :boolean, :dynamic => false})
171 a.defProperty('port', 'Sender port number', 'p', {:type => :integer, :dynamic => false})
172 a.defProperty('window', 'TCP Send Window Size', nil, {:type => :integer, :dynamic => false})
173 a.defProperty('time', "Duration of traffic generation(secs)", nil, {:type => :integer, :dynamic => false})
174 a.defProperty('len', "Buffer length (A.K.A. datagram payload size)", 'l', {:dynamic => false, :type => :string})
175 a.defProperty('bandwidth', "Offered load for UDP", 'b', {:dynamic => false, :type => :string})
176 a.defProperty('parallel', "Number of parallel flows", nil, {:type => :integer, :dynamic => false})
177 a.defProperty('interval', "Interval between reports (sec)", nil, {:type => :string, :dynamic => false})
178
179 # Define the Measurement Points and associated metrics that are available for this application
180 #
181 a.defMeasurement("Peer_Info"){ |m|
182 m.defMetric('ID', :long)
183 m.defMetric('local_address', :string)
184 m.defMetric('local_port', :long)
185 m.defMetric('foreign_address', :string)
186 m.defMetric('foreign_port', :long)
187 }
188 a.defMeasurement("TCP_Info"){ |m|
189 m.defMetric('ID', :long)
190 m.defMetric('Begin_interval', :float)
191 m.defMetric('End_interval', :float)
192 m.defMetric('Transfer', :float)
193 m.defMetric('Bandwidth', :float)
194 }
195 a.defMeasurement("UDP_Periodic_Info"){ |m|
196 m.defMetric('ID', :long)
197 m.defMetric('Begin_interval', :float)
198 m.defMetric('End_interval', :float)
199 m.defMetric('Transfer', :float)
200 m.defMetric('Bandwidth', :float)
201 }
202 a.defMeasurement("UDP_Rich_Info"){ |m|
203 m.defMetric('ID', :long)
204 m.defMetric('Begin_interval', :float)
205 m.defMetric('End_interval', :float)
206 m.defMetric('Transfer', :float)
207 m.defMetric('Bandwidth', :float)
208 m.defMetric('Jitter', :float)
209 m.defMetric('Packet_Lost', :long)
210 m.defMetric('Total_Packet', :long)
211 m.defMetric('PLR', :float)
212 }
213 a.defMeasurement("DCCP_Info"){ |m|
214 m.defMetric('ID', :long)
215 m.defMetric('Begin_interval', :float)
216 m.defMetric('End_interval', :float)
217 m.defMetric('Transfer', :float)
218 m.defMetric('Bandwidth', :float)
219 m.defMetric('Jitter', :float)
220 m.defMetric('Packet_Lost', :long)
221 m.defMetric('Total_Packet', :long)
222 m.defMetric('PLR', :float)
223 }
224 a.defMeasurement("TFRC_Info"){ |m|
225 m.defMetric('ID', :long)
226 m.defMetric('x', :float, "Sender rate in Bps")
227 m.defMetric('x_recv', :float, "(Remote) Receive rate in Bps")
228 m.defMetric('x_calc', :float, "Calculated rate in Bps")
229 m.defMetric('rtt', :float, "RTT in ms")
230 m.defMetric('p', :float)
231 m.defMetric('rto', :float)
232 m.defMetric('ipi', :float)
233 }
234 #a.omlPrefix = 'iperf2'
235 }
236
237 defPrototype("SenderNode") { |proto|
238 proto.name = "SenderNode"
239 proto.description = "An Iperf DCCP sender node"
240
241 proto.defProperty('destination', 'Name or IP address of the destination')
242
243 proto.defProperty('bandwidth', 'Application sending rate, in bps', property.datarate)
244 proto.defProperty('len', 'Send buffer size', property.payloadsize)
245 proto.defProperty('time', 'Duration of the transmission', property.duration*4)
246 proto.defProperty('interval', 'Reporting interval', property.interval)
247
248 proto.addApplication("iperf20", "iperf20") { |app|
249 app.setProperty('dccp', true)
250 app.setProperty('reportstyle', 'o')
251
252 app.bindProperty('client', 'destination')
253 app.bindProperty('bandwidth', 'bandwidth')
254 app.bindProperty('len', 'len')
255 app.bindProperty('time', 'time')
256 app.bindProperty('interval', 'interval')
257
258 app.measure('TFRC_Info', :interval => 1) { |mp|
259 mp.filter('x', 'avg') # sender's rate
260 mp.filter('rtt', 'avg') # RTT
261 mp.filter('p', 'avg') # loss event rate
262 }
263 }
264 }
265
266 # OMF is not aware of those filters (#407 & #408)
267 # The following should be added to
268 # /usr/share/omf-expctl-5.3/omf-expctl/oml/filter.rb
269 #
270 # FilterSpec.register(:sum) do |s|
271 # s.addParam 'sum', 'FLOAT'
272 # end
273 # FilterSpec.register(:delta) do |s|
274 # s.addParam 'delta', 'FLOAT'
275 # end
276
277 defPrototype("ReceiverNode") { |proto|
278 proto.name = "ReceiverNode"
279 proto.description = "A DCCP receiver node which measures QoE"
280
281 proto.addApplication("freezedccp_qoe_recv_app", "freezedccp_qoe_recv_app") { |app|
282 app.measure('dccp', :interval => 1) { |mp|
283 mp.filter('x_recv', 'avg') # trsprate
284 mp.filter('rtt', 'avg') # rtt
285 mp.filter('p', 'avg') # loss event rate
286 mp.filter('freezestate', 'last') # frozen state
287 }
288 app.measure('stream', :interval => 1) { |mp|
289 mp.filter('id', 'delta') # npkts-tot
290 mp.filter('npkts', 'delta') # npkts
291 mp.filter('nloss', 'delta') # nloss
292 mp.filter('size', 'sum') # apprate
293 }
294 app.measure('video', :interval => 1) { |mp|
295 mp.filter('id', 'delta') # nframes
296 mp.filter('psnr', 'avg') # psnr
297 }
298 }
299 }
300
301 # We define the Freeze nodes first so the application instance names don't
302 # get a _2 appended (see #413)
303 defGroup('FreezeSender', property.freezesrc) {|node|
304 node.prototype("SenderNode", {
305 'destination' => freezedstaddr,
306 'bandwidth' => "#{property.datarate}",
307 'len' => "#{property.payloadsize}",
308 'time' => property.duration*4,
309 'interval' => property.interval,
310 })
311 node.net.e0.ip = freezesrcaddr
312 }
313 defGroup('Sender', property.src) {|node|
314 node.prototype("SenderNode", {
315 'destination' => dstaddr,
316 'bandwidth' => "#{property.datarate}",
317 'len' => "#{property.payloadsize}",
318 'time' => property.duration*4,
319 'interval' => property.interval,
320 })
321 node.net.e0.ip = srcaddr
322 }
323 defGroup('FreezeReceiver', property.freezedst) {|node|
324 node.prototype("ReceiverNode")
325 node.net.e0.ip = freezedstaddr
326 }
327 defGroup('Receiver', property.dst) {|node|
328 node.prototype("ReceiverNode")
329 node.net.e0.ip = dstaddr
330 }
331
332 defGroup('Actors', ["Sender","Receiver", "FreezeSender", "FreezeReceiver"])
333 defGroup('Senders', ["Sender", "FreezeSender"])
334 defGroup('Receivers', ["Receiver", "FreezeReceiver"])
335
336 def cleanup ()
337 group("Actors").exec("killall -9 iperf freezedccp_qoe_recv; tc qdisc del dev #{property.interface} root;
338 tc qdisc del dev #{property.interface} ingress; iptables -F")
339 end
340
341 def init(bw, delay)
342 group("Actors").exec("modprobe sch_htb; modprobe sch_netem")
343 group("Actors").exec("modprobe xt_dccp")
344 group("Actors").exec("sysctl -w net.dccp.default.seq_window=10000 net.dccp.default.rx_ccid=3 net.dccp.default.tx_ccid=3")
345
346 cleanup
347
348 group("Actors").exec("tc qdisc add dev #{property.interface} root handle 1 netem delay #{delay}s loss #{property.wlossrate}%; \
349 tc qdisc add dev #{property.interface} handle ffff: ingress; \
350 tc filter add dev #{property.interface} parent ffff: protocol ip prio 50 u32 match \
351 ip src 0.0.0.0/0 police rate #{bw}Mbit burst #{[property.minbuffersize, bw * 2].max}k drop flowid 1")
352
353 wait 10
354 end
355
356 # Message passing function has not been completely adapted in OMF-5.3
357 # The sendMessage method in
358 # /usr/share/omf-expctl-5.3/omf-expctl/node/rootNodeSetPath.rb (l. 216)
359 # should be changed to
360 #
361 # def sendMessage(name, *args)
362 # @nodeSet.send(ECCommunicator.instance.create_message(:cmdtype => :STDIN,
363 # :appID => name,
364 # :value => "#{args.join(' ')}"))
365 # end
366
367 def reconnect(bw, delay)
368 discoduration=2.5 + 2 * delay
369 info "Connecting to network (#{bw}Mbps/#{delay}s), handoff duration #{discoduration}"
370 group("Actors").exec("tc qdisc change dev #{property.interface} root handle 1 netem delay #{delay}s loss #{property.wlossrate}%; \
371 tc filter change dev #{property.interface} parent ffff: handle 800::800 protocol ip prio 50 u32 match \
372 ip src 0.0.0.0/0 police rate #{bw}Mbit burst #{[property.minbuffersize, bw * 2].max}k drop flowid 1; \
373 sleep #{discoduration}s; \
374 iptables -D OUTPUT -o #{property.interface} -j DROP; \
375 iptables -D INPUT -i #{property.interface} -j DROP")
376 group("FreezeReceiver").sendMessage("freezedccp_qoe_recv_app", "unfreeze")
377 end
378
379 def disconnect(rtt)
380 group("FreezeReceiver").sendMessage("freezedccp_qoe_recv_app", "freeze")
381 info "Handing off"
382 group("Actors").exec("sleep #{rtt}s; \
383 iptables -A INPUT -i #{property.interface} -j DROP; \
384 iptables -A OUTPUT -o #{property.interface} -j DROP")
385 end
386
387 onEvent(:ALL_UP_AND_INSTALLED) { |node|
388 init(scenario[0]['bw'], scenario[0]['delay'] + property.coredelay)
389 first = true
390
391 info "Starting receivers..."
392 group("Receivers").startApplications
393 wait 10
394 info "Starting senders..."
395 group("Senders").startApplications
396
397 scenario.each { |step|
398 delay = step['delay'] + property.coredelay
399 rtt = 2 * delay
400
401 if first != true
402 reconnect(step['bw'], delay)
403 end
404
405 # Freeze one RTT before disconnection,
406 # the remaining RTT is accounted for in disconnect()
407 wait step['duration'] - rtt
408
409 disconnect(rtt)
410 first = false
411 }
412
413 cleanup
414
415 Experiment.done
416 }
- Lines 31--60: define the configurable parameters and default values such as nodes, duration or link capacities;
- Lines 64--67: derive IP addresses from the selected nodes' ID;
- Lines 69--75: define the scenario “phases” as a Ruby array for later iteration;
- Lines 77--149: add a tab to the EC's dashboard with graphs showing various metrics measured on the testbed nodes;
- Lines 151--235: (re-(re-))define the Iperf application;
- Lines 237-299: define sender and receiver nodes prototypes;
- Lines 301--332: instantiate those prototype for the selected nodes;
- Lines 332-334: define handy groups of nodes for easier manipulation;
- Lines 336--385: helper functions to be used during the experiment;
- Lines 387--416: main experimental loop, iterating over the scenario “phases” and emulating both network handoffs and network conditiens for each.